用Visual Studio .Net Whidbey PDC发布版创建Indigo应用程序[译]
Hyperlink: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnlingo/html/indigolingo01062004.asp
Yasser Shohoud
Microsoft Corporation
今年(译注:按现在的时间已经是去年了。本文发表于2004年1月),开发者大会(Professional Developers Conference)的与会嘉宾预览了即将发布的名为LongHorn的Windows操作系统,该系统包括了新的分布式子系统——Indigo。同时,他们也预览了Longhorn SDK,其中包含有好几个Indigo开发工具和超过40个以上的Indigo样例,即Visual Studio.Net Indigo项目模板。
本文,我将介绍如何使用PDC的Longhorn发布版——名为Whidbey的Visual Studio.Net,创建和调用服务。我将使用在Longhorn SDK中公布的不同的Visual Studio .Net 项目模板。目的在于使读者对其获得总体的了解,以及学会如何使用这些模板。
你的第一个Indigo Service
Indigo的PDC发布版包含两种服务编程模型:名为Service Framework(简称SFx)的高级编程模型,和名为Messaging Framework(简称MFx)的低级编程模型。
MFx支持开发人员直接访问Indigo的四种基本概念,在MSDN杂志《用Indigo开发和运行连接系统的指南》一文中,分别为:端口(ports),消息(messages),信道(channels)和服务(services)。
另一方面,SFx则将这些概念进行抽象,并提供可声明的API(attribute和.config),它类似于ASP.Net中的Web Services。
我首先介绍使用MFx模型创建和调用服务,然后再介绍使用SFx模型做相同的事情。最后,我还将介绍怎样将Indigo服务加载到ASP.NET中。
使用MFx创建服务
在Longhorn SDK的PDC发布版中,为使用MFx创建和调用服务提供了Visual Studio项目模板。如要访问这些模板,你首先需要安装Visual Studio .NET Whidbey的PDC发布版,同时还要安装Longhorn SDK。你可以看到三个新的Visual Studio .NET项目模板,其中包括模板“Indigo Messaging Framework Service”,如图一所示。
Figure 1. Visual Studio project templates
选择该项目模板将创建一个控制台应用程序,同时系统将自动创建如图二所示的五个文件,最重要的是MsgServer.cs和MsgServerService.cs。
Figure 2. Console application project
MsgServer.cs包含了应用程序的入口点,并作为Indigo Service的一个简单的宿主(如Listing 1)。该入口点通过实例化MsgServerService对象启动Indigo Service,并调用其Run方法。然后它将处于等待状态,直到用户敲下回车键,然后调用Close方法关闭Indigo Service。
Listing 1. MsgServer.cs包含了启动和停止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包含了如图三所示的两个类,它们同时都存在于Indigo service的代码中。
Figure 3. Static structure of Indigo service code
MsgServerService是一个服务,它能侦听Indigo端口等待发送过来的消息。MsgServerService.Run通过实例化端口启动服务,并将服务的URI(从.config中获得)传递给端口,然后将MyReceiveChannelHandler实例赋给端口的ReceiveChannel.Handler。当消息到达的时候,Indigo会调用MyReceiveChannelHandler.ProcessMessage方法,以处理信息。默认的ProcessMessage模板代码,仅仅包含一个对控制台的通知,并再关闭时检查消息是否为not null,然后返回false值。从ProcessMessage返回的布尔值表明消息流是否已被使用。在《用Indigo开发和运行连接系统的指南》一文中,阐释了消息支持流I/O;因此,如果消息处理器读了消息的内容,该消息将不可再读。再使用了消息之后,消息处理器需要调用消息的Close方法,然后返回false标志表明该消息流不可再用。默认的模板代码中,没有使用消息,但你可以通过插入自己的代码来使用消息或者取消对已有的模板代码的注释。
ProcessMessage方法包含了一段代码,作为服务器处理输入的消息和发送下响应消息的例子,如Listing 2所示。前面几行向控制台输出了消息的Action,Content,DidUnderstand和Encoding属性。接下来是通过foreach循环输出消息头的名字。最后五行调用了CreateReply方法,创建了一个响应。该方法返回一个响应消息对象,这个对象可以通过端口的SendChannel属性的Send方法进行发送。
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;
}
通过MFx使用服务
Listing 3展示了调用上面所述的Indigo Service所需的最少的客户端代码。客户端首先创建和打开一个端口,然后用端口创建一个信道SendChannel。在创建了包含一个简单字符串的message对象后,客户端将调用SendChannel的Send方法,并传递到该message对象中。
Listing 3. 调用上述服务的一个简单客户端例子
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);
}
}
与Listing 3中手工写的代码不一样,Indigo的Messaging Framework客户端项目模板(如图四所示),创建了一个用MFx使用Indigo服务的示例项目。
Figure 4. Creating a boilerplate project for consuming Indigo services using MFx
生成的项目包含5个文件,其中最重要的是MsgClient.cs和MsgServerClient.cs。MsgServerClient.cs包含了一个MsgServerClient类,该类有三个公共方法,如Listing 4所示。
Listing 4. MsgServerClient methods
public class MsgServerClient
{
public void Open(string servicePortIdentity)
{ ... }
public void SendMessage(string action, string content)
{ ... }
public void Close()
{...}
}
Open方法通过从app.config中获得的URI创建了一个端口。该URI表示客户端终端地址,并允许其他应用程序发送消息到这个客户端。该URI并不要求必须严格符合请求/响应的场景,但项目模板都会包含它。在Listing 3中不具备的代码段,完成的功能是获取和处理从服务中送回的相关的响应消息。要实现该目的,模板代码通过SendRequestChannel方法发送消息,而非SendChannel。为了获得SendRequestChannel,可以创建RequestReplyManager实例,并调用该实例的CreateSendRequestChannel方法。返回的SendRequestChannel对象允许服务经由相同的传输连接(例如,相同的TCP连接),而不是建立一个新的连接,发送响应消息到客户端(由于客户端拥有自身的终端地址,使该操作成为可能)。SendMessage方法分别为请求和响应创建了message对象,并通过调用SendRequestChannel的SendRequest方法,将其传送到请求消息,并返回一个响应消息。Close方法则仅仅完成关闭客户端端口的功能。
MsgClient.cs包含了应用程序的入口点。入口点的代码仅仅实例化MsgServerClient对象,然后调用了它的Open方法,并将服务的URL(从app.config中获取)作为参数传入。接下来,再调用MsgServerClient的SendMessage方法,然后调用Close方法。
通过SFx创建服务
要创建Indigo SFx服务,可以简单地通过如图五所示的Service Framework Service模板创建一个新的项目。创建的项目将生成5个文件。在IndigoService.cs中是服务类IndigoService。该类中有一个被注释了的方法,名为Greeting。方法签名很简单,而ServiceMethod特性则表明Greeting方法是服务公共契约(service’s public contract)中的一部分。类本身由DatagramPortType特性修饰,表明服务收发的消息在传输中并不保证是安全的(如,不被信赖的消息 no reliable messaging)。其语义可以认作是通过ASP.NET Web Service创建的服务。DatagramPortType的Name和Namespace属性控制了WSDL端口名的结果。
Figure 5. Creating a new project from the Indigo Service Framework Service template
为了解除对服务的某些制约,应用程序的入口点在hots.cs中装载了ServiceEnviroment。这种执行服务的逻辑容器或者说是环境,提供了一种局部机制,使得不同的服务可以根据不同的配置运行在相同的应用程序域中。如果没有特别指定ServiceEnvironment,装载的ServiceEnvironment名在app.config中被配置为“main”。在这个范围内,ServiceEnviroment可以包含多种管理器,包括服务管理器(ServiceManager),安全管理器(SecurityManager)和事务管理器(TransactionManager)。ServiceManager的ActivatableServices属性是一个服务类型的集合,它可以在ServiceEnvironment里被激活。在本例中,仅有一个服务类型,名为IndigoSFxService1.IndigoService。
通过SFx使用服务
关于SFx服务,精彩的是它提供了一种简单生成WSDL文档的方法,该文档描述了服务和方法的信息,而这些信息是由反射获得的。在Longhorn的SDK中有一个名为wsdlgen.exe的工具,将其运行在程序集上时,将产生WDDL和样式文档,其内容包括程序集中的SFx服务。同理,wsdlgen.exe也可以通过WSDL文档生成代理代码。在编译后的IndigoSFxService1.exe上运行wsdlgen.exe,将产生一个WSDL文档和两个样式文档。在生成的文档上运行wsdlgen.exe(wsdlgen.exe tempuri_org.wsdl tempuri_org.xsd),将产生tempuri_org.cs的代码文件,其中包含了名为IhelloChannel的接口,它将被用作对服务的调用。
使用生成的代理代码(proxy code),你还可以在客户端创建另一个项目。该项目同样需要包含了ServiceEnvironment的app.config文件。但ServiceEnvironment中不包括SecurityManager,即允许非信赖策略(UntrustedPolicyAttachments)。创建该配置文件最简单的办法就是从IndigoSfxService1项目中复制app.config,然后移去<port>元素及其内容。再添加tempuri_org.cs文件到项目中,并添加System.MessageBus.dll的引用。最后,调用服务的客户端代码如下:
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();
}
注意ServiceManager的CreateChannel方法被用作创建一个代理对象,该对象实现了IHelloChannel接口,以用来发送消息到服务,及接受响应消息。如要调用服务的Greeting方法,很简单,你可以直接调用IHelloChannel.Greeting。这种类似于RPC的方式与ASP.Net Web service编程模式相似。但应注意的是,该方式是基于接口的,而非基于类的。
ASP.NET宿主服务
上述创建的服务都是以控制台应用程序为宿主。而Indigo其中一个非常有用的特色可以将服务加载到任何类型的应用程序中,而在以前,这种应用程序间通信的方式是不可能的。然而在大多数情况下,你可能更希望将你的服务加载到ASP.NET中,使其能够更好的利用自动启动、恢复,以及其他易于管理的特性。在Visual Studio.NET下,创建ASP.NET中的MFx服务非常简单,只需要在你的ASP.NET web站点下,选择Indigo Messaging Framwork Service,以添加一个新的项目。它将产生两个文件:IndigoService.msgx和IndigoService.msgx.cs。前者仅包含了一个简单的指示,而后者才是实际的服务代码。服务代码放在IndigoService_msgx类中,该类作为一个消息处理类,继承了SyncMessageHandler类。类SyncMessageHandler与我们在MFx服务中使用的MyReceiveChannelHandler类相似。值得关注的代码是ProcessMessage方法,功能是读取消息,也可能会发送响应消息。这同样与MyReciveChannelHandler中相应的方法相似。
IndigoService_msgx和MyReceiveChannelHandler的一个区别,是前者实现了IhostedMessageHandler接口。该接口包含一个返回MessageHandlerSite对象的属性:
public interface IHostedMessageHandler
{
public MessageHandlerSite Site { get; set; }
}
MessageHandlerSite提供了一个有趣的属性,它返回当前的ServiceEnviroment。该属性允许服务获取它的ServiceEnviroment,然后再用它去获取其它有用的对象,例如服务的端口。ASP.NET宿主服务在Indigo的PDC版中有几个限制:首先,服务仅能加载到运行IIS的ASP.NET,不支持Cassini。第二,ASP.NET宿主服务仅支持MFx。这些限制仅针对PDC版,在Indigo试用版中将不存在(这意味着我们不得不受囿于PDC版本的这些限制)。如需获得更多关于IIS和ASP.NET的配置信息,以加载Indigo服务,包括基于HTTP和TCP激活的方法,请参考Longhorn SDK的Readme.htm文档或在线帮助中的Indigo Hosting样例。
结束语
Indigo Visual Studio模板为你提供了快速创建Indigo SFx或MFx应用程序的方法。要获得这些模板,需要安装Longhorn的PDC发布版,然后还需安装Visual Studio .NET 2005的PDC发布版。最后,当你开始Indigo的探索之旅时,如果有什么问题或发现了bug,请登录Indigo新闻组(microsoft.public.windows.developer.winfx.indigo),参与讨论。