在petshop3.0 中实现了分布式事务处理,定单的处理在不同的数据库,作为分布式事务,跨多个资源结合不同的操作,我们用自动事务来处理, .NET Framework 依靠 MTS/COM+ 服务来支持自动事务。COM+ 使用 Microsoft Distributed Transaction Coordinator (DTC) 作为事务管理器和事务协调器在分布式环境中运行事务。通过提供基于声明性事务的编程模型,COM+ 使应用程序可以很容易地运行跨不同种类的资源的事务。这种做法的缺点是,由于存在 DTC 和 COM 互操作性开销,导致性能降低,而且不支持嵌套事务。首先应了解COM+中提供的池化对象, 对象池是 COM+ 提供的一种自动服务,通过它您能够配置一个组件使其实例在池中保持活动,准备被请求组件的任何客户端所使用。您可以管理性地配置为给定组件所维护的池,指定各种特性,如池的大小和创建请求超时值,然后还可以对池进行监视。一旦应用程序运行起来,COM+ 将为您管理池,根据您已经指定的条件处理对象激活和重用的细节。
通过这种方式重用对象您可以获得显著的性能、可伸缩性和管理上的优势,尤其在对象编写时就考虑到了充分利用重用的情况下。凭借对象池您可以:
• |
通过将比较耗时的初始化和资源获取从对象为客户端执行的实际操作中提取出来,减少每个客户端使用对象的时间。 |
• |
让所有客户端分摊获取昂贵资源的成本。 |
• |
在应用程序启动时,在任何客户端请求(如数据库连接、信号量、代理等等)到来之前预先分配对象。 |
• |
使用管理性的池管理措施管理资源的使用情况 — 例如,通过设置合适的最大池等级,能打开的数据库连接的数量应该与所拥有的许可证数量相同。 |
• |
管理性地配置池以最充分地利用可用的硬件资源(池的配置可以随着可用硬件资源的改变而改变)。 |
• |
减少使用实时 (JIT) 激活的对象重新激活的时间,同时小心地控制资源专用于客户端的方式。 |
对象池与连接池
对象池与连接池不同。连接池使应用程序能够使用来自连接池的数据库连接,连接池无需每次使用时再重新建立。当连接在池中创建和放置之后,应用程序可以重用该连接,无需再执行完整的连接过程。这一功能在开放数据库连接 (ODBC) 和 Microsoft 数据访问组件 (MDAC) 2.0 中的 OLEDB 里都可使用。可以将它看成是与持久资源的非持久连接。
而另一方面,对象池允许一个应用程序在无需花费对象初始化成本的情况下使用来自池中的对象。它还允许请求在超时的情况下优雅地失败。如果池化连接超时,会返回一个错误给调用者。但是在池化对象的情况下,对象可以再次尝试,也可以选择返回错误给调用者。
图 2 显示了池化连接的场景。在这里,业务对象使用 ODBC 或者 OLEDB 提供了一个数据库连接池。
图 2. 连接池场景
图 3 说明了对象池场景。这里业务对象调用了一个拥有数据对象池的 COM+ 服务器,每个数据对象都有与数据库的连接。
图 3. 对象池场景
对象池和连接池还有如下区别:
• |
速度。池化数据对象速度经证明大约是使用连接池的两倍。 |
• |
成本增加。对象池并不增加成本。想像一下这样的情景,一个服务器对象与两个不同的数据库连接。如果使用连接池,每个数据库将必须都有一个单独的池。如果使用对象池,则每个对象将与每个数据库都有连接,因此无需额外的池。 |
• |
事务。连接池中的连接总是要在事务中登记,因此使用这些连接的对象都不用在事务中显式地登记。而另一方面池化对象则必须显式地在事务中登记。 |
下面这是PETSHOP3.0中的事务处理类,insert.
要参与自动事务,.NET 类必须是从 System.EnterpriseServices.ServicedComponent 类继承的,这可使得该 .NET 类能够在 COM+ 内运行。在这个过程中,要将 COM+ 与 DTC 进行交互以创建一个分布式事务,也要登记后台的所有资源连接。您还需要对该类设置声明性事务属性以确定其事务性行为。
[Transaction(System.EnterpriseServices.TransactionOption.Required)]
ClassInterfaceType.AutoDispatch,该方式下只生成dispatch接口,如果是编译型语言,那么我们可以让编译器在编译的时候装载类型库,也就是装载接口的描述。装载了类型库后,编译器就知道应该如何编译接口函数的调用了---这叫“前绑定”。但是,如果想在脚本语言中使用组件,问题就大了,因为脚本语言是解释执行的,它执行的时候不会知道具体的函数地址,怎么办?自动化接口就为此诞生了---“后绑定”。
自动化组件,其实就是实现了 IDispatch 接口的组件。
[ClassInterface(ClassInterfaceType.AutoDispatch)]
ObjectPooling 类指定了其最小的池大小为 4,而最大的池大小为 4
[ObjectPooling(MinPoolSize=4, MaxPoolSize=4)]
在接口名字之前,每个接口需要一个GUID特性。要生成变个唯一的Guid,需要运行guidgen.exe工具软件
[Guid("14E3573D-78C8-4220-9649-BA490DB7B78D")]
public class OrderInsert : ServicedComponent {
private const string ACID_USER_ID = "ACID";
private const string ACID_ERROR_MSG = "ACID test exception thrown for distributed transaction!";
protected override bool CanBePooled() {
return true;
}
//<AutoComplete> 实现提交事务,终止事务
[AutoComplete]
public int Insert(OrderInfo order) {
IOrder dal = PetShop.DALFactory.Order.Create();
int orderId = dal.Insert(order);
Inventory inventory = new Inventory();
inventory.TakeStock( order.LineItems);
throw new ApplicationException(ACID_ERROR_MSG);
return orderId;
}
最后为了使COM+对象被外部调用我们必须使用强名称,SN.EXE生成私匙.