C# 用Attribute实现AOP事务Word格式.docx
《C# 用Attribute实现AOP事务Word格式.docx》由会员分享,可在线阅读,更多相关《C# 用Attribute实现AOP事务Word格式.docx(12页珍藏版)》请在冰豆网上搜索。
///<
summary>
///字符串连接
/summary>
publicvirtualSqlConnectionStringBuilderConnectionString
{
get
if(_ConnectionString==null||string.IsNullOrEmpty(_ConnectionString.ConnectionString))
_ConnectionString=newSqlConnectionStringBuilder(Configurations.SQLSERVER_CONNECTION_STRING);
}
return_ConnectionString;
set{_ConnectionString=value;
#endregion
#regionExecuteNonQuery
publicintExecuteNonQuery(stringcmdText)
returnSqlHelper.ExecuteNonQuery(ConnectionString.ConnectionString,CommandType.Text,cmdText);
publicintExecuteNonQuery(stringcmdText,CommandTypetype)
returnSqlHelper.ExecuteNonQuery(ConnectionString.ConnectionString,type,cmdText);
publicintExecuteNonQuery(stringcmdText,CommandTypetype,paramsSqlParameter[]cmdParameters)
returnSqlHelper.ExecuteNonQuery(ConnectionString.ConnectionString,type,cmdText,cmdParameters);
代码说明:
1.本类对SqlHelper.cs进一步封装。
2.Configurations.SQLSERVER_CONNECTION_STRING替换成自己的连接字符串就行了。
UserInfoAction.cs
publicclassUserInfoAction:
SqlDAL
///添加用户
publicvoidAdd(UserInfouser)
StringBuildersb=newStringBuilder();
sb.Append("
UPDATE[UserInfo]SETPassword='
"
);
sb.Append(user.Password);
'
WHEREUID="
sb.Append(user.UID);
ExecuteNonQuery(sql);
如果我们要加入事务,通常的办法就是在方法内try、catch然后Commit、Rollback,缺点就不说了,下面我会边贴代码边讲解,力图大家也能掌握这种方法:
)
先贴前面两个被我修改的类
SqlDAL.cs
publicabstractclassSqlDAL:
ContextBoundObject
privateSqlTransaction_SqlTrans;
///仅支持有事务时操作
publicSqlTransactionSqlTrans
if(_SqlTrans==null)
//从上下文中试图取得事务
objectobj=CallContext.GetData(TransactionAop.ContextName);
if(obj!
=null&
&
objisSqlTransaction)
_SqlTrans=objasSqlTransaction;
return_SqlTrans;
set{_SqlTrans=value;
if(SqlTrans==null)
else
returnSqlHelper.ExecuteNonQuery(SqlTrans,CommandType.Text,cmdText);
returnSqlHelper.ExecuteNonQuery(SqlTrans,type,cmdText);
returnSqlHelper.ExecuteNonQuery(SqlTrans,type,cmdText,cmdParameters);
代码说明:
1.加了一个属性(Property)SqlTrans,并且每个ExecuteNonQuery执行前都加了判断是否以事务方式执行。
这样做是为后面从上下文中取事务做准备。
2.类继承了ContextBoundObject,注意,是必须的,MSDN是这样描述的:
定义所有上下文绑定类的基类。
3.TransactionAop将在后面给出。
[Transaction]
[TransactionMethod]
1.很简洁、非侵入式、很少改动、非常方便(想要事务就加2个标记,不想要就去掉)。
2.两个Attribute后面将给出。
///<
///标注类某方法内所有数据库操作加入事务控制
[AttributeUsage(AttributeTargets.Class,AllowMultiple=false)]
publicsealedclassTransactionAttribute:
ContextAttribute,IContributeObjectSink
///标注类某方法内所有数据库操作加入事务控制,请使用TransactionMethodAttribute同时标注
publicTransactionAttribute()
:
base("
Transaction"
)
{}
publicIMessageSinkGetObjectSink(MarshalByRefObjectobj,IMessageSinknext)
returnnewTransactionAop(next);
///标示方法内所有数据库操作加入事务控制
[AttributeUsage(AttributeTargets.Method,AllowMultiple=false)]
publicsealedclassTransactionMethodAttribute:
Attribute
publicTransactionMethodAttribute()
1.在上面两篇文章中都是把IContextProperty,IContributeObjectSink单独继承并实现的,其实我们发现ContextAttribute已经继承了IContextProperty,所有这里我仅仅只需要再继承一下IContributeObjectSink就行了。
关于这两个接口的说明,上面文章中都有详细的说明。
2.TransactionAop将在后面给出。
3.需要注意的是两个Attribute需要一起用,并且我发现Attribute如果标记在类上他会被显示的实例化,但是放在方法上就不会,打断点可以跟踪到这一过程,要不然我也不会费力气弄两个来标注了。
TransactionAop.cs
publicsealedclassTransactionAop:
IMessageSink
privateIMessageSinknextSink;
//保存下一个接收器
///构造函数
paramname="
next"
>
接收器<
/param>
publicTransactionAop(IMessageSinknextSink)
this.nextSink=nextSink;
///IMessageSink接口方法,用于异步处理,我们不实现异步处理,所以简单返回null,
///不管是同步还是异步,这个方法都需要定义
msg"
<
replySink"
returns>
/returns>
publicIMessageCtrlAsyncProcessMessage(IMessagemsg,IMessageSinkreplySink)
returnnull;
///下一个接收器
publicIMessageSinkNextSink
get{returnnextSink;
///
publicIMessageSyncProcessMessage(IMessagemsg)
IMessageretMsg=null;
IMethodCallMessagecall=msgasIMethodCallMessage;
if(call==null||(Attribute.GetCustomAttribute(call.MethodBase,typeof(TransactionMethodAttribute)))==null)
retMsg=nextSink.SyncProcessMessage(msg);
//此处换成自己的数据库连接
using(SqlConnectionConnect=newSqlConnection(Configurations.SQLSERVER_CONNECTION_STRING))
Connect.Open();
SqlTransactionSqlTrans=Connect.BeginTransaction();
//讲存储存储在上下文
CallContext.SetData(TransactionAop.ContextName,SqlTrans);
//传递消息给下一个接收器->
就是指执行你自己的方法
if(SqlTrans!
=null)
IMethodReturnMessagemethodReturn=retMsgasIMethodReturnMessage;
Exceptionexcept=methodReturn.Exception;
if(except!
SqlTrans.Rollback();
//可以做日志及其他处理
SqlTrans.Commit();
SqlTrans.Dispose();
SqlTrans=null;
returnretMsg;
///用于提取、存储SqlTransaction
publicstaticstringContextName
get{return"
TransactionAop"
;
1.IMessageSinkMSDN:
定义消息接收器的接口。
2.主要关注SyncProcessMessage方法内的代码,在这里创建事务,并存储在上下文中间,还记得上面SqlDAL的SqlTrans属性么,里面就是从上下文中取得的。
3.请注意了,这里能捕捉到错误,但是没有办法处理错误,所以错误会继续往外抛,但是事务的完整性我们实现了。
你可以在Global.asax可以做全局处理,也可以手动的try一下,但是我们不需要管理事务了,仅仅当普通的错误来处理了。
结束
大家可以看到,在被标注的方法里面所有的数据库操作都会被事务管理起来,也算是了了我心愿,貌似我的Attribute做权限又看到了一丝希望了,欢迎大家多提意见:
补充(2009-1-8)
关于在评论中提到的性能的问题,如果要使用AOP的方式来实现事务肯定比直接trycatch然后Commit和Rollback效率要低的,但是很明显可维护性、使用方便性要高得多的,所以看个人需求了。
这里补充的是关于SqlDAL继承ContextBoundObject的问题,以下是想到的解决办法:
1.最简单、修改UserInfoAction最少的办法:
把SqlDAL复制一份改下类名,继承一下ContextBoundObject,然后把继承类改一下。
很不推荐:
(
2.从一开始就不使用继承方法来访问数据层的方法,而是将SqlDAL改成一个普通类,通过声明一个SqlDAL方式来访问数据层:
privateSqlDAL_sqlDao;
publicSqlDALSqlDao
if(_sqlDao==null)
_sqlDao=newSqlDAL();
_sqlDao.SqlTrans=objasSqlTransaction;
return_sqlDao;
这样相对于没有加事务类仅仅多一个取值过程和判断过程,效率应该还是比继承SqlDAL直接继承ContextBoundObject好很多。
个人感觉还是不是很好,继续探索,已经想到了减少一个Attribute的办法了,感谢欢迎大家提建议: