C#用Attribute实现AOP事务.docx

上传人:b****8 文档编号:9650759 上传时间:2023-02-05 格式:DOCX 页数:16 大小:487.18KB
下载 相关 举报
C#用Attribute实现AOP事务.docx_第1页
第1页 / 共16页
C#用Attribute实现AOP事务.docx_第2页
第2页 / 共16页
C#用Attribute实现AOP事务.docx_第3页
第3页 / 共16页
C#用Attribute实现AOP事务.docx_第4页
第4页 / 共16页
C#用Attribute实现AOP事务.docx_第5页
第5页 / 共16页
点击查看更多>>
下载资源
资源描述

C#用Attribute实现AOP事务.docx

《C#用Attribute实现AOP事务.docx》由会员分享,可在线阅读,更多相关《C#用Attribute实现AOP事务.docx(16页珍藏版)》请在冰豆网上搜索。

C#用Attribute实现AOP事务.docx

C#用Attribute实现AOP事务

刖言

使用Attribute来实现方法级别事务一直是我的梦想,浅谈Attribute[C#|Attribute|DefaultValueAttribute]有体现我的无奈,

用Attribute实现AOP事务吧!

关键性的CallContext是在这里发现的

象和需求有那么点出入,通过三天的努力,Google的陪伴,下面和大家一起分享我这三天的成果

致谢文章

1.Aspect-OrientedProgrammingEnablesBetterCodeEncapsulationandReuse

2.C#Attribute在.net编程中的应用(转)这篇文章原文地址找不到了,DUDU的Attribute在.net编程中的应用系列文章就是这篇文章的分解,他写到了五,后面的大家可以从这篇文章里面提前看到了

阅刖注意

1.整篇文章的核心和突破点在于上下文Context的使用,务必注意CallContext在整个程序中起到的作用

2.本文中看到的SqlHelper使用的是微软SqlHelper.cs。

3.本文重点在于如何实现,并且已经测试通过,只贴关键性代码,所以请认真阅读,部分代码直接拷贝下来运行是会岀错的

正文

首先我们来看一段未加事务的代码:

SqlDAL.cs

publicabstractclassSqlDAL

{

#regionConnectionString

privateSqlConnectionStringBuilder_ConnectionString=null;

///

III字符串连接

///

publicvirtualSqlConnectionStringBuilderConnectionString

get

{

if(_ConnectionString==null||string.lsNullOrEmpty(_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);

}

#endregion

代码说明:

1.本类对SqlHelper.cs进一步封装。

2.Configurations.SQLSERVER_CONNECTION_STRING替换成自己的连接字符串就行了。

UserlnfoAction.cs

publicclassUserInfoAction:

SqlDAL

{

///

///添加用户

///

publicvoidAdd(UserInfouser)

{

StringBuildersb=newStringBuilder();

sb.Append("UPDATE[Userinfo]SETPassword^");

sb.Append(user.Password);

sb.Append("'WHEREUID=");

sb.Append(user.UID);

ExecuteNonQuery(sql);

}

}

如果我们要加入事务,通常的办法就是在方法内try、catch然后Commit、Rollback,缺点就不说了,下面我会边贴代码边讲解,力图大

家也能掌握这种方法:

先贴前面两个被我修改的类

SqlDAL.cs

publicabstractclassSqlDAL:

ContextBoundObject

{

privateSqlTransaction_SqlTrans;

///

/〃仅支持有事务时操作

///

publicSqlTransactionSqlTrans

{

get

{

if(_SqlTrans==null)

{

//从上下文中试图取得事务

objectobj=CallContext.GetData(TransactionAop.ContextName);

if(obj!

=null&&objisSqlTransaction)

_SqlTrans=objasSqlTransaction;

}

return_SqlTrans;

}

set{_SqlTrans=value;}

}

#regionConnectionString

Ill

///字符串连接

Ill

publicvirtualSqlConnectionStringBuilderConnectionstring

{

get

{

if(_ConnectionString==null||string.lsNullOrEmpty(_ConnectionString.ConnectionString))

{

_ConnectionString=newSqlConnectionStringBuilder(Configurations.SQLSERVER_CONNECTION_STRING);

}

return_ConnectionString;

}

set{_ConnectionString=value;}

}

#endregion

#regionExecuteNonQuery

publicintExecuteNonQuery(stringcmdText)

{

if(SqlTrans==null)

returnSqlHelper.ExecuteNonQuery(ConnectionString.ConnectionString,CommandType.Text,cmdText);

else

returnSqlHelper.ExecuteNonQuery(SqlTrans,CommandType.Text,cmdText);

}

publicintExecuteNonQuery(stringcmdText,CommandTypetype)

{

if(SqlTrans==null)

returnSqlHelper.ExecuteNonQuery(ConnectionString.ConnectionString,type,cmdText);

else

returnSqlHelper.ExecuteNonQuery(SqlTrans,type,cmdText);

}

publicintExecuteNonQuery(stringcmdText,CommandTypetype,paramsSqlParameter[]cmdParameters)

{

if(SqlTrans==null)

returnSqlHelper.ExecuteNonQuery(ConnectionString.ConnectionString,type,cmdText,cmdParameters);

else

returnSqlHelper.ExecuteNonQuery(SqlTrans,type,cmdText,cmdParameters);

这样做是为后面

#endregion

}

代码说明:

1.加了一个属性(Property)SqlTrans,并且每个ExecuteNonQuery执行前都加了判断是否以事务方式执行。

从上下文中取事务做准备。

2.类继承了ContextBoundObject,注意,是必须的,MSDN是这样描述的:

定义所有上下文绑定类的基类。

3.TransactionAop将在后面给出。

UserlnfoAction.cs

[Transaction]

publicclassUserInfoAction:

SqlDAL

{

[TransactionMethod]

publicvoidAdd(UserInfouser)

{

StringBuildersb=newStringBuilder();

sb.Append("UPDATE[UserInfo]SETPassword^");

sb.Append(user.Password);

sb.Append("'WHEREUID=");

sb.Append(user.UID);

ExecuteNonQuery(sql);

}

}

代码说明:

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;//保存下一个接收器

///

///构造函数

///

///接收器

publicTransactionAop(IMessageSinknextSink)

{

this.nextSink=nextSink;

}

///

///IMessageSink接口方法,用于异步处理,我们不实现异步处理,所以简单返回null,

///不管是同步还是异步,这个方法都需要定义

///

///

///

///

publicIMessageCtrlAsyncProcessMessage(IMessagemsg,IMessageSinkreplySink)

{

returnnull;

}

///

///下一个接收器

///

publicIMessageSinkNextSink

{

get{returnnextSink;}

}

///

///

///

///

///

publicIMessageSyncProcessMessage(IMessagemsg)

{

IMessageretMsg=null;

IMethodCallMessagecall=msgasIMethodCallMessage;

if(call==null||(Attribute.GetCustomAttribute(call.MethodBase,typeof(TransactionMethodAttribute)))==n

ull)

retMsg=nextSink.SyncProcessMessage(msg);

else

{

//此处换成自己的数据库连接

using(SqlConnectionConnect=newSqlConnection(Configurations.SQLSERVER_CONNECTION_STRING))

Connect.Open();

SqlTransactionSqlTrans=Connect.BeginTransaction();

//讲存储存储在上下文

CallContext.SetData(TransactionAop.ContextName,SqlTrans);

//传递消息给下一个接收器->就是指执行你自己的方法

retMsg=nextSink.SyncProcessMessage(msg);

if(SqlTrans!

=null)

{

IMethodReturnMessagemethodReturn=retMsgasIMethodReturnMessage;

Exceptionexcept=methodReturn.Exception;

if(except!

=null)

{

SqlTrans.Rollback();

//可以做日志及其他处理

}

else

{

SqlTrans.Commit();

}

SqlTrans.Dispose();

SqlTrans=null;

}

}

}

returnretMsg;

}

///

///用于提取、存储SqlTransaction

///

publicstaticstringContextName

{

get{return"TransactionAop";}

}

}

代码说明:

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

{

get

{

if(_sqlDao==null)

{

_sqlDao=newSqlDAL();

objectobj=CallContext.GetData(TransactionAop.ContextName);if(obj!

=null&&objisSqlTransaction)

_sqlDao.SqlTrans=objasSqlTransaction;

}

return_sqlDao;

好很多

这样相对于没有加事务类仅仅多一个取值过程和判断过程,效率应该还是比继承SqlDAL直接继承ContextBoundObject

个人感觉还是不是很好,继续探索,已经想到了减少一个Attribute的办法了,感谢欢迎大家提建议:

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > IT计算机 > 计算机硬件及网络

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1