WCF分布式开发必备知识系列文章3:Enterpise Services
那么现在我们就开始今天的学习,首先介绍的是:
一.基本概念
1.什么是COM+
COM+的底层结构仍然以COM为基础,它不仅具备了COM的易于用户定制\可重用\简化开发的优点,同时又避免了COM实现方面的一些不足.把COM、DCOM和MTS的编程模型结合起来,它通过操作系统的各种支持,使组件对象模型建立在应用层上,继承了它们的绝大多数特性,增加了新的功能。 COM+的几个主要特性: 比如队列服务、负载平衡、内存数据库、事件服务等。它更加注重于分布式网络应用的设计和实现(参考msdn)。
2.什么是Enterpise Services
Enterpise Services是微软应用程序服务器技术的别称..Net Enterpise Services提供了可以在.Net 组件中使用的COM+服务.因为它也是基于以前的COM+技术,在.NET平台上开发.NET组件,使用Enterpise Services可以将.NET组件并进行封装为COM对象,这样.NET组件就可以使用COM+服务了..NET做为新一代的开发平台,实现了COM组件相互之间的调用,继承了向前兼容的优良传统.同样COM+可以调用.Net 组件,会给COM+服务带来一些扩展.
二.Enterpise Services中重要的类及特性
Enterpise Servicesg工作在三层架构中的业务逻辑层或者数据访问层.由于基于COM+技术,所以它也可以通过Micro Application Center Server实现负载均衡.下面我们来介绍一下Enterpise Services里几个重要的概念.
(1)上下文(context又翻译为环境):所有的COM+服务都是通过上下文(context又翻译为环境)实现的,上下文是进程里提供给对象运行时服务的空间.如果一个对象调用另外一个上下文里的对象,这个方法调用会被代理截取,COM运行时就可以利用代理来预处理或者迟处理请求并执行相关的服务代码.过程如图1
图1COM+服务与上下文
(2)自动事务处理(automatic transaction):
Enterpise Services里最常用的特性就是自动事务处理,这个一般在类的星星[Transaction]使用这个特性,就不需要再编写复杂的代码来显示执行事务准备工作,如对象的传递等,设置这个属性后,上下文环境会在后台进行设置.具体的设置在TransactionOption类的属性里选择.Required表示共享一个事务,必要的时候创建一个新事务.
(3)对象池(Obejct pooling):
这个属性可以设置是需要对象池和对象池的大小.主要是考虑到对象的创建和销毁会占用更多的资源.使用对象池会事先创建对象,客户的请求到来就直接到对象池里查找需要的对象,直接响应请求,可以提高服务的性能,节约时间.
(4)ServicedComponent:
是所有使用COM+服务类的基类.ServicedComponent继承自ContextBoundObject类.ContextBoundObject继承自MarshalByRefObject.这个类在上一节的.Net Remoting介绍过,使支持remoting的程序可以跨程序域边界的访问对象.
另外还有分布式事务处理的等概念,Enterpise Services里是使用DTC来实现的.
三.Enterpise Services实现COM+事务的编码\部署\测试过程
上面我们熟悉了Enterpise Services的特性和一些重要的概念,下面我们就来利用Enterpise Services技术实现里COM+事务处理.在分布式应用程序中,我们往往需要同时操作多个数据库,使用数据库本身的事务处理,很难满足程序对事务控制的要求。
大家都知道事务具有ACID的特性:Aotomicity原子性\Consistency一致性\Isolation独立性\Durability永久性.
原子性就是一组数据库操作被视为一个单元。要么所有的操作都成功;如果其中一个操作失败,则整个事务失败。事物失败,不会提交更改。
具体的实现过程如下:
1.编写Enterpise Services COM+事务服务组件:
首先我们使用Visual Studio 创建一个类库项目,添加程序集System.EnterpriseServices的引用.添加类EnterpriseServicesCOMPlus继承自ServicedComponent,确保可以使用COM+服务.命名空间外设置程序集属性.具体代码
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Data;
5using System.Data.SqlClient;
6using System.EnterpriseServices;
7[assembly: ApplicationName("FrankEnterpriseServicesTransactionTest")]//在组件服务浏览器里的名称
8[assembly: Description("Coded By Frank Xu Lei 2/14/2009")]//在组件服务浏览器里的描述信息
9[assembly: ApplicationActivation(ActivationOption.Server)]//启动类型Library在创建进程里激活,运行在客户进程,Server类型系统进程激活,
10[assembly: ApplicationAccessControl(false)]//访问设置,关闭后,用户都可以访问
11namespace EnterpriseServicesCOM
12{
13 //每个服务器组件必须继承自类ServicedComponent,ServicedComponent类继承自ContextBoundObject
14 //这样服务就可以绑定到.net环境上
15 //
16 [EventTrackingEnabled(true)]//允许监视,组件对象浏览器可以看到。默认关闭,因为会降低性能
17 [Description("Coded By Frank Xu Lei For EnterpriseServices Transaction Test")]//组件对象浏览器显示的描述信息
18 //[JustInTimeActivation(false)]//打开或者关闭JIT启动
19 [ObjectPooling(true, 1, 10)]//是否启用对象池,如果对象创建消耗更多的资源,可以考虑对象池
20 [Transaction(TransactionOption.Supported)]//是否需要支持事务特性
21
22 public class EnterpriseServicesCOMPlusTransaction : ServicedComponent
23 {
24 //构造函数
25 public EnterpriseServicesCOMPlusTransaction()
26 {
27
28 }
29 //新增用户user信息,姓名长度表name和address的长度都是10字符,user2的长度都是100字符,此处调用Ado.net据持久层方法
30 //[AutoComplete]//设置方法属性,自动更新方法调用的结果
31 public bool AddUser(string name, string address)
32 {
33 //调用另外一个COM组件的方法,主要是测试事务的完整性
34 SqlConnection Conn;
35 try
36 {
37 //user,第1次执行的插入操作,字段长度限制10个字符
38 //使用DataSet向导创建方法操作数据库,
39 EnterpriseServicesCOM.DatabaseDataSetTableAdapters.userTableAdapter _userTableAdapter = new EnterpriseServicesCOM.DatabaseDataSetTableAdapters.userTableAdapter();
40 _userTableAdapter.Insert(name, address);
41 /**/////user2,第2次执行的插入操作,字段长度限制100个字符
42 EnterpriseServicesCOM.DatabaseDataSetTableAdapters.user2TableAdapter _user2TableAdapter = new EnterpriseServicesCOM.DatabaseDataSetTableAdapters.user2TableAdapter();
43 _user2TableAdapter.Insert(name, address);
44 //也可以自己写代码实现数据库的操作
45
46 //SqlConnection Conn = new SqlConnection("Data Source=|DataDirectory|Database.sdf");
47 //Conn.Open();
48 /**///// //user,第1次执行的插入操作,字段长度限制10个字符
49 //SqlCommand sqlCommand = new SqlCommand("INSERT INTO [user] (name, address) VALUES (" + name + "," + address + ")");
50 //sqlCommand.ExecuteNonQuery();
51 /**/////user2,第2次执行的插入操作,字段长度限制100个字符
52 //sqlCommand.CommandText = "INSERT INTO [user2] (name, address) VALUES (" + name + "," + address + ")";
53 //sqlCommand.ExecuteNonQuery();
54
55 ContextUtil.SetComplete();//事务成功
56 //Conn.Close();
57 return true;
58 }
59 catch (Exception e)
60 {
61 ContextUtil.SetAbort();//失败,事务回滚,终止
62 return false;
63 throw e;
64
65 }
66 finally
67 {
68 //if (Conn != null)
69 //{
70 // Conn.Dispose();//释放资源
71 // Conn = null;
72 //}
73 }
74 }
75 //更新数据库的2个表,此处可以调用数据持久层方法
76 //[AutoComplete]//设置方法属性,自动更新方法调用的结果
77 public bool UpdateUser(string name, string address, int id)
78 {
79 //调用另外一个COM组件的方法,主要是测试事务的完整性
80 SqlConnection Conn;
81 try
82 {
83 //使用DataSet向导创建方法操作数据库,
84 EnterpriseServicesCOM.DatabaseDataSetTableAdapters.userTableAdapter _userTableAdapter = new EnterpriseServicesCOM.DatabaseDataSetTableAdapters.userTableAdapter();
85 _userTableAdapter.Update(name,address,id);
86
87 EnterpriseServicesCOM.DatabaseDataSetTableAdapters.user2TableAdapter _user2TableAdapter = new EnterpriseServicesCOM.DatabaseDataSetTableAdapters.user2TableAdapter();
88 _user2TableAdapter.Update(name, address, id);
89 //也可以自己写代码实现数据库的操作
90 //SqlConnection Conn = new SqlConnection("Data Source=|DataDirectory|Database.sdf");
91 //Conn.Open();
92 /**/////
93 //SqlCommand sqlCommand = new SqlCommand("UPDATE [user] SET name = " + name + ", address =" + address + "WHERE (id =" + id + ")");
94 //sqlCommand.ExecuteNonQuery();
95
96 //sqlCommand.CommandText = "UPDATE [user2] SET name = " + name + ", address =" + address + "WHERE (id =" + id + ")";
97 //sqlCommand.ExecuteNonQuery();
98
99 ContextUtil.SetComplete();//事务成功
100 return true;
101 //Conn.Close();
102 }
103 catch (Exception e)
104 {
105 ContextUtil.SetAbort();//失败,事务回滚,终止
106 return false;
107 throw e;
108 }
109 finally
110 {
111 //if (Conn != null)
112 //{
113 // Conn.Dispose();//释放资源
114 // Conn = null;
115 //}
116 }
117 }
118 }
119}