This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
Note
Access to this page requires authorization. You can trysigning in orchanging directories.
Access to this page requires authorization. You can trychanging directories.
David Chappell
Chappell & Associates
February 2005
Summary: Provides an architectural overview of "Indigo," Microsoft's unified programming model for building service-oriented applications. The paper covers Indigo's relationship to existing distributed application technologies in the .NET Framework, the basics of creating and consuming Indigo services, and an overview of Indigo's capabilities, including security, reliable messaging, and transaction support. (24 printed pages)
Note This article is based on a prerelease version of Indigo's first Community Technology Preview (CTP). Be aware that features and implementation details are subject to change as Indigo progresses through the product cycle.
What Is Indigo?
What Indigo Provides
Creating an Indigo Service
Creating an Indigo Client
Other Aspects of Indigo
Coexistence and Migration
Conclusions
About the Author
Choosing the best abstractions for building software is an ongoing process. Objects are the dominant approach today for building an application's business logic, but modeling application-to-application communication using objects hasn't been as successful. A better approach is to explicitly model interactions between discrete chunks of software asservices. Plenty of support already exists for building object-oriented applications, but thinking of services as a fundamental software building block is a more recent idea. Because of this, technologies explicitly designed to create service-oriented applications haven't been widely available.
Microsoft's framework for building service-oriented applications, code-namedIndigo, changes this. Indigo allows developers who today create object-oriented applications using the .NET Framework to also build service-oriented applications in a familiar way. And to let those applications interact effectively with software running on Windows and on other platforms, Indigo implements SOAP and other Web services technologies, allowing developers to create reliable, secure, and transactional services that can interoperate with software running on any system.
.gif&f=jpg&w=240)
The figure above shows a simple view of an Indigo client and service. Indigo provides a foundation, implemented primarily as a set of classes running on the Common Language Runtime (CLR), for creating services that are accessed by clients. A client and service interact via SOAP, Indigo's native protocol, and so even though the figure shows both parties built on Indigo, this certainly isn't required.
Indigo is built on and extends the .NET Framework 2.0, which is scheduled for release in 2005. Indigo itself will ship as part of the Windows release code-namedLonghorn, scheduled for 2006, and it will also be made available on Windows XP and Windows Server 2003. This description is based on a pre-release version of Indigo's first Community Technology Preview. Be aware that some changes are likely (almost certain, in fact) before the final version ships.
Many people at Microsoft have devoted years of their lives to creating Indigo. This level of effort wouldn't have been necessary if the problems it solved were simple or if their solutions were obvious. Accordingly, Indigo is a substantial piece of technology. Three things stand out, however, as Indigo's most important aspects: its unification of several existing Microsoft technologies, its support for cross-vendor interoperability, and its explicit service-orientation. This section looks at each of these.
The initial releases of the .NET Framework included several different technologies for creating distributed applications. The figure below lists each one, along with the primary reason why a developer would typically use that technology. To build basic interoperable Web services, for example, the best choice was ASP.NET Web services, more commonly referred to as ASMX. To connect two .NET Framework-based applications, .NET Remoting was sometimes the right approach. If an application required distributed transactions and other more advanced services, its creator was likely to use Enterprise Services, the .NET Framework's successor to COM+. To exploit the latest Web services specifications, such as WS-Addressing and WS-Security, a developer could build applications that used Web Services Enhancements (WSE), Microsoft's initial implementation of these emerging specifications. And to create queued, message-based applications, a Windows-based developer would use Microsoft Message Queuing (MSMQ).
| ASMX | .NET Remoting | Enterprise Services | WSE | MSMQ | Indigo | |
|---|---|---|---|---|---|---|
| Interoperable Web Services | X | X | ||||
| .NET – .NET Communication | X | X | ||||
| Distributed Transactions, etc. | X | X | ||||
| Support for WS-* Specifications | X | X | ||||
| Queued Messaging | X | X |
All of these options had value, yet the diversity was certainly confusing to developers. Why have so many choices? A better solution would be to have one technology that addresses all of these problems. With the arrival of Indigo, that technology has appeared. Rather than forcing developers to choose one of several possibilities, Indigo lets them create distributed applications that address all of the problems solved by the technologies it subsumes. While Microsoft will still support these earlier technologies, most new applications that would previously have used any of them will instead be built on Indigo.
Making life easier for Windows developers by unifying disparate technologies is a good thing. But with the universal agreement among vendors on Web services, the long-standing problem of application interoperability can also be solved. Because Indigo's fundamental communication mechanism is SOAP, Indigo applications can communicate with other software running in a variety of contexts. As shown in the figure below, an application built on Indigo can interact with all of the following:
.gif&f=jpg&w=240)
Indigo applications can also interoperate with applications built on some of the .NET Framework technologies that preceded Indigo, such as ASMX, as described later.
To allow more than just basic communication, Indigo implements a group of newer Web services technologies collectively referred to as the WS-* specifications. These documents define multi-vendor ways to add reliable messaging, security, transactions, and more to SOAP-based Web services. All of these specs were originally defined by Microsoft, IBM, and other vendors working together. As they become stable, ownership often passes to standards bodies such as the Organization for the Advancement of Structured Information Standards (OASIS). The Web services specs supported in Indigo's first release include WS-Addressing, WS-Policy, WS-MetadataExchange, WS-ReliableMessaging, WS-Security, WS-Trust, WS-SecureConversation, WS-Coordination, WS-AtomicTransaction, and the SOAP Message Transmission Optimization Mechanism (MTOM).
When an Indigo application communicates with an application running on a non-Windows system, the protocol used is standard SOAP (perhaps with some WS-* extensions), represented on the wire in its usual text-based XML encoding. When one Indigo-based application communicates with another Indigo-based app, however, it makes sense to optimize this communication. All of the same features are provided, including reliable messaging, security, and transactions, but the wire encoding used is an optimized binary version of SOAP. Messages still conform to the data structure of a SOAP message, referred to as itsInfoset, but their encoding uses a binary representation of that Infoset rather than the standard angle-brackets-and-text format of XML.
Thinking of an application as providing and consuming services is hardly a new idea. What is new is a clear focus on services as distinct from objects. Toward this end, Indigo's creators kept four tenets in mind during the design of this technology:
Service-orientation is a broad area, encompassing service-oriented applications and the more general concepts of service-oriented architecture (SOA). Indigo will be the foundation for service-oriented applications built on Windows, and so it will be fundamental to the SOA efforts of many organizations.
As the figure below shows, every Indigo service is constructed from three things:
.gif&f=jpg&w=240)
All communication with an Indigo service happens via the service's endpoints. Each endpoint specifies acontract that identifies which methods are accessible via this endpoint, abinding that determines how a client can communicate with this endpoint, and anaddress that indicates where this endpoint can be found.
Understanding Indigo requires grasping all of these concepts. This section describes each one, beginning with service classes.
An Indigo service class is a class like any other, but it has a few additions. These additions allow the class's creator to define one or morecontracts that this class implements. Each Indigo service class implements at least oneservice contract, which defines the operations this service exposes. A service class might also explicitly implement adata contract, which defines the data those operations convey. This section looks at both, beginning with service contracts.
Every Indigo service class implements methods for its clients to use. The creator of a service class determines which of its methods are exposed as client-callable operations by including them in a service contract. Defining service contracts—in fact, working explicitly with services in general—is largely a new idea for the .NET world. Indigo's creators needed to find a way to graft this idea on top of the CLR and the programming languages built on it. Fortunately, the CLR's creators anticipated the need for extensions like this, and so they provided support forattributes. As seen by a developer, attributes are character strings, perhaps with associated properties, that can appear before a class definition, a method definition, and in other places. Wherever an attribute appears, it changes some aspect of the behavior of the thing it's associated with.
The .NET Framework has used attributes for various things since its initial release. For example, to mark a method as a SOAP-callable web service in the Framework's ASMX technology, that method is preceded by theWebMethod attribute. Similarly, Enterprise Services uses theTransaction attribute to indicate that a method requires a transaction. Indigo applies this idea to services, defining a range of attributes to define and control services.
The most fundamental attribute in Indigo isServiceContract. In fact, an Indigo service class is just a class that is either itself marked with theServiceContract attribute or that implements an interface marked with this attribute. Here's a simple C# example that uses the first approach:
using System.ServiceModel;[ServiceContract]class Calculator{ [OperationContract] private int Add(int a, int b) { return a + b; } [OperationContract] public int Subtract(int a, int b) { return a - b; } public int Multiply(int a, int b) { return a * b; }}TheServiceContract attribute and all of the other attributes that Indigo uses are defined in theSystem.ServiceModel namespace, and so this example begins with ausing statement that references this namespace. Each method in a service class that can be invoked by a client must be marked with another attribute namedOperationContract. All of the methods in a service class that are preceded by theOperationContract attribute are automatically exposed by Indigo as SOAP-callable operations. In this example,Add andSubtract are both marked with this attribute, and so both are exposed to clients of this service. Any methods in a service class that aren't marked withOperationContract, such asMultiply in the example above, aren't included in the service contract, and so can't be called by clients of this Indigo service.
Two quite separate abstractions, services and objects, come together in Indigo. It's important to understand that both rely on contracts, either explicitly or implicitly, to define what they expose to the outside world. An object, specified by some class, effectively defines a contract that determines which of its methods can be invoked by another object in the same application. Access to these methods is controlled by language keywords such aspublic andprivate. In the classCalculator shown above, for example, other objects in the same application can callSubtract andMultiply, this class's two public methods. The object ** contract this class exposes contains only these two methods.
Using Indigo's attributes,Calculator also defines a service contract, as just described. This contract also has two methods, but they aren't the same as those in its object contract. Whether a method can be invoked by a client of this Indigo service is controlled by theOperationContract attribute, not thepublic andprivate keywords. Because this attribute appears only onAdd andSubtract, only these two methods can be called by clients. The object contract and service contract are completely distinct from one another, which is why a method such asAdd can beprivate while still carrying theOperationContract attribute.
The example just shown illustrates the simplest way to create an Indigo service class: marking a class directly withServiceContract. When this is done, the class's service contract is implicitly defined to consist of all methods in that class that are marked withOperationContract. It's also possible (and probably better in most cases) to specify service contracts explicitly using a language'sinterface type. With this approach, theCalculator class might look like this:
using System.ServiceModel;[ServiceContract]interface ICalculator{ [OperationContract] int Add(int a, int b); [OperationContract] int Subtract(int a, int b);}class Calculator : ICalculator{ public int Add(int a, int b) // private methods aren't { // allowed in interfaces return a + b; } public int Subtract(int a, int b) { return a - b; } public int Multiply(int a, int b) { return a * b; }}In this example, theServiceContract andOperationContract attributes are assigned to theICalculator interface and the methods it contains rather than to theCalculator class itself. The result is the same, however, and so this version of the service exposes the same service contract as the previous one. Using explicit interfaces like this is slightly more complicated, but it also allows more flexibility. For example, a class can implement more than one interface, which means that it can also implement more than one service contract. By exposing multiple endpoints, each with a different service contract, a class can present different groups of services to different clients.
One final point: marking a service class withServiceContract andOperationContract also allows automatically generating service contract definitions in the Web Services Description Language (WSDL). Accordingly, the externally visible definition of every Indigo service contract can be accessed as a standard WSDL document specifying the operations in that contract. And although it's not described here, it's also possible to create an Indigo service class directly from a WSDL document, an approach that's especially useful for implementing externally defined WSDL interfaces.
An Indigo service class specifies a service contract that defines which of its methods are exposed to service clients. Each of those operations will typically convey some data, which means that a service contract also implies some kind of data contract describing the information that will be exchanged. In some cases, this data contract is defined implicitly as part of the service contract. For example, in theCalculator classes shown above, each method takes two input parameters, both integers, and returns a single integer. These parameters define all of the data exchanged by this service, and so they comprise the service's data contract. For services like this one, where every operation uses only simple types, it makes sense to define the data aspects of its contract implicitly within the service contract. There's no need for anything else.
But services can also have parameters of more complex types, such as structures. In cases like this, an explicit data contract is required. Data contracts define how in-memory types are converted to a form suitable for transmission across the wire, a process known asserialization. In effect, data contracts are a mechanism for controlling how data is serialized.
In an Indigo service class, a data contract is defined using theDataContract attribute. A class, structure, or other type marked withDataContract can have one or more of its members preceded by theDataMember attribute, indicating that this member should be included in a serialized value of this type. Here's a simple example:
[DataContract]struct Customer { [DataMember] public string Name; int public age; [DataMember] private int CreditRating;}When an instance of thisCustomer type is passed as a parameter in a method marked withOperationContract, only the fields marked with theDataMember attribute—Name andCreditRating—will be passed.
Whether a field is labeled aspublic orprivate has no effect on whether that field is serialized. Just as with methods, thepublic andprivate keywords are part of the contract defining how this type can be accessed by other objects in the same application.DataMember, likeOperationContract, defines how the type can be accessed by clients of the service this class implements. Once again, the two are completely distinct.
One final point worth emphasizing about Indigo contracts is that nothing becomes part of either a service contract or a data contract by default. Instead, a developer must explicitly use theServiceContract andDataContract attributes to indicate which types have Indigo-defined contracts, then explicitly specify which parts of those types are exposed to clients of this service using theOperationContract andDataMember attributes. One of the tenets of its designers was that services should have explicit boundaries, and so Indigo is an opt-in technology. Everything a service makes available to its clients is expressly specified in the code.
Contracts and the attributes that define them are a major aspect of Indigo, and this short description covers only the highlights. TheOperationContract attribute can be used to defineone-way operations, for example, where a call to a service has no reply. It's also possible to define interactions where both sides can act as client and service, each invoking operations and exposing operations that the other invokes, by creating what are calledduplex contracts. TheDataContract attribute has several more options as well, and it's even possible to work directly with SOAP messages natively using an attribute calledMessageContract. Contracts are used to express much of what Indigo provides, and so they're one of its most fundamental concepts.
A class implementing an Indigo service is typically compiled into a library. By definition, all libraries need a host application domain and Windows process to run in. Indigo provides two options for hosting libraries that implement services. One is to use a host app domain and process provided by the Windows Activation Service (WAS), while the other allows a service to be hosted in any app domain running in an arbitrary process. This section describes both, beginning with WAS.
The simplest way to host an Indigo service is to rely on WAS. (Note that WAS isn't supported in Indigo's first Community Technology Preview. Instead, Indigo services can be hosted in Internet Information Server on Windows Server 2003 and Windows XP, although only SOAP over HTTP is supported in this configuration.) Using WAS is much like using the hosting mechanism provided by IIS for ASMX. Among other things, both rely on the notion of avirtual directory, which is just a shorter alias for an actual directory path in the Windows file system.
To see how WAS hosting works, suppose either of theCalculator classes shown earlier was compiled into a library called calc.dll, then placed in the virtual directorycalculator on a system running Windows Server 2003. To indicate that the Indigo service implemented in calc.dll should be hosted by WAS, a developer creates a file in the calculator virtual directory with the extension .svc (which stands, of course, for "service"). For our simple example, this file might be called calc.svc, and its entire contents could be:
%@Service language=c# %Once this has been done and an endpoint has been defined as shown in the next section, a request from a client to one of theCalculator service's methods will automatically create an instance of this class to execute the specified operation. That instance will run in an application domain created within the standard process that WAS provides.
Relying on WAS to provide a process for hosting an Indigo service is certainly the simplest choice. Yet applications often need to expose services from their own process rather than relying on one provided by Windows. Fortunately, this isn't hard to do. The following example shows how to create a process that hosts either of theCalculator classes defined earlier:
using System.ServiceModel;public class CalculatorHost{ public static void Main() { ServiceHost<Calculator> s1 = new ServiceHost<Calculator>(); s1.Open(); Console.Writeline("Press ENTER to end service"); Console.Readline(); }}Since the classCalculatorHost includes aMain method, it will run as a distinct process. To host the exampleCalculator service, this method must create a new instance of the classServiceHost<T>, passing in theCalculator class. (Note that this standard Indigo class is ageneric, indicated by the< and> that enclose its parameter. Generics are a new language feature in version 2.0 of C#, Visual Basic .NET, and other languages based on version 2.0 of the .NET Framework.) Once an instance of this class is created, the only thing required to make the service available is to call theOpen method on that instance. Indigo will now automatically direct requests from clients to the appropriate methods in theCalculator class.
To allow an Indigo service to process requests from its clients, the process that hosts it must remain running. This isn't an issue with WAS-hosted services, since the standard process WAS provides ensures this. A hosting application must solve this problem on its own, however. In this simple example, the process is kept running through the straightforward mechanism of waiting for input from a console user.
Along with defining operations in an Indigo service class and specifying a host process to run those operations, an Indigo service must also expose one or more endpoints. Every endpoint specifies the following three things:
ServiceContract that implements no explicit interfaces, such asCalculator in the first example shown earlier, can expose only one service contract. In this case, all of its endpoints will expose the same contract. If a class explicitly implements two or more interfaces marked withServiceContract, however, different endpoints can expose different contracts.Bindings are a critical part of how communication is accomplished. To make them easier to use, Indigo includes a set of predefined bindings, each of which specifies a particular group of options. This set includes:
.gif&f=jpg&w=240)
The figure above shows example values for each of the three elements in an endpoint for the firstCalculator service shown earlier. The name of the service's contract isCalculator, which is the name of the class that implements this service, and the binding isBasicProfileHttpBinding. Assuming this service is hosted using WAS, installed in the virtual directorycalculator as described earlier, and running on a machine named qwickbank.com, its address might behttp://www.qwickbank.com/calculator/calc.svc.
Unlike contracts, endpoints aren't defined using attributes. While it is possible to create endpoints programmatically, the most common approach will probably be to use a configuration file associated with the service. WAS-hosted services use the web.config file, while those hosted independently using the configuration file associated with the application they're running in (commonly referred to as app.config, although the actual filename varies). If used solely for the firstCalculator service class shown earlier, this configuration file might look like this:
<configuration> <system.serviceModel> <services> <service serviceType="Calculator"> <endpoint contractType="Calculator" bindingType="basicProfileHttpBinding /> </service> </services> </system.serviceModel></configuration>The configuration information for all services an Indigo application implements is contained within thesystem.serviceModel element. This element contains aservices element that can contain one or moreservice elements. This simple example has only a single service, so there's just one occurrence ofservice. TheserviceType attribute of theservice element identifies the service class that implements the service this configuration applies to, which in this case isCalculator. Eachservice element can contain one or moreendpoint elements, each of which specifies a particular endpoint through which this Indigo service can be accessed. In this example, the service exposes only a single endpoint, and so only oneendpoint element appears. The name of the endpoint's contract isCalculator, which is the name of the class that implements it. If this configuration file were for the secondCalculator service class shown earlier, which defined its service contract using an explicit interface, the value of theserviceType attribute would remain the same, but the value ofcontractType would instead beICalculator, the name of this explicit interface. The binding specified here isbasicProfileHttpBinding, although since this is the default, it could have been omitted. And assumingCalculator is a WAS-hosted service, an address is created automatically, so there's no need to specify one in this config file.
Creating a basic Indigo service isn't especially complicated. Creating an Indigo client is even simpler. All that's required is to create a local stand-in for the service, called aproxy, that's connected to a particular endpoint on the target service, then invoke the service's operations via this proxy. The figure below shows how this looks.
.gif&f=jpg&w=240)
Creating a proxy requires knowing exactly what contract is exposed by the target endpoint, then using this contract's definition to generate the proxy. In Indigo, this process is performed by a tool called svcutil. If the service is implemented using Indigo, svcutil can access the service's DLL to learn about the contract and generate a proxy. If only the service's WSDL definition is available, svcutil can read this to produce a proxy. If only the service itself is available, svcutil can access it directly using either WS-MetadataExchange or a simple HTTP GET to acquire the service's WSDL interface definition, then generate the proxy.
However it's generated, the client can create a new instance of the proxy, then invoke the service's methods using it. Here's a simple example of a client for theCalculator class:
using System.ServiceModel;using Indigo.Example; // namespace for generated proxy classpublic class CalculatorClient{ public static void Main() { CalculatorProxy p = new CalculatorProxy(); Console.WriteLine("7 + 2 = {0}", p.Add(7, 2)); Console.WriteLine("7 - 2 = {0}", p.Subtract(7, 2)); p.Close(); }}One more thing remains to be specified by the client: the exact endpoint it wishes to invoke operations on. Like a service, the client must specify the endpoint's contract, its binding, and its address, and this is typically done in a config file. In fact, if enough information is available, svcutil will automatically generate an appropriate client configuration file for the target service.
The basics of services and clients are fundamental to every Indigo application. Yet most of those applications will also use other aspects of this technology. This section takes a look at some of the additional features that Indigo provides for applications built on it.
Many aspects of Indigo, such as contracts, bindings, and more, are related to communication between a service and its clients. Yet there are also parts of a service's behavior that are essentially local. How is a service instance's lifetime controlled, for example, and how is concurrent access to that instance managed? To allow developers to control behaviors like these, Indigo defines two primary attributes, both of which have a number of properties. One of these attributes,ServiceBehavior, can be applied to classes that are also marked with theServiceContract attribute. The other,OperationBehavior, can be applied to methods in a service class that are also marked with theOperationContract attribute.
TheServiceBehavior attribute has various properties that affect the behavior of the service as a whole. For example, a property calledConcurrencyMode can be used to control concurrent access to the service. If set toSingle, Indigo will deliver only one client request at a time to this service, i.e., the service will be single-threaded. If this property is set toMultiple, Indigo will deliver more than one client request at a time to the service, each running on a different thread. Similarly,ServiceBehavior'sInstanceMode property can be used to control how instances of a service are created and destroyed. IfInstanceMode is set toPerCall, a new instance of the service will be created to handle each client request, then destroyed when the request is completed. If it's set toPrivateSession, however, the same instance of the service will be used to handle all requests from a particular client.
Suppose, for example, that its creator decided that theCalculator class should be multi-threaded and use the same instance for each call from a particular client. The class's definition would then look like this:
using System.ServiceModel;[ServiceContract] [ServiceBehavior( ConcurrencyMode=Multiple, InstanceMode=PrivateSession)]class Calculator { ... }Similarly, properties on theOperationBehavior attribute allow controlling the impersonation behavior of the method that implements this operation, its transactional requirements (described later), and other things.
The simple examples shown in this article assume a synchronous remote procedure call (RPC) approach to client/service interaction. Indigo supports this option, but it's not the only choice. SOAP is a message-oriented protocol, which means that it can support a variety of programming models. In fact, Indigo supports several possibilities, including the following:
And even though the great majority of distributed applications require it, the SOAP specification says nothing about reliability. One common way to ensure reliability is to use SOAP only in point-to-point scenarios, relying on TCP to guarantee delivery of requests and responses. This is sufficient in some cases, and it's what's done when the BasicProfileHttpBinding is used.
Yet there are plenty of cases for which this isn't enough. What if a service is accessed through multiple SOAP intermediaries, for example? The reliability guarantees provided by TCP aren't enough in this case to ensure end-to-end reliability. To address this problem, Indigo implements the WS-ReliableMessaging specification. By choosing a binding such as WsHttpBinding, which uses WS-ReliableMessaging, a service and its client can guarantee reliable end-to-end communication even through multiple SOAP intermediaries.
Exposing services on a network, even an internal network, usually requires some kind of security. How can the service be certain of its client's identity? How can messages sent to and from a service be kept safe from malicious changes and prying eyes? And how can access to a service be limited to only those authorized to use it? Without some solution to these problems, it's too dangerous to expose many kinds of services. Yet building secure applications can get complicated. Ideally, there should be straightforward ways to address common security scenarios, along with more fine-grained control for applications that need it.
To achieve this, Indigo provides the core security functions of authentication, message integrity, message confidentiality, and authorization. Indigo's approach to the first three of these relies primarily on bindings, and a developer's choices include the following:
An Indigo service can also control which clients are authorized to use its services. For the most part, Indigo just supports existing authorization mechanisms in the .NET Framework. A service can use the standardPrincipalPermission attribute, for example, to define who is allowed to access it.
Letting developers build secure applications without exposing them to overwhelming complexity has proven to be challenging in the past. By providing both a straightforward approach for the most common cases and fine-grained control for more complex situations, Indigo aims at hitting this target in a usable and effective way.
Handling transactions is an important aspect of building many kinds of business logic. Yet using transactions in a service-oriented world can be problematic. Distributed transactions assume a high level of trust among the participants, and so it often isn't appropriate for a transaction to span a service boundary. Still, there are situations where combining transactions and services makes good sense, and so Indigo includes support for this important aspect of application design.
The transaction support in Indigo builds on enhancements provided in version 2.0 of the .NET Framework. This forthcoming release includes System.Transactions, a new namespace focused solely on controlling transactional behaviors. Developers will most often use the services of System.Transactions in concert with anexecution context, a construct that's new in version 2.0 of the .NET Framework. An execution context allows specifying common information, such as a transaction, that applies to all code contained within a defined scope. Here's an example of how an application can use this approach to group a set of operations into a single transaction:
using System.Transactions;using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required)) { // Do work, e.g., update different DBMSs ts.Complete();}All of the operations within theusing block will become part of a single transaction, since they share the transactional execution context it defines. The last line in this example, calling theTransactionScope'sComplete method, will result in a request to commit the transaction when the block is exited. This approach also provides built-in error handling, aborting the transaction if an exception is raised.
SpecifyingRequired for the newTransactionScope, as this example does, means that this code will always run as part of a transaction: joining its caller's transaction if one exists, creating a new one if it doesn't. As in Enterprise Services, other options can also be specified, includingRequiresNew,Supported, andNotSupported.
Unlike Enterprise Services and its predecessors MTS and COM+, Systems.Transactions is focused entirely on controlling transactional behavior. There's no required connection between a transaction and the internal state of an object, for example. While Enterprise Services requires an object to be deactivated when it ends a transaction, Systems.Transactions makes no such demand. Since Indigo builds on Systems.Transaction, Indigo applications are also free to manage transactions and object state independently.
Indigo applications can use System.Transactions explicitly, or they can control transactions using attributes that rely on System.Transactions under the covers. One option is for a method inside a class marked with theServiceContract attribute to wrap its work in a transaction usingTransactionScope, as just described. For example, this method might include ausing statement that establishes a transaction scope, then update two independent databases within that transaction.
A service's methods can also control transactional behavior using an attribute. Rather than explicitly using System.Transactions, a service can use theOperationBehavior attribute described earlier. Here's an example:
using System.ServiceModel;[ServiceContract]class XactOperations{ [OperationContract] public int Add(int value1, int value2) { return value1 + value2; } [OperationContract] [OperationBehavior(RequireTransaction=true, AutoCompleteTransaction=true)] int Update(int value1, int value2) { // Insert value1 and value2 into // two different databases }}The first method in this example,Add, doesn't use a transaction, and so its simple operation will happen just as before. But the second method,Update, is preceded by theOperationBehavior attribute with theRequireTransaction property set to true. Because of this, all work done within this method will happen inside a transaction, just as if it were inside the transaction scope of theusing block shown earlier. And because theAutoCompleteTransaction property is also specified, the transaction will automatically vote to commit if no exception is raised.
If the client invoking this method is not running inside a transaction, theUpdate method will run in its own transaction—there's no other choice. But suppose the client is already part of an existing transaction when it callsUpdate. Will the work done by theUpdate method join the client's transaction, or will it still run inside its own independent transaction? The answer depends on whether this service can accept atransaction context passed by the client, an option controlled using theTransactionFlowAllowed property of theOperationContract attribute. IfTransactionFlowAllowed is not attached to a method in the service, as in the example shown above, work done within this method can never join an existing transaction. If this attribute is present, however, a method is able to join the transaction of its client.
It's also worth emphasizing that applications built on Indigo can participate in transactions that include applications running on non-Indigo platforms. For example, an Indigo application might start a transaction, update a record in a local SQL Server database, then invoke a web service implemented on a J2EE application server that updates a record in another database. If this service is transactional and the platform it's running on supports the WS-AtomicTransaction specification, both database updates can be part of the same transaction. Like security and reliable messaging, Indigo transactions work in the heterogeneous world that Web services make possible.
Using a binding such as WsHttpBinding, an Indigo application can communicate reliably with another application built on Indigo or any other Web services platform that implements WS-ReliableMessaging. But while the technology this specification defines guarantees reliable end-to-end delivery of a SOAP message, it doesn't address message queuing. With queuing, an application sends a message to a queue rather than directly to another application. When the receiving application is ready, it can read the message from the queue and process it. Allowing this kind of interaction is useful, for example, when the sender of a message and its receiver might not be running at the same time.
Accordingly, Indigo provides support for message queuing. This support is built on top of MSMQ, which means that unlike most other aspects of Indigo, such as reliable messaging, security, and transactions, Indigo queuing doesn't interoperate directly across vendor boundaries (although an MSMQ-MQSeries bridge is available).
To use Indigo queuing, a developer creates a standard Indigo service class, marked as usual withServiceContract. The operations in this class's service contract have some limitations, however. In particular, they must all be marked as one-way, which means that no response is returned. This isn't surprising, since invoking a queued operation sends a message into a queue rather than to its ultimate receiver, and so waiting for an immediate response wouldn't make much sense. And like any other service class, queued Indigo applications expose endpoints. These endpoints use bindings such as NetMsmqBinding, which allows communication with other queued Indigo applications, or MsmqIntegrationBinding, which allows a queued Indigo application to interoperate with a standard MSMQ application that doesn't use Indigo. Indigo queuing also supports other traditional features of queued environments, such as dead letter queues and handling of poison messages.
Queuing is the right approach for a significant set of distributed applications. Indigo's support for this communication style allows developers to build queued applications without learning an entirely separate queuing technology.
Indigo represents a modern approach to creating distributed applications in the era of reliable, secure, and transactional services. A key point to understand, however, is that installing Indigo will not break any existing applications. Current code running on ASMX, .NET Remoting, and the other technologies whose functionality is subsumed by Indigo will continue to run, and so there's no requirement to move to Indigo. But for organizations with investments in current Microsoft technologies, an obvious question remains: what happens to existing code written using the technologies that preceded Indigo?
For each of the current technologies whose future is deeply affected by the advent of Indigo, developers need to understand two things: whether applications built on this technology will interoperate with applications built on Indigo, and how much work it will be to port applications from this technology to the Indigo environment. Here's a short description of how each technology addresses these issues:
Before Indigo is available, the best technology choice for distributed .NET applications that don't need queuing is probably ASMX. It's simple to use, and it also provides the smoothest migration path to Indigo. Enterprise Services can also make sense for applications that need the things it provides, such as distributed transactions, while MSMQ remains the right choice for queued applications. .NET Remoting, however, should be used primarily for communication between two application domains in the same process. ASMX is a better choice for direct application-to-application communication in most other cases.
Introducing new software always has an impact on what already exists. By providing a common foundation for building service-oriented applications, Indigo offers a simpler, more consistent platform for developers. While this change incurs some pain, the goal of Indigo's creators is to make the transition as smooth and as simple as possible.
Indigo represents an important evolution in how developers create software. As service-oriented applications become the norm, Indigo will be a mainstream technology for Windows software developers. Other Microsoft products will also change to take advantage of what Indigo offers. BizTalk Server, for example, will add support for Indigo as a communication option sometime following the release of BizTalk Server 2006. Because Indigo provides a standard foundation for service-oriented software, it will be the basis for a large fraction of Windows communication.
The impact of this technology will not be small. Anyone who builds distributed applications on Windows, especially applications that must interoperate with those on other platforms, should pay close attention. Indigo will significantly change their world.
About the Author
David Chappell is Principal of Chappell & Associates (www.davidchappell.com) in San Francisco, California. This article is drawn from his forthcoming book on Indigo, to be published by Addison-Wesley.