博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[转]利用EnteLib Unity Interception Extension和PIAB实现Trans
阅读量:6863 次
发布时间:2019-06-26

本文共 16583 字,大约阅读时间需要 55 分钟。

转【】

一.写作前提

之前在苏州的一家知名软件企业工作时,使用了他们提供的框架和类库,切实的感受到它 们所带来的便利,它不仅提高了软件的开发速度,减少了代码的冗余,更重要的是提高了企 业产品的开发效率及质量。而今换了工作环境(一家国外小软件公司),在缺少了这些有利 的工具之后,发现公司之前的几乎所有项目都在重复的Copy代码,这不仅仅是延长项目的开 发周期,最麻烦的莫过于对项目的管理借来及大的困难,看了让我心里有些不是滋味。之后 ,我就开始尝试着写些高效、集成的代码(已经写了一部分了),我希望能够和大家分享和 交流,也希望得到一些指正和建议。

本篇我们所要讨论的问题就是如何使用Enterprise Library的Unity Interception Extension  和Policy Injection Application Block(PIAB)实现以Attribute形式注入的 Transaction Call Handler。关于什么是事务,这里就不在多加叙述,我们把更多的篇幅用 来本文的主题进行讲解。

二.将Transaction与业务逻辑分离

Enterprise Library 是微软推出的一个Open Source框架,或者说是一个可扩展的类库, 最新Release版本是4.1,目录已经出了5.0人的Bate版本。Enterprise Library是由多个 Application Block组成的,例如:

Caching Application Block Data Access Application Block Exception Handling Application Block Logging Application Block Unity Application Block Validation Application Block Policy Injection Application Block

Policy Injection Application Block使得开发人员可以使用拦截策略执行一些比较 Common及非业务逻辑的特性,例如:Logging、Caching、Exception、Validation以及其他的 控制与操作,在PIAB中,它们以注入的形式提供给开发者,为用户的开发带来了及大的方便 ,所以我考虑以同样的形式来写了一个injection的Transaction Call Handler,它用来在对 实际方法调用之前进行拦截,然后开启Transaction,调用实际目标方法,最好实现提交 Transaction,并在Transaction中进行相应的扩展操作。

OK,首先我们来看看传统的事务代码:

图1:  Traditional Transaction Code

 

public void AddUser(DataSetUser ds){     SqlConnection connection = new SqlConnection();     connection.ConnectionString =  ConfigurationManager.ConnectionStrings ["TransactionDemoConnectionString"].ConnectionString;     connection.Open();     SqlTransaction transaction = connection.BeginTransaction();     try     {         SqlCommand command = connection.CreateCommand();         command.Transaction = transaction;         command.CommandType = CommandType.Text;         int i = 0;         foreach (DataSetUser.T_USERRow row in ds.T_USER.Rows)         {             //if (i == 1)             //    throw new Exception("Test");             command.CommandText = "insert into T_USER([Name], [Password]) values(@p_username,@p_password)";             command.Parameters.Clear();             command.Parameters.Add(new SqlParameter (@"@p_username", row.Name));             command.Parameters.Add(new SqlParameter (@"@p_password", row.Password));             command.ExecuteNonQuery();             i++;         }         transaction.Commit();     }     catch (Exception ex)     {         transaction.Rollback();     }     finally     {         if (connection != null)             connection.Close();         connection.Dispose();     }}

 

读过上面的代码,其实我们要做的是对DB的某些数据做增加、更新或删除操作,我们可以 把这些操作称为对数据库数据操作的业务逻辑,而Transaction和对Database连接以及其它的 相关操作(对DB的操作以后有空会写一个,在这里的代码我们作为示例,直接写在代码里) ,在这里它们是与对这些data操作无关的非业务逻辑,因为他不涉及到对具体data的操作; 另外,往往在一个项目中对Transaction要求的代码是非常之多的,我们总不能把相同的代码 在不同的method中Copy来Copy去,因为这样会存在大量的代码冗余,并且不能保证代码的正 确性,增加代理的管理难度和可读性。所以我考虑如下:

1.能否简化Transaction的代码,方便的操作、减少冗余;

2.能否将Transaction从这些对数据操作中分离出来;

3.能否对Transaction进行扩展,比如说通过Transaction在开启、执行、提交或回滚的 过程中记录所进行的操作或一些异常等。

下面就来看看我提出的一些解决方案。

三.创建Transaction Call Handler

下面我们就来具体的创建这样的Call Handler以及实现对其操作。

1)创建Solution

先创建一个Solution文件,然后在这个项目文件中包含两个Project,一个是Transaction Call Handler Attribute实现的类库,我们这里叫CWS.Framework.TransactionCallHandler ,它实现了把Transaction分离出来后所以做的所有工作,包括对其进行的一个日志操作;另 外一个Project文件是用来测试的Web Application,我们这里叫WebTest,他实现了利用 Policy Injection Application Block下的PolicyInjection实现对Transaction的注入。

CWS.Framework.TransactionCallHandler Project需要引用如下dll:

图2:  Call Handler Project需要引用的dll

Microsoft.Practices.EnterpriseLibrary.Common

Microsoft.Practices.EnterpriseLibrary.Logging

Microsoft.Practices.ObjectBuilder2

Microsoft.Practices.Unity

Microsoft.Practices.Unity.Interception

WebTest Project需要引用如下dll:

图3:  WebTest Project需要引用的dll

CWS.Framework.TransactionCallHandler

Microsoft.Practices.EnterpriseLibrary.PolicyInjection

Microsoft.Practices.Unity.Interception

 

2)CWS.Framework.TransactionCallHandler的实现

我们要实现Transaction的Call Handler需要用到 Microsoft.Practices.Unity.InterceptionExtension. HandlerAttribute抽象类,它继承于 System.Attribute,是自定义HandlerAttribute的基类;以及 Microsoft.Practices.Unity.InterceptionExtension. ICallHandler 接口。每一个Call Handler的实现都需要继承所需要应用的对象上,并且实现ICallHandler 接口。下面提供了 HandlerAttribute及ICallHandler的原型。

图4: 

Microsoft.Practices.Unity.InterceptionExtension. HandlerAttribute abstract class

using Microsoft.Practices.Unity;using System;namespace Microsoft.Practices.Unity.InterceptionExtension{     public abstract class HandlerAttribute : Attribute     {         protected HandlerAttribute();         public int Order { get; set; }         public abstract ICallHandler CreateHandler(IUnityContainer  container);     }}
using System;namespace Microsoft.Practices.Unity.InterceptionExtension{     public interface ICallHandler     {         int Order { get; set; }         IMethodReturn Invoke(IMethodInvocation input,  GetNextHandlerDelegate getNext);     }}

 

首先需要对ICallHandler接口中的所有成员Method及成员Property进行实现,另外还需要 定义一些Method及Property来实现从原先Code中分离出来的Transaction功能,以及对分离出 来的Transaction进行扩展的一些功能(如Log记录,Exception捕获等),具体实现如图6所示 。

图6:  自定义的Transaction处理类

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Transactions;using Microsoft.Practices.EnterpriseLibrary.Logging;using Microsoft.Practices.Unity.InterceptionExtension;namespace CWS.Framework.CallHandler.Transaction{     public class TransactionCallHandler : ICallHandler     {         private TransactionScopeOption _scopeOption =  TransactionScopeOption.Required;         private TransactionOptions _transactionOptions;         private EnterpriseServicesInteropOption _interopOption =  EnterpriseServicesInteropOption.None;         private string _functionName;         private string _actionDescription;         public TransactionCallHandler(string functionName, string  actionDescription, TransactionOptions transactionOptions)         {             this._transactionOptions = transactionOptions;             this._functionName = functionName;             this._actionDescription = actionDescription;         }         public TransactionCallHandler(TransactionScopeOption  scopeOption, TransactionOptions transactionOptions             , EnterpriseServicesInteropOption interopOption)         {             this._scopeOption = scopeOption;             this._transactionOptions = transactionOptions;             this._interopOption = interopOption;         }         public int Order { get; set; } //用来控制执行顺序          public IMethodReturn Invoke(IMethodInvocation input,  GetNextHandlerDelegate getNext)         {             IMethodReturn result;             using (TransactionScope scope =  CreateTransactionScope())             {                 Logger.Write(string.Format("Function Name{0},  Action Description:{1}.", _functionName, _actionDescription));                 result = getNext()(input, getNext);                 if (result.Exception == null)                 {                     Logger.Write("Action Done.");                     scope.Complete();                 }                 else                     Logger.Write(result.Exception);             }             return result;         }         protected virtual TransactionScope CreateTransactionScope ()         {             if (_interopOption ==  EnterpriseServicesInteropOption.None)             {                 return new TransactionScope(_scopeOption,  _transactionOptions);             }             else             {                 return new TransactionScope(_scopeOption,  _transactionOptions, _interopOption);             }         }     }}

 

我自定义了如下属性:

1.TransactionScopeOption  _scopeOption:它是一个枚举类型,它定义了事务行为的 范围,我们在创建Transaction instance Object 的时候需要用到它。它具有三个值选项, 如下所示,默认值是Required:

Required: 加入环境事务,或者在环境事务不存在时创建一个新的环境事务;

RequiresNew:开始一个新的Transaction,并使该Transaction成为自己范围内的新环境 事务;

Suppress:结果就是没有任何环境事务。

2.TransactionOptions _transactionOptions:它可以用来设置Transaction的超时时间 及Transaction的隔离级别,这里我们需要介绍一下Transaction的隔离级别IsolationLevel ,Transaction的隔离级别确定在该Transaction完成之前,其他Transaction对可变数据所拥 有的访问级别;

3.EnterpriseServicesInteropOption _interopOption:在Transaction创建的时候可以 用它指定与 COM+ 交互的方式;

4.protected virtual TransactionScope CreateTransactionScope():这个方法主要的 作用是根据对上面三个属性的设置来创建我们所需要的TransactionScope,它可以自动选择 和管理环境事务,由于他的简单、易用和高效, 它Microsoft推荐的事务处理类。

上面介绍完了创建事务的方法及所需参数的说明,下面我们看看对ICallHandler的方法是 如何实现的。ICallHandler中有一个成员变量Order和唯一的成员方法Invoke,Order是用来 指示Call Handler执行的顺序,这里我们保留没有使用。继承ICallHandler接口的类规定将 会自动执行Invoke方法,Invoke的参数input代表对调用类的实例,而参数getNext是Call Handler中对实现对象调用的委托(Delegate)。我们对Invoke实现如下内容:

1.使用using创建Transaction实例,并定义了他的使用范围(或者说使用环境),即整 个using的有效区域;

2.使用Enterprise Library 的Logger实现对操作方法的记录(具体如何配置和实现这里 暂不叙述,如果有时间下次再写一个关于Logger的内容);

3.通过在Invoke方法中调用图7的语句实现对目标调用,值得我们注意的是,对他的调用 是在整个Transaction的有效作用范围内实现的,如果对目标对象的调用失败或目标对象执行 出现异常,那我们就不应该调用Invoke中事务范围的Complete方法,那么整个事务就将自动 进行回滚,反之亦然。

图7: Invoke中对目标方法的调用

result = getNext()(input, getNext);

4.Exception的捕获,我们通过对图6中执行结果进行判断,查看是否是存在Exception, 如果存在Exception的相关信息,就将其记录下来(我们也可以把对Exception的操作从此示 例中分离出来,这里只是为了更加形象的表示我们可以在Call Handler中做更多的操作)。

上面只是定义了一个类TransactionCallHandler,它对ICallHandler interface进行实现 ,但是它依然没有被任何方法所调用,同时我们也没有实现Transaction的Attribute。Ok, 现在我们就来实现Attribute的操作处理的Class,并且在这个Class对 Microsoft.Practices.Unity.InterceptionExtension namespace下抽象类 HandlerAttribute的抽象方法CreateHandler的实现过程中对我们自定义Class TransactionCallHandler进行调用。具体代码如图8所示。

图8: Transaction Attribute的实现

 

using System;using System.ComponentModel;using System.Transactions;using Microsoft.Practices.Unity;using Microsoft.Practices.Unity.InterceptionExtension;namespace CWS.Framework.CallHandler.Transaction{     [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method |  AttributeTargets.Property)]     public class TransactionCallHandlerAttribute :  HandlerAttribute     {         private TransactionScopeOption _scopeOption;         private TimeSpan _timeout = TimeSpan.FromMinutes(1);         private IsolationLevel _isolationLevel =  IsolationLevel.ReadCommitted;         private TransactionOptions _transactionOptions;         private EnterpriseServicesInteropOption  _interopOption;         private string _functionName;         private string _actionDescription;         public TransactionCallHandlerAttribute()         {         }         public TransactionCallHandlerAttribute(TransactionScopeOption  scopeOption)         {             this._scopeOption = scopeOption;         }         public TransactionCallHandlerAttribute(IsolationLevel  isolationLevel)         {             this._isolationLevel = isolationLevel;         }         public TransactionCallHandlerAttribute(TransactionScopeOption  scopeOption, IsolationLevel isolationLevel)         {             this._scopeOption = scopeOption;             this._isolationLevel = isolationLevel;         }         public TransactionCallHandlerAttribute(string functionName)              : this(functionName, string.Empty)         {         }         public TransactionCallHandlerAttribute(string functionName,  string actionDescription)         {             this._functionName = functionName;             this._actionDescription = actionDescription;         }         public string FunctionName         {             get { return this._functionName; }             set { this._functionName = value; }         }         public string ActionDescription         {             get { return this._actionDescription; }             set { this._actionDescription = value; }         }         public TransactionScopeOption ScopeOption         {             get { return _scopeOption; }             set { _scopeOption = value; }         }         public IsolationLevel IsolationLevel         {             get { return _isolationLevel; }             set { _isolationLevel = value; }         }         public EnterpriseServicesInteropOption InteropOption         {             get { return _interopOption; }             set { _interopOption = value; }         }         public TransactionOptions TransactionOptions         {             get             {                 if (_transactionOptions == null)                     _transactionOptions = new  TransactionOptions();                 _transactionOptions.Timeout = _timeout;                 _transactionOptions.IsolationLevel =  _isolationLevel;                 return _transactionOptions;             }         }         public override ICallHandler CreateHandler(IUnityContainer  container)         {             _transactionOptions = TransactionOptions;             if (!string.IsNullOrEmpty(this._functionName))             {                 return new TransactionCallHandler (_functionName, _actionDescription, _transactionOptions);             }             else             {                 return new TransactionCallHandler (ScopeOption, TransactionOptions, InteropOption);             }         }     }}

 

可以看到我们定义了一个TransactionCallHandlerAttribute,它继承了 HandlerAttribute,我们必需实现HandlerAttribute抽象类中的抽象方法CreateHandler,当 我们对Attribute进行引用的时候,他将会自动调用CreateHandler方法来创建Call Handler 对象,我们这里使用的Call Handler对象就是上面定义的TransactionCallHandler,在 TransactionCallHandler里我们知道他在创建TransactionScope对象的时候需要一些参数, 所我们创建了一些属性用于在引用Transaction Attribute 的使用符合项目需求的Custom Parameters。另外我们还提供几个构造函数,同样可以传递相应的Custom Parameters。

注:在TransactionCallHandlerAttribute的上面有一个Attribute AttributeUsage,它 的作用是用来指定我们新创建的TransactionCallHandlerAttribute的使用范围。这里我们指 定他可以使用在Class、Method及Property上,除这三个以外,还有很多个使用范围的界定, 这里我们就不在一一说明了。

OK,至此我们已经完成了对Transaction Call Handler和Attribute的类的实现,下面要 做的就是在我们创建的WebTest Project中应用这个Transaction Attribute。

 

四.Transaction Attribute 的应用

我们WebTest Project中加入一个Class,这里叫做TestDA。在这个类里我们提供了一个方 法叫Add,它会循环的把DataSet中的User信息插入到数据库中T_USER表中(这里只是做示例 ,不考虑其合理性及其它因素),我们对这个方法注入我们上面完成的 TransactionCallHandlerAttribute,如图9所示。这也就是说我们把这个方法纳入到 Transaction处理的范围中了,OK,现在我们让第一条数据成功插入,当在插入第二条数据的 时候,我们抛出一个Exception,以验证我们的Transaction注入是否成功,之后我们再把抛 出Exception删除掉,来确认我们的Transaction Commit是功能的。

图9: 数据库操作类

 

using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Data;using CWS.Framework.CallHandler.Transaction;using System.Data.SqlClient;using System.Configuration;namespace test{     public class TestDA : MarshalByRefObject     {         [TransactionCallHandlerAttribute(FunctionName = "User",  ActionDescription = "Add User Information")]         public void AddUser(DataSetUser ds)         {             SqlConnection connection = new SqlConnection ();             connection.ConnectionString =  ConfigurationManager.ConnectionStrings ["TransactionDemoConnectionString"].ConnectionString;             connection.Open();             SqlCommand command = connection.CreateCommand ();             command.CommandType = CommandType.Text;             int i = 0;             foreach (DataSetUser.T_USERRow row in  ds.T_USER.Rows)             {                 if (i == 1)                     throw new Exception("throw this  exception when update second record.");                 command.CommandText = "insert into T_USER ([Name],[Password]) values(@p_username,@p_password)";                 command.Parameters.Clear();                 command.Parameters.Add(new SqlParameter (@"@p_username", row.Name));                 command.Parameters.Add(new SqlParameter (@"@p_password", row.Password));                 command.ExecuteNonQuery();                 i++;             }         }     }}

 

 

 

下面我们来创建两条数据并且调用TestDA中的AddUser方法。我们在WebTest Project的 Default.cs的Page_Load中加入如下代码。

图10: 创建测试数据,并调用TestDA的AddUser方法

using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.UI;using System.Web.UI.WebControls;using Microsoft.Practices.EnterpriseLibrary.PolicyInjection;namespace test{     public partial class _Default : System.Web.UI.Page     {         protected void Page_Load(object sender, EventArgs e)         {             DataSetUser ds = new DataSetUser();             DataSetUser.T_USERRow user1 =  ds.T_USER.NewT_USERRow();             user1.Name = "admin";             user1.Password = "password";             ds.T_USER.Rows.Add(user1);             DataSetUser.T_USERRow user2 =  ds.T_USER.NewT_USERRow();             user2.Name = "tomery";             user2.Password = "password";             ds.T_USER.Rows.Add(user2);             //TestDA t = new TestDA();             TestDA t = PolicyInjection.Create ();             t.AddUser(ds);         }     }}

 

我们通过PIAB的PolicyInjection的Create方法创建一个TestDA的实例对象,为什么要要 用这个方法去创建,而不直接New一个对象呢?这是因为PIAB需要将对方法的调用进行拦截, 然后执行你所需要调用的目标方法上面的注入操作,如我们这个例子中的 TransactionCallHandlerAttribute,然后再执行目标方法,如果你直接使用New的方式,他 不能拦截到对目标方法的调用,这就是我们使用这样方式的原因。

五.总结

通过对传统Transaction的分析与比较,提出对Transaction分离的想法,使用EnterLib实 现对Transaction的注入,然后通过它来拦截操作,完成Transaction的功能与扩展。通过上 边的具体了解了如下内容:

1.Transaction及创建Transaction所需的参数进行了解;

2.Attribute的实现;

3.CallHandler处理类的实现;

4.PIAB注入与拦截的了解。

转载于:https://www.cnblogs.com/chenjiang/archive/2013/06/01/3113309.html

你可能感兴趣的文章
【2012百度之星资格赛】F:百科蝌蚪团
查看>>
【解决方法】Ubuntu文本编辑器gedit打开中文出现乱码的
查看>>
【linux】ubuntu11.10下各种问题以及解决方案
查看>>
C++指针
查看>>
Python学习第一二章
查看>>
Docker学习笔记二:Docker常用命令及提升拉取镜像的速度
查看>>
Python操作Oracle
查看>>
Algs4-2.1.38不同类型的元素
查看>>
MapReduce源码分析总结(转)
查看>>
linux cpu、内存、硬盘空间查询
查看>>
idea 启动调试模式总提示端口58346被占用问题
查看>>
Pro JPA2读书笔记系列(八)-第八章(查询语言)
查看>>
oracle目录操作
查看>>
主流ETL工具
查看>>
fileinput 图片上传
查看>>
UUID
查看>>
Selenium2+Python--下拉选择用select
查看>>
easyui 跳转页面语句
查看>>
golang 中unicode包用法
查看>>
20165226第二次实验
查看>>