前言
    使用Attribute来实现方法级别事务一直是我的梦想,浅谈Attribute [C# | Attribute | DefaultValueAttribute]有体现我的无奈,Attribute确实是真真切切的非侵入式的东西(其实我是想侵入的: ) ),前有DUDUAttribute编程中的应用系列文章,但是总是离想象和需求有那么点出入,通过三天的努力,Google的陪伴,下面和大家一起分享我这三天的成果 Attribute实现AOP事务 吧!
致谢文章
    1.    Aspect-Oriented Programming Enables Better Code Encapsulation and Reuse 关键性的CallContext是在这里发现的。
    2.    C# Attribute在编程中的应用 (转)这篇文章原文地址不到了,DUDUAttribute编程中的应用系列文章就是这篇文章的分解,他写到了五,后面的大家可以从这篇文章里面提前看到了。
阅前注意
    1.    整篇文章的核心和突破点在于上下文Context的使用,务必注意CallContext在整个程序中起到的作用
    2.    本文中看到的SqlHelper使用的是微软SqlHelper.cs
    3.    本文重点在于如何实现,并且已经测试通过,只贴关键性代码,所以请认真阅读,部分代码直接拷贝下来运行是会出错的!
正文
    首先我们来看一段未加事务的代码:
    SqlDAL.cs   
public abstract class SqlDAL
{


        #region ConnectionString

        private SqlConnectionStringBuilder _ConnectionString = null;
        /// <summary>
        /// 字符串连接
        /// </summary>
        public virtual SqlConnectionStringBuilder ConnectionString
        {
            get
            {
                if (_ConnectionString == null || string.IsNullOrEmpty(_ConnectionString.ConnectionString))
                {
                    _ConnectionString = new SqlConnectionStringBuilder(Configurations.SQLSE
RVER_CONNECTION_STRING);
                }
                return _ConnectionString;
            }
            set { _ConnectionString = value; }
        }

        #endregion

        #region ExecuteNonQuery

        public int ExecuteNonQuery(string cmdText)
        {
            return SqlHelper.ExecuteNonQuery(ConnectionString.ConnectionString, CommandType.Text, cmdText);
        }

        public int ExecuteNonQuery(string cmdText, CommandType type)
        {
            return SqlHelper.ExecuteNonQuery(ConnectionString.ConnectionString, type, cmdText);
        }

        public int ExecuteNonQuery(string cmdText, CommandType type, params SqlParameter[] cmdParameters)
        {
            return SqlHelper.ExecuteNonQuery(ConnectionString.ConnectionString, type, cmdText, cmdParameters);
        }

        #endregion
    代码说明:
          1.    本类对SqlHelper.cs 进一步封装。
          2.    Configurations.SQLSERVER_CONNECTION_STRING 替换成自己的连接字符串就行了。
    UserInfoAction.cs
    public class UserInfoAction : SqlDAL
    {
        /// <summary>
        /// 添加用户
        /// </summary>
        public void Add(UserInfo user)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("UPDATE [UserInfo] SET Password='");
            sb.Append(user.Password);
            sb.Append("' WHERE UID=");
            sb.Append(user.UID);
            ExecuteNonQuery(sql);
        }
    }
    如果我们要加入事务,通常的办法就是在方法内trycatch然后CommitRollback,缺点就不说了,下面我会边贴代码边讲解,力图大家也能掌握这种方法: )
    先贴前面两个被我修改的类
    SqlDAL.cs
public abstract class SqlDAL : ContextBoundObject
{


        private SqlTransaction _SqlTrans;
        /// <summary>
        /// 仅支持有事务时操作
        /// </summary>
        public SqlTransaction SqlTrans
        {
            get
            {
                if (_SqlTrans == null)
                {
                    //从上下文中试图取得事务
                    object obj = CallContext.GetData(TransactionAop.ContextName);
                    if (obj != null && obj is SqlTransaction)
                        _SqlTrans = obj as SqlTransaction;
                }
                return _SqlTrans;
            }
            set { _SqlTrans = value; }
        }

        #region ConnectionString

        private SqlConnectionStringBuilder _ConnectionString = null;
        /// <summary>
        /// 字符串连接
        /// </summary>
        public virtual SqlConnectionStringBuilder ConnectionString
        {
            get
            {
                if (_ConnectionString == null || string.IsNullOrEmpty(_ConnectionString.ConnectionString))
                {
                    _ConnectionString = new SqlConnectionStringBuilder(Configurations.SQLSERVER_CONNECTION_STRING);
                }
                return _ConnectionString;
            }
            set { _ConnectionString = value; }
        }

        #endregion

        #region ExecuteNonQuery


        public int ExecuteNonQuery(string cmdText)
        {
            if (SqlTrans == null)
                return SqlHelper.ExecuteNonQuery(ConnectionString.ConnectionString, CommandType.Text, cmdText);
            else
                return SqlHelper.ExecuteNonQuery(SqlTrans, CommandType.Text, cmdText);
        }

        public int ExecuteNonQuery(string cmdText, CommandType type)
        {
            if (SqlTrans == null)
                return SqlHelper.ExecuteNonQuery(ConnectionString.ConnectionString, type, cmdText);
            else
                return SqlHelper.ExecuteNonQuery(SqlTrans, type, cmdText);
        }

        public int ExecuteNonQuery(string cmdText, CommandType type, params SqlParameter[] cmdParameters)
        {
            if (SqlTrans == null)
                return SqlHelper.ExecuteNonQuery(ConnectionString.ConnectionString, type, cmdText, cmdParameters);
            else
                return SqlHelper.ExecuteNonQuery(SqlTrans, type, cmdText, cmdParameters);
        }

        #endregion


    }
    代码说明:
          1.    加了一个属性(Property)SqlTrans,并且每个ExecuteNonQuery执行前都加了判断是否以事务方式执行。这样做是为后面从上下文中取事务做准备。
          2.    类继承了ContextBoundObject,注意,是必须的,MSDN是这样描述的:定义所有上下文绑定类的基类。
          3.    TransactionAop将在后面给出。
    UserInfoAction.cs
  [Transaction]
    public class UserInfoAction : SqlDAL
    {
        [TransactionMethod]
        public void Add(UserInfo user)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("UPDATE [UserInfo] SET Password='");
            sb.Append(user.Password);
            sb.Append("' WHERE UID=");
            sb.Append(user.UID);
            ExecuteNonQuery(sql);
        }
    }
    代码说明:
          1.    很简洁、非侵入式、很少改动、非常方便(想要事务就加2个标记,不想要就去掉)
          2.    两个Attribute后面将给出。
  /// <summary>
    /// 标注类某方法内所有数据库操作加入事务控制
    /// </summary>
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public sealed class TransactionAttribute : ContextAttribute, IContributeObjectSink
    {

        /// <summary>
        /// 标注类某方法内所有数据库操作加入事务控制,请使用TransactionMethodAttribute同时标注
        /// </summary>
        public TransactionAttribute()
            : base("Transaction")
        { }


        public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink next)
        {
            return new TransactionAop(next);
        }sqltransaction什么意思
    }

    /// <summary>
    /// 标示方法内所有数据库操作加入事务控制
    /// </summary>
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
    public sealed class TransactionMethodAttribute : Attribute
    {
        /// <summary>
        /// 标示方法内所有数据库操作加入事务控制
        /// </summary>
        public TransactionMethodAttribute()
        {

        }
    }

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。