MSDN Home >  MSDN Library >  Windows Development > 

Creating Indigo Applications with the PDC Release of Visual Studio .NET Whidbey

Yasser Shohoud
Microsoft Corporation

January 13, 2004

Summary: In the first installment of the Indigo Lingo column, Yasser Shohoud explores Visual Studio .NET project templates for creating Indigo applications. (11 printed pages)

This year's Professional Developers Conference (PDC) attendees received a technology preview of the upcoming Windows® release code-named Longhorn that contains a new communications subsystem code-named Indigo. They also received a preview of the Longhorn SDK that contains several Indigo development tools and over 40 Indigo code samples, as well as Visual Studio .NET® Indigo project templates.

In this article I explain how to create and consume services using the PDC release of Longhorn, Visual Studio® .NET code-named Whidbey, and the various Visual Studio .NET project templates present in the Longhorn SDK. The goal is to get you started with a general understanding of what's in the bits and how you can use the templates.

Writing Your First Indigo Service

The PDC release of Indigo exposes two service programming models: a high-level programming model named the Service Framework, or SFx, and a lower-level one named the Messaging Framework, or MFx.

MFx is designed to give you direct access to the four fundamental Indigo concepts described in the MSDN Magazine article, Code Name Indigo: A Guide to Developing and Running Connected Systems with Indigo, namely ports, messages, channels, and services.

SFx, on the other hand, is designed to abstract away these concepts and provide a mostly-declarative (attribute and config-based) API that's similar in spirit to ASP.NET Web services.

I will first show you how to create and consume services using MFx and then show you how to do the same using the higher-level SFx. Finally, I will show you how to host Indigo services in ASP.NET.

Creating a Service Using MFx

The PDC release of the Longhorn SDK contains Visual Studio project templates for creating and consuming services using MFx. To access these templates, you need to first install the PDC release of Visual Studio .NET Whidbey, and then install the Longhorn SDK. You will get three new Visual Studio .NET project templates, including one named "Indigo Messaging Framework Service" as shown in Figure 1.

Figure 1. Visual Studio project templates

Selecting this project template gives you a console application project with the five files shown in Figure 2, of which MsgServer.cs and MsgServerService.cs are the most interesting.

Figure 2. Console application project

MsgServer.cs contains the application's entry point that acts as a simple host for the Indigo service (see Listing 1). The entry point starts the Indigo service by instantiating a MsgServerService object and calling it's Run method. It then waits until the user presses enter and then shuts down the Indigo service by calling its Close method.

Listing 1. MsgServer.cs contains the application's entry point that starts and stops the Indigo service.

using System;

namespace IndigoMFxService1
{
   class MsgServer
   {
      /// The main entry point for the application.
      [STAThread]
      static void Main(string[] args)
      {
         try
         {
            Console.WriteLine(
               "Starting MsgServerService...");

            MsgServerService service = 
               new MsgServerService();

            service.Run();
            Console.WriteLine(
               "Press enter to exit.");
            Console.ReadLine();
            service.Close();
         }
         catch (Exception e)
         {
            Console.WriteLine(
               "msgServer: Exception thrown.");
            Console.WriteLine(
               "msgServer: {0}", e.Message);
         }
      }
   }
}

MsgServerService.cs contains the two classes shown in Figure 3 that together represent the actual Indigo service code.

Figure 3. Static structure of Indigo service code

MsgServerService is the service that listens on an Indigo port waiting for messages. MsgServerService.Run starts the service by instantiating a Port, passing it the service's URI (which is obtained from config), then setting the port's ReceiveChannel.Handler to a new instance of MyReceiveChannelHandler. When a message arrives, Indigo calls MyReceiveChannelHandler.ProcessMessage and passes it the message. The default ProcessMessage template code writes a notification to the console and then verifies the message is not null before closing it and returning false. The Boolean returned from ProcessMessage indicates whether a message stream can still be used. As explained in the aforementioned article on developing and running connected systems on Indigo, messages support streaming I/O; therefore, if a message handler reads the contents of a message, that message cannot be read again. After consuming a message, a message handler is required to call Close on the message and to return false indicating the message stream cannot be used. Although the template code doesn't consume the message by default, you can consume the message yourself by inserting your own code or uncommenting the existing template code.

The ProcessMessage method contains a block of commented-out code that serves as an example of processing the input message and sending a reply message, as shown in Listing 2. The first few lines write out to the console the message's Action, Content, DidUnderstand, and Encoding properties. The next few lines form a foreach loop that writes out the message header names. The last five lines in this block of commented code create a reply using the message's CreateReply method. This method returns a reply message object that can then be sent by calling the Send method of the port's SendChannel.

Listing 2. MyReceiveChannelHandler.ProcessMessage

public override bool ProcessMessage(Message message)
{

       Console.WriteLine("Message received");
 
       Debug.Assert(null != message, 
             "ProcessMessage received a null message");
   if (null == message)
      return false;

   // print out the properties of the message
   Console.WriteLine("MyReceiveChannelHandler:ProcessMessage");
   Console.WriteLine("message.Action: {0}", 
                    message.Action.OriginalString.ToString());
   Console.WriteLine("message.Content: {0}", 
                    message.Content.GetObject(typeof(string)));
   Console.WriteLine("message.DidUnderstand: {0}", 
                    message.DidUnderstand.ToString());
   Console.WriteLine("message.Encoding: {0}", 
                    message.Encoding.ToString());

   // print out the headers of the message
   Console.WriteLine("message.Headers");
   foreach (MessageHeader msgHeader in message.Headers)
   {
      Console.WriteLine("message.Header: {0}", msgHeader);
   }

   Console.WriteLine("ProcessMessage - creating reply message");
   string content = string.Format("How are you? From MsgServer: {0}", 
                                       DateTime.Now.ToString());
   Message replyMessage = message.CreateReply(null, new ObjectContent(content));

   _port.SendChannel.Send(replyMessage);
   Console.WriteLine("ProcessMessage - exit");

   // we received the message, therefore we'll close it.
   message.Close();
   return false;
}

Consuming the Service Using MFx

Listing 3 shows the minimal client code required to invoke the above Indigo service. The client first creates and opens a port, then uses that port to create a SendChannel. After creating a message object containing a simple string, the client calls Send on the SendChannel and passes it the message object.

Listing 3. A simple client to invoke the above service

class IndigoClient
{
   static void Main(string[] args)
   {
      System.MessageBus.Port port = new System.MessageBus.Port();
      port.Open();
      Uri serviceUri = new Uri("soap.tcp://localhost:46005/msgserverservice");
      System.MessageBus.SendChannel channel=port.CreateSendChannel(serviceUri);
      System.MessageBus.Message msg = new System.MessageBus.Message(
         new Uri("http://some.fake.action/server"),
         "some data in the message");
      channel.Send(msg);
   }
}

Instead of manually writing the code in Listing 3, the Indigo Messaging Framework Client Visual Studio template (as shown in Figure 4) creates a boilerplate project for consuming Indigo services using MFx.

Figure 4. Creating a boilerplate project for consuming Indigo services using MFx

The resulting project contains five files, of which MsgClient.cs and MsgServerClient.cs are the most interesting. MsgServerClient.cs contains a class named MsgServerClient that exposes three public methods shown in Listing 4.

Listing 4. MsgServerClient methods

public class MsgServerClient
{
   public void Open(string servicePortIdentity)
   { ... }
   public void SendMessage(string action, string content)
   { ... }
   public void Close()
   {...}
}

The Open method creates a port using a URI retrieved from app.config. This URI represents the client's endpoint address and allows others applications to send messages to this client. This URI is not strictly required for our request/reply scenario, but the project template includes it anyway. Missing from the code in Listing 3 is the ability to receive and process the correlated reply message sent back from the service. To do this, the template code sends the message using a SendRequestChannel instead of a SendChannel. To get a SendRequestChannel, an instance of RequestReplyManager is created and its CreateSendRequestChannel method is called. The returned SendRequestChannel object allows the service to send replies back over the same transport connection (e.g. over the same TCP connection) rather than opening a new connection back from the service to the client (which is also possible since our client does have its own endpoint address). The SendMessage method creates a message object for the request and another for the response, calls the SendRequestChannel's SendRequest method, passes it the request message, and gets back the reply message. The Close method simply closes the client's port.

MsgClient.cs contains the application's entry point. The code in this entry point simply instantiates a MsgServerClient object, and then calls its Open method, and passes it the service's URL (retrieved from app.config). Next it calls the MsgServerClient's SendMessage method followed by its Close method.

Creating a Service Using SFx

To create an Indigo SFx service, simply create a new project from the Indigo Service Framework Service template as shown in Figure 5. A project with five files is generated. The service is a class named IndigoService in IndigoService.cs. The class contains one commented-out method named Greeting. The method signature is rather plain except for the ServiceMethod attribute that indicates that the Greeting method is part of the service's public contract. The class itself is decorated with the DatagramPortType attribute that indicates that messages to and from the service do not enjoy any delivery guarantees (i.e., no reliable messaging). This is semantically equivalent to services created using ASP.NET Web services (ASMX). DatagramPortType's Name and Namespace properties control the resulting WSDL port's qname.

Figure 5. Creating a new project from the Indigo Service Framework Service template

To bootstrap the service, the application's entry point in host.cs loads a ServiceEnvironment. This logical container or environment in which services execute provides a scoping mechanism so that different services can run under different configurations within the same appdomain. Loading a ServiceEnvironment without specifying a name simply loads the ServiceEnvironment named "main" from app.config. Within its scope, a ServiceEnvironment can contain managers for a variety of things including services (ServiceManager), security (SecurityManager), and transactions (TransactionManager). The ServiceManager's ActivatableServices property is a collection of service types that can be activated within the ServiceEnvironment. In this case, there's only one service type, namely IndigoSFxService1.IndigoService.

Consuming the Service Using SFx

One nice thing about SFx services is that there's a straightforward way to generate a WSDL document that describes the service by reflecting over the service and its methods. The Longhorn SDK contains a tool named wsdlgen.exe that, when run on an assembly, generates WSDL and Schema documents describing SFx services in that assembly. As one would expect, wsdlgen.exe can also be used to generate proxy code from a WSDL document. Running wsdlgen.exe on the compiled IndigoSFxService1.exe generates a WSDL document and two schema documents. Running wsdlgen.exe on the generated documents (wsdlgen.exe tempuri_org.wsdl tempuri_org.xsd) results in a code file named tempuri_org.cs that contains an interface named IHelloChannel that can be used to invoke the service.

To use this generated proxy code, you'll need to create another project that acts as the client. This new project will also need an app.config with a ServiceEnvironment that removes the SecurityManager and allows UntrustedPolicyAttachments. An easy way to create this config file is by copying app.config from IndigoSFxService1 project and removing the <port> element and its content. Next, add tempuri_org.cs to the project and add a reference to System.MessageBus.dll. Finally, here's the client code that invokes the service:

Listing 5. SFx client code for invoking the service

// Load the default service environment, called "main".
ServiceEnvironment se = ServiceEnvironment.Load();
// Retrieve the ServiceManager from the default environment
ServiceManager sm = 
   se[typeof(ServiceManager)] as ServiceManager;
// Start the service environment.
se.Open();
// Create a proxy channel that points to the service to call.
Uri uri = new Uri("soap.tcp://localhost:46001/HelloService/");
IHelloChannel channel = (IHelloChannel)sm.CreateChannel(
   typeof(IHelloChannel), uri);
try 
{
       // This is the service call
   Console.WriteLine(channel.Greeting("Indigo client"));
   Console.WriteLine("Press enter to exit");
   Console.ReadLine();
}
catch(Exception Ex)
{
   Console.WriteLine(Ex);
}
finally
{
   se.Close();
}

Note that the ServiceManager's CreateChannel method is used to create a proxy object that implements IHelloChannel that can then be invoked to send messages to the service and receive reply messages. To call the service's Greeting method, you simply call IHelloChannel.Greeting. This RPC-like style is very similar to the ASP.NET Web services' programming model with the notable difference that it's interface-, not class-, based.

Hosting the Service in ASP.NET

Both of the services created to this point were hosted in console applications. The ability to host services in any type of application is a very useful feature of Indigo that allows applications to communicate in ways not possible before. However, in many cases, you'll want to host your services in ASP.NET to take advantage of auto-startup, recovery, and manageability features. Visual Studio .NET lets you easily create MFx services hosted in ASP.NET by adding a new item to your ASP.NET Web site and selecting the Indigo Messaging Framework Service. The result is two files: an IndigoService.msgx file that contains a simple directive and an IndigoService.msgx.cs file that contains the actual service code. The service code is in a class named IndigoService_msgx, which is simply a message handler class that inherits from SyncMessageHandler similar to MyReceiveChannelHandler class that we used in the MFx service. The interesting code that reads the message and possibly sends a reply message is located in the ProcessMessage method, again similar to MyReceiveChannelHandler.

One difference between IndigoService_msgx and MyReceiveChannelHandler is that the former class also implements IhostedMessageHandler, which is contains one property that returns a MessageHandlerSite object:

public interface IHostedMessageHandler
{
  public MessageHandlerSite Site { get; set; } 
}

MessageHandlerSite provides one interesting property that returns the current ServiceEnvironment. This property allows the service to reach out and grab its ServiceEnvironment, then use it to retrieve other interesting objects, such as the service's port. There are a couple of limitations of ASP.NET-hosted services in the PDC release of Indigo: First, services can only be hosted in ASP.NET when running in IIS and not in Cassini. Second, ASP.NET–hosted services support only MFx. These limitations are specific to the PDC release and should be removed by the Indigo beta (meaning we had to ship the PDC bits before these features were done). For more information on configuring IIS and ASP.NET for hosting Indigo services, including enabling HTTP- and TCP-based activation, see the Indigo Hosting sample's Readme.htm document available in the Longhorn SDK and online.

Conclusion

The Indigo Visual Studio templates give you a quick way to get started with Indigo applications that use either SFx or MFx. To get these templates, install the PDC release of Longhorn and then install the PDC release of Visual Studio .NET Whidbey, followed by the PDC release of the Longhorn SDK. Finally, as you begin exploring Indigo and have questions or find bugs, please post to the Indigo newsgroup at microsoft.public.windows.developer.winfx.indigo.

References

Code Name Indigo: A Guide to Developing and Running Connected Systems with Indigo

Longhorn SDK

Longhorn and Visual Studio .NET Whidbey Previews

Indigo Frequently Asked Questions

The Road to Indigo Technology Roadmap

 


Indigo Lingo

Yasser Shohoud is a Program Manager on the Indigo team where he's responsible for Indigo performance and ASP.NET Web services. He's written several Web services articles and a book entitled Real World XML Web Services. In his free time, Yasser enjoys snowboarding in the Cascade Mountains near Seattle.


©2004 Microsoft Corporation. All rights reserved. Terms of Use |Privacy Statement
Microsoft