WCF分布式开发必备知识(3):Enterprise Services(企业服务与COM+)

简介:
   今天继续我们< WCF分布式开发必备知识>系列文章第3节,通过前两节的学习,我们已经掌握了MSMQ和.Net Remoting的概念和开发过程.今天我们来学习..Net分布式开发的另外一个技术: Enterpise Services.本节的基本结构是1.EnterpriseServices和COM+的基本概念.2.Enterprise Services中重要的类及特性3.Enterprise Services实现COM+事务的编码\部署\测试过程.最后是总结.有兴趣的可以看一下我翻译的文章.经典技术文章翻译(1):COM+集成:.NET Enterprise Services 如何帮你建立分布式应用
     那么现在我们就开始今天的学习,首先介绍的是:
     一.基本概念
     1.什么是COM+
     COM+的底层结构仍然以COM为基础,它不仅具备了COM的易于用户定制\可重用\简化开发的优点,同时又避免了COM实现方面的一些不足.把COM、DCOM和MTS的编程模型结合起来,它通过操作系统的各种支持,使组件对象模型建立在应用层上,继承了它们的绝大多数特性,增加了新的功能。  COM+的几个主要特性: 比如队列服务、负载平衡、内存数据库、事件服务等。它更加注重于分布式网络应用的设计和实现(参考msdn)。     
2.什么是 Enterpise Services
      Enterprise Services是微软应用程序服务器技术的别称..Net Enterprise Services提供了可以在.Net 组件中使用的COM+服务.因为它也是基于以前的COM+技术,在.NET平台上开发.NET组件,使用Enterprise Services可以将.NET组件并进行封装为COM对象,这样.NET组件就可以使用COM+服务了..NET做为新一代的开发平台,实现了COM组件相互之间的调用,继承了向前兼容的优良传统.同样COM+可以调用.Net 组件,会给COM+服务带来一些扩展.
     二. Enterprise Services中重要的类及特性
     Enterprise Servicesg工作在三层架构中的业务逻辑层或者数据访问层.由于基于COM+技术,所以它也可以通过Micro Application Center Server实现负载均衡.下面我们来介绍一下Enterprise Services里几个重要的概念.
(1)上下文(context又翻译为环境):所有的 COM+服务都是通过上下文(context又翻译为环境)实现的,上下文是进程里提供给对象运行时服务的空间.如果一个对象调用另外一个上下文里的对象,这个方法调用会被代理截取, COM运行时就可以利用代理来预处理或者迟处理请求并执行相关的服务代码.过程如图1
图1 COM+服务与上下文
(2)自动事务处理(automatic transaction):
     Enterprise Services里最常用的特性就是自动事务处理,这个一般在类的星星 [Transaction]使用这个特性,就不需要再编写复杂的代码来显示执行事务准备工作,如对象的传递等,设置这个属性后,上下文环境会在后台进行设置.具体的设置在TransactionOption类的属性里选择.Required表示共享一个事务,必要的时候创建一个新事务.
(3)对象池(Obejct pooling):
     这个属性可以设置是需要对象池和对象池的大小.主要是考虑到对象的创建和销毁会占用更多的资源.使用对象池会事先创建对象,客户的请求到来就直接到对象池里查找需要的对象,直接响应请求,可以提高服务的性能,节约时间.
(4)ServicedComponent:
     是所有使用COM+服务类的基类.ServicedComponent继承自ContextBoundObject类.ContextBoundObject继承自MarshalByRefObject.这个类在上一节的.Net Remoting介绍过,使支持remoting的程序可以跨程序域边界的访问对象.
     另外还有分布式事务处理的等概念,Enterprise Services里是使用DTC来实现的.
     三. Enterprise Services实现COM+事务的编码\部署\测试过程
     上面我们熟悉了Enterprise Services的特性和一些重要的概念,下面我们就来利用Enterprise Services技术实现里COM+事务处理.在分布式应用程序中,我们往往需要同时操作多个数据库,使用数据库本身的事务处理,很难满足程序对事务控制的要求。
     大家都知道事务具有ACID的特性:Aotomicity原子性\Consistency一致性\Isolation独立性\Durability永久性. 
    原子性就是一组数据库操作被视为一个单元。要么所有的操作都成功;如果其中一个操作失败,则整个事务失败。事物失败,不会提交更改。
     具体的实现过程如下:
     1.编写Enterprise Services  COM+事务服务组件:
     首先我们使用Visual Studio 创建一个类库项目,添加程序集 System.EnterpriseServices的引用.添加类 EnterpriseServicesCOMPlus继承自 ServicedComponent,确保可以使用COM+服务.命名空间外设置程序集属性.具体代码
InBlock.gif    1using System; 
InBlock.gif    2using System.Collections.Generic; 
InBlock.gif    3using System.Text; 
InBlock.gif    4using System.Data; 
InBlock.gif    5using System.Data.SqlClient; 
InBlock.gif    6using System.EnterpriseServices; 
InBlock.gif    7[assembly: ApplicationName( "FrankEnterpriseServicesTransactionTest")] //在组件服务浏览器里的名称 
InBlock.gif    8[assembly: Description( "Coded By Frank Xu Lei 2/14/2009")] //在组件服务浏览器里的描述信息 
InBlock.gif    9[assembly: ApplicationActivation(ActivationOption.Server)] //启动类型Library在创建进程里激活,运行在客户进程,Server类型系统进程激活, 
InBlock.gif 10[assembly: ApplicationAccessControl( false)] //访问设置,关闭后,用户都可以访问 
InBlock.gif 11namespace EnterpriseServicesCOM 
InBlock.gif 12{ 
InBlock.gif 13         //每个服务器组件必须继承自类ServicedComponent,ServicedComponent类继承自ContextBoundObject 
InBlock.gif 14         //这样服务就可以绑定到.net环境上 
InBlock.gif 15         // 
InBlock.gif 16        [EventTrackingEnabled( true)] //允许监视,组件对象浏览器可以看到。默认关闭,因为会降低性能 
InBlock.gif 17        [Description( "Coded By Frank Xu Lei For EnterpriseServices Transaction Test")] //组件对象浏览器显示的描述信息 
InBlock.gif 18         //[JustInTimeActivation(false)]//打开或者关闭JIT启动 
InBlock.gif 19        [ObjectPooling( true, 1, 10)] //是否启用对象池,如果对象创建消耗更多的资源,可以考虑对象池 
InBlock.gif 20        [Transaction(TransactionOption.Supported)] //是否需要支持事务特性 
InBlock.gif 21 
InBlock.gif 22         public  class EnterpriseServicesCOMPlusTransaction : ServicedComponent 
InBlock.gif 23        { 
InBlock.gif 24                 //构造函数 
InBlock.gif 25                 public EnterpriseServicesCOMPlusTransaction() 
InBlock.gif 26                { 
InBlock.gif 27 
InBlock.gif 28                } 
InBlock.gif 29                 //新增用户user信息,姓名长度表name和address的长度都是10字符,user2的长度都是100字符,此处调用Ado.net据持久层方法 
InBlock.gif 30                 //[AutoComplete]//设置方法属性,自动更新方法调用的结果 
InBlock.gif 31                 public  bool AddUser( string name,  string address) 
InBlock.gif 32                { 
InBlock.gif 33                         //调用另外一个COM组件的方法,主要是测试事务的完整性 
InBlock.gif 34                        SqlConnection Conn; 
InBlock.gif 35                         try 
InBlock.gif 36                        { 
InBlock.gif 37                                 //user,第1次执行的插入操作,字段长度限制10个字符 
InBlock.gif 38                                 //使用DataSet向导创建方法操作数据库, 
InBlock.gif 39                                EnterpriseServicesCOM.DatabaseDataSetTableAdapters.userTableAdapter _userTableAdapter =  new EnterpriseServicesCOM.DatabaseDataSetTableAdapters.userTableAdapter(); 
InBlock.gif 40                                _userTableAdapter.Insert(name, address); 
InBlock.gif 41                                /** /////user2,第2次执行的插入操作,字段长度限制100个字符 
InBlock.gif 42                                EnterpriseServicesCOM.DatabaseDataSetTableAdapters.user2TableAdapter _user2TableAdapter =  new EnterpriseServicesCOM.DatabaseDataSetTableAdapters.user2TableAdapter(); 
InBlock.gif 43                                _user2TableAdapter.Insert(name, address); 
InBlock.gif 44                                 //也可以自己写代码实现数据库的操作 
InBlock.gif 45 
InBlock.gif 46                                 //SqlConnection Conn = new SqlConnection("Data Source=|DataDirectory|Database.sdf"); 
InBlock.gif 47                                 //Conn.Open(); 
InBlock.gif 48                                /** ///// //user,第1次执行的插入操作,字段长度限制10个字符 
InBlock.gif 49                                 //SqlCommand sqlCommand = new SqlCommand("INSERT INTO [user] (name, address) VALUES (" + name + "," + address + ")"); 
InBlock.gif 50                                 //sqlCommand.ExecuteNonQuery(); 
InBlock.gif 51                                /** /////user2,第2次执行的插入操作,字段长度限制100个字符 
InBlock.gif 52                                 //sqlCommand.CommandText = "INSERT INTO [user2] (name, address) VALUES (" + name + "," + address + ")"; 
InBlock.gif 53                                 //sqlCommand.ExecuteNonQuery(); 
InBlock.gif 54 
InBlock.gif 55                                ContextUtil.SetComplete(); //事务成功 
InBlock.gif 56                                 //Conn.Close(); 
InBlock.gif 57                                 return  true
InBlock.gif 58                        } 
InBlock.gif 59                         catch (Exception e) 
InBlock.gif 60                        { 
InBlock.gif 61                                ContextUtil.SetAbort(); //失败,事务回滚,终止 
InBlock.gif 62                                 return  false
InBlock.gif 63                                 throw e; 
InBlock.gif 64                                 
InBlock.gif 65                        } 
InBlock.gif 66                         finally 
InBlock.gif 67                        { 
InBlock.gif 68                                 //if (Conn != null) 
InBlock.gif 69                                 //{ 
InBlock.gif 70                                 //        Conn.Dispose();//释放资源 
InBlock.gif 71                                 //        Conn = null; 
InBlock.gif 72                                 //} 
InBlock.gif 73                        } 
InBlock.gif 74                } 
InBlock.gif 75                 //更新数据库的2个表,此处可以调用数据持久层方法 
InBlock.gif 76                 //[AutoComplete]//设置方法属性,自动更新方法调用的结果 
InBlock.gif 77                 public  bool UpdateUser( string name,  string address,  int id) 
InBlock.gif 78                { 
InBlock.gif 79                         //调用另外一个COM组件的方法,主要是测试事务的完整性 
InBlock.gif 80                        SqlConnection Conn; 
InBlock.gif 81                         try 
InBlock.gif 82                        { 
InBlock.gif 83                                 //使用DataSet向导创建方法操作数据库, 
InBlock.gif 84                                EnterpriseServicesCOM.DatabaseDataSetTableAdapters.userTableAdapter _userTableAdapter =  new EnterpriseServicesCOM.DatabaseDataSetTableAdapters.userTableAdapter(); 
InBlock.gif 85                                _userTableAdapter.Update(name,address,id); 
InBlock.gif 86 
InBlock.gif 87                                EnterpriseServicesCOM.DatabaseDataSetTableAdapters.user2TableAdapter _user2TableAdapter =  new EnterpriseServicesCOM.DatabaseDataSetTableAdapters.user2TableAdapter(); 
InBlock.gif 88                                _user2TableAdapter.Update(name, address, id); 
InBlock.gif 89                                 //也可以自己写代码实现数据库的操作 
InBlock.gif 90                                 //SqlConnection Conn = new SqlConnection("Data Source=|DataDirectory|Database.sdf"); 
InBlock.gif 91                                 //Conn.Open(); 
InBlock.gif 92                                /** ///// 
InBlock.gif 93                                 //SqlCommand sqlCommand = new SqlCommand("UPDATE [user] SET name = " + name + ", address =" + address + "WHERE (id =" + id + ")"); 
InBlock.gif 94                                 //sqlCommand.ExecuteNonQuery(); 
InBlock.gif 95 
InBlock.gif 96                                 //sqlCommand.CommandText = "UPDATE [user2] SET name = " + name + ", address =" + address + "WHERE (id =" + id + ")"; 
InBlock.gif 97                                 //sqlCommand.ExecuteNonQuery(); 
InBlock.gif 98 
InBlock.gif 99                                ContextUtil.SetComplete(); //事务成功 
InBlock.gif100                                 return  true
InBlock.gif101                                 //Conn.Close(); 
InBlock.gif102                        } 
InBlock.gif103                         catch (Exception e) 
InBlock.gif104                        { 
InBlock.gif105                                ContextUtil.SetAbort(); //失败,事务回滚,终止 
InBlock.gif106                                 return  false
InBlock.gif107                                 throw e; 
InBlock.gif108                        } 
InBlock.gif109                         finally 
InBlock.gif110                        { 
InBlock.gif111                                 //if (Conn != null) 
InBlock.gif112                                 //{    
InBlock.gif113                                 //        Conn.Dispose();//释放资源 
InBlock.gif114                                 //        Conn = null; 
InBlock.gif115                                 //} 
InBlock.gif116                        } 
InBlock.gif117                } 
InBlock.gif118        } 
InBlock.gif119}
     [assembly: ApplicationName("FrankEnterpriseServicesTest")],在组件服务浏览器里的名称
     [assembly: Description("Coded By Frank Xu Lei 2/11/2009")],在组件服务浏览器里的描述信息
     [assembly: ApplicationActivation(ActivationOption.Server)],启动类型Library在创建进程里激活,运行在客户进程,Server类型系统进程激活.
     [assembly: ApplicationAccessControl(false)],访问设置,关闭后,用户都可以访问.
    每个服务器组件必须继承自类ServicedComponent,ServicedComponent类继承自ContextBoundObject,这样服务就可以绑定到.net remoting上下文环境上.
    [EventTrackingEnabled(true)],允许监视,组件对象浏览器可以看到。默认关闭,因为会降低性能.
    [Description("Coded By Frank Xu Lei For EnterpriseServices  Test")],组件对象浏览器显示的描述信息.
    //[JustInTimeActivation(false)],打开或者关闭JIT启动.
    [ObjectPooling(true,1,10)],是否启用对象池,如果对象创建消耗更多的资源,可以考虑对象池.
    [Transaction(TransactionOption.Required)].是否需要支持事务特性,必要时候创建新的事务.
     设置一个 try 块以捕获在数据库处理过程中可能出现的任何异常。您必须捕获这些异常来终止事务。
 
      组件必须有一个强名称。为了防止Dll hell.生成一个强名称,然后使用该强名称对程序集进行签名。步骤如下:
     (1)在 Visual Studio .NET 命令提示符处,键入 sn.exe -k FrankXuKey.snk 以创建一个密钥文件.
     (2)将 FrankXuKey.snk 复制到您的项目文件夹中。可以在项目属性里设置签名文件.
     (3)也可以在在 AssemblyInfo.vc 中,将以下代码行添加到其他程序集属性语句之前或之后: [assembly: AssemblyKeyFileAttribute("..\\..\\FrankXuKey.snk")]  
     (4)保存,重新编译,然后bin目录下生成dll文件。下面我们开始部署
     2.部署服务组件:
      使用regsvcs.exe将Dll注册到COM+ Services里面 ,在Visual studio命令行窗口输入运行regsvcs.可以把生成的DLL文件拷贝到c盘更目录下安装也可以直接输入路径进行安装,格式是:
regsvcs 文件名 .注册的界面如下图2:输入命令,按回车键
图2安装结果
注册成功,regsvcs.exe把dll输入到COM+ Services中。
如果是服务器操作系统,注册完成后可以在组件服务里查看到安装后的组件,如下图3:
图3对象浏览器查看组件
可以看我们的组件服务和实现的接口信息.右键可以查看组件的信息如图4.
图4组件属性
     现在可以看到详细组件属性信息,包括安全\对象池等信息.我们还可以选择导出组件为MSI文件,方便安装.如图5
     导出的安装文件,可以进行组件的快速部署安装.导出文件下载 /Files/frank_xl/FrankXuEnterpriseServicesMSI.rar
    3.简单客户端
     创建简单的控制台程序,方便调试,首先是循环调用组件服务 EnterpriseServicesCOMPlus的SayHello方法.测试组件服务调用是否成功,其次是测试组件事物特性的代码,具体信息如下:
InBlock.gif 1using System; 
InBlock.gif 2using System.Collections.Generic; 
InBlock.gif 3using System.Text; 
InBlock.gif 4using EnterpriseServicesCOM; 
InBlock.gif 5namespace EnterpriseServicesClient 
InBlock.gif 6{ 
InBlock.gif 7         //客户端为控制台程序,方便测试 
InBlock.gif 8         class EnterpriseServicesClient 
InBlock.gif 9        { 
InBlock.gif10                 static  void Main( string[] args) 
InBlock.gif11                { 
InBlock.gif12                         //创建COM对象,测试组件服务的一般调用 
InBlock.gif13                         using (EnterpriseServicesCOM.EnterpriseServicesCOMPlus enterpriseServicesCOM =  new EnterpriseServicesCOMPlus()) 
InBlock.gif14                        { 
InBlock.gif15                                 for ( int i = 0; i < 10; i++) 
InBlock.gif16                                { 
InBlock.gif17 
InBlock.gif18                                         string sName =  "Frank Xu Lei"
InBlock.gif19                                         string sMessage = enterpriseServicesCOM.SayHello(sName); //调用 
InBlock.gif20                                        Console.WriteLine(sMessage); 
InBlock.gif21                                } 
InBlock.gif22                        } 
InBlock.gif23                         //创建COM对象,测试组件服务的事物特性 
InBlock.gif24                         using (EnterpriseServicesCOM.EnterpriseServicesCOMPlusTransaction enterpriseServicesCOMTransaction =  new EnterpriseServicesCOMPlusTransaction()) 
InBlock.gif25                        { 
InBlock.gif26                                 //第一次事务操作成功,字段长度合法 
InBlock.gif27                                 if (enterpriseServicesCOMTransaction.AddUser( "FrankXu""ShangHai")) 
InBlock.gif28                                { 
InBlock.gif29                                        Console.WriteLine( "Transaction1 is successful"); 
InBlock.gif30                                } 
InBlock.gif31                                 else 
InBlock.gif32                                { 
InBlock.gif33                                        Console.WriteLine(Console.WriteLine( "Transaction1 is failed")); 
InBlock.gif34                                } 
InBlock.gif35                                 //第2次事务操作失败 
InBlock.gif36                                 if (enterpriseServicesCOMTransaction.AddUser( "FrankXuLei2009""ShangHai2009")) 
InBlock.gif37                                { 
InBlock.gif38                                        Console.WriteLine( "Transaction1 is successful"); 
InBlock.gif39                                } 
InBlock.gif40                                 else 
InBlock.gif41                                { 
InBlock.gif42                                        Console.WriteLine(Console.WriteLine( "Transaction1 is failed")); 
InBlock.gif43                                } 
InBlock.gif44                                 
InBlock.gif45                        } 
InBlock.gif46                        Console.ReadLine(); 
InBlock.gif47                } 
InBlock.gif48        } 
InBlock.gif49}
 
   测试事物特性的代码分了两种情况,第一次调用新增用户的方法事务成功,字符长度合法.第2次事务则提交失败,字符插入数据库异常.事务回滚.
  四.总结
运行结果如下图:
  
   Transaction1执行成功,Transaction2执行失败. ContextUtil.SetAbort();设置上下文环境的状态信息,事物终止,该语句使所有的数据库操作回滚,数据库未增加新记录.
      今天我们详细学习了.NET Enterprise Services基本知识,以及它和COM+的关系, 熟悉了.NET Enterprise Services的事务特性,详细的了解COM+组件服务的技术细节.希望能给大家一些启发和帮助.欢迎交流.下一节打算写分布式编程的另外一个技术Web Service.会附加一部分WSE的部分,另外延续本文的习惯,我会上穿完整的代码/Files/frank_xl/EnterpriseServices.rar.本文参考了大牛Tim Ewald的文章 COM+ Integration: How .NET Enterprise Services Can Help You Build Distributed Applications.很不错的文章,好象还没人翻译,打算周末抽时间翻译一下一起放出来,与大家分享.有兴趣的朋友可以关注.



 本文转自 frankxulei 51CTO博客,原文链接:http://blog.51cto.com/frankxulei/320396,如需转载请自行联系原作者

相关文章
|
数据库 测试技术 安全
使用Entity Framework和WCF Ria Services开发SilverLight之2:POCO
在上一篇中《使用Entity Framework和WCF Ria Services开发SilverLight之1:简单模型》我们提出这类简单模型的几个问题: 1:实体模型被紧耦合在EDM中,同时它不能项目(模块)使用。
1207 0