C#怎样用AOP管理多表事务实现统一提交2024版

原标题:使用AOP组件重构老旧 ado.net 代码,统一管理多表操作的事务,C#怎样用AOP管理多表事务实现统一提交2021版。老项目使用传统简单三层,分为数据访问层(Accessor)+ DBHelper 类,业务层(Business),以及界面项目。

AOP管理多表事务

随着项目规模的扩大,功能越来越多,随便一个功能就有可能操作好几张表,按以前套路,多表操作的事务管理是在数据访问层里面的,在Accessor这个类库里面使用数据库事务来对多表的写入,更新进行统一事务管理,而且对事务的管理还是处于原始的刀耕火种状态,如下示例代码所示:

    /// <summary>
    /// 新增发票记录
    /// </summary>
    /// <param name="model">Invoice实体</param>
    /// <param name="log">操作日志对象</param>
    /// <returns>返回受影响的行数</returns>
    public int Add(Invoice model, Log log)
    {
        var cmd = db.GetStoredProcCommond(strAdd);
        DbTrans t = new Trans(cmd.Connection);
        try
        {
            using (t)
            {
                int id = Add(model, log, t);
                model.InvoiceID = id;
                detailsDal.Add(model.List, log, t);
                UpdateStatus(model.List, t);
                AddSettlement(model, log, t);
                t.Commit();
                return id;
            }
        }
        catch (SqlException ex)
        {
            t.RollBack();
            return 0;
            throw ex;
        }
    }

上面代码中是对 3 张表进行操作,其中包含了插入操作,更新操作;所以使用了DbTrans对象是对数据库事务的简单封装,从上面代码中可以看出,项目规模越大,老项目写起来越费力费时,全手工打造事务的代码,以及异常处理等等。

AOP面向切面重构代码

聪明的你,或许已经想到了该怎样减少这种重复代码的抒写,那我们就用
(AOP) 面向切面的编程技术来重构上面的代码;在 .net 项目里面,我们可以选择Postsharp、MrAdvice中的任意一个组件来重构,postsharp是需要收费的,并且你要做的工作是将其下载下来进行安装才行,而MrAdvice是开源的aop组件,可以拿来即用,今天我们就使用MrAdvice进行重构。

首先需要告诉你的是,MrAdvice的项目地址是:https://github.com/ArxOne/MrAdvice,可以从这儿下载源码进行查看,你也可以通过NuGet直接在VS 2017里面获得其引用,如下图所示:

AOP管理多表事务

添加了引用后,我们编写一个类:TransactionAttribute,这个类你可以放在业务层,因为重构后我们将在业务层进行事务管理,代码如下:

    /// <summary>
    /// 用于统一在业务层管理数据库事务
    /// </summary>
    public class TransactionAttribute : Attribute, IMethodAdvice
    {
        public void Advise(MethodAdviceContext context)
        {
            TransactionScope TransactionScope= new TransactionScope(TransactionScopeOption.RequiresNew);

            try
            {
                context.Proceed();
                TransactionScope.Complete();
                TransactionScope.Dispose();
            }
            catch (Exception ex)
            {
                Transaction.Current.Rollback();
                //在此处你可以记录错误日志
            }
                               
        }
    }

完成上面代码后,我们修改业务层的代码,在多表操作的方法上面打上这个特性[Transaction],如下代码所示:

        [Transaction]
        public void 多表写入2()
        {
            var i = dal1.Add("第2班,https://jhrs.com 故意出错写入的。");
            if (i == true)
            {
                Console.WriteLine("业务发生错误,看事务回滚了没有!");
                throw new Exception("https://jhrs.com 出错了。。。。");
            }
            dal2.Add("张三2", "0002");
        }

到这一步,你啥都不用管了,是不是经过重构,代码变得优雅了呢?

如果您还不明白,我在重构过程中,编写了一些测试代码,您可以直接复制到测试工程里面跑一下即知道其作用了,完整代码如下:

–创建数据库语句如下:

CREATE TABLE [dbo].[AClass](
     [Class] nvarchar NOT NULL,
     [Id] [int] IDENTITY(1,1) NOT NULL
 GO
CREATE TABLE [dbo].[AStudent](
     [Name12] nvarchar NOT NULL,
     [No] nvarchar NOT NULL,
     [Id] [int] IDENTITY(1,1) NOT NULL
 GO

C#程序代码如下:

public class DbHelper
    {
        #region 字段
        /// <summary>
        /// 数据库提供者
        /// </summary>
        private static string dbProviderName = "System.Data.SqlClient";
        /// <summary>
        /// 连接字符串
        /// </summary>
        private static string connectionString = "Data Source=.;Initial Catalog=DEBUG;Integrated Security=True;Max Pool Size=512; Min Pool Size=20;timeout=5000";

        /// <summary>
        /// 数据库连接
        /// </summary>
        private DbConnection connection;
        #endregion

        #region 构造函数
        /// <summary>
        /// 无参构造函数
        /// </summary>
        public DbHelper()
        {
            connection = this.CreateConnection();
        }
        #endregion

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="conString">连接字符串</param>
        public DbHelper(string conString)
        {
            connectionString = conString;
        }

        /// <summary>
        /// 创建数据库连接
        /// </summary>
        /// <returns>数据库连接</returns>
        private DbConnection CreateConnection()
        {
            DbProviderFactory dbfactory = DbProviderFactories.GetFactory(DbHelper.dbProviderName);
            DbConnection dbconn = dbfactory.CreateConnection();
            dbconn.ConnectionString = connectionString;
            return dbconn;
        }

        /// <summary>
        /// 创建数据库命令
        /// </summary>
        /// <param name="storedProcedure">存储过程名</param>
        /// <returns>数据库命令</returns>
        public DbCommand GetStoredProcCommond(string storedProcedure)
        {
            DbCommand dbCommand = connection.CreateCommand();
            dbCommand.CommandText = storedProcedure;
            dbCommand.CommandType = CommandType.StoredProcedure;
            dbCommand.CommandTimeout = 3600;
            if (dbCommand.Connection.State == ConnectionState.Closed)
            {
                dbCommand.Connection.Open();
            }
            return dbCommand;
        }

        /// <summary>
        /// 创建数据库命令
        /// </summary>
        /// <param name="sqlQuery">SQL语句</param>
        /// <returns>数据库命令</returns>
        public DbCommand GetSqlStringCommond(string sqlQuery)
        {
            DbCommand dbCommand = connection.CreateCommand();
            dbCommand.CommandText = sqlQuery;
            dbCommand.CommandType = CommandType.Text;
            dbCommand.CommandTimeout = 3600;
            if (dbCommand.Connection.State == ConnectionState.Closed)
            {
                dbCommand.Connection.Open();
            }
            return dbCommand;
        }

        /// <summary>
        /// 添加输出参数数
        /// </summary>
        /// <param name="cmd">数据库命令</param>
        /// <param name="parameterName">参数名(一般指输出参数名)</param>
        /// <param name="dbType">数据类型</param>
        /// <param name="size">数据大小</param>
        public void AddOutParameter(DbCommand cmd, string parameterName, DbType dbType, int size)
        {
            DbParameter dbParameter = cmd.CreateParameter();
            dbParameter.DbType = dbType;
            dbParameter.ParameterName = parameterName;
            dbParameter.Size = size;
            dbParameter.Direction = ParameterDirection.Output;
            cmd.Parameters.Add(dbParameter);
        }

        /// <summary>
        /// 添加数据库命令参数(存储过程或SQL语句的参数)
        /// </summary>
        /// <param name="cmd">数据库命令</param>
        /// <param name="parameterName">参数名</param>
        /// <param name="dbType">数据类型</param>
        /// <param name="value">数据值</param>
        public void AddInParameter(DbCommand cmd, string parameterName, DbType dbType, object value)
        {
            DbParameter dbParameter = cmd.CreateParameter();
            dbParameter.DbType = dbType;
            dbParameter.ParameterName = parameterName;
            dbParameter.Value = value;
            dbParameter.Direction = ParameterDirection.Input;
            cmd.Parameters.Add(dbParameter);
        }

        /// <summary>
        /// 添加返回参数(指执行了数据操作后返回的参数)
        /// </summary>
        /// <param name="cmd">数据库命令</param>
        /// <param name="parameterName">参数名</param>
        /// <param name="dbType">数据类型</param>
        public void AddReturnParameter(DbCommand cmd, string parameterName, DbType dbType)
        {
            DbParameter dbParameter = cmd.CreateParameter();
            dbParameter.DbType = dbType;
            dbParameter.ParameterName = parameterName;
            dbParameter.Direction = ParameterDirection.ReturnValue;
            cmd.Parameters.Add(dbParameter);
        }


        /// <summary>
        /// 执行数据库命令
        /// </summary>
        /// <param name="cmd">数据库命令</param>
        /// <returns>返回受影响的行数</returns>
        public int ExecuteNonQuery(DbCommand cmd)
        {
            try
            {
                if (cmd.Connection.State == ConnectionState.Closed)
                {
                    cmd.Connection.Open();
                }
                int ret = cmd.ExecuteNonQuery();
                cmd.Connection.Close();
                return ret;
            }
            catch (SqlException ex)
            {
                cmd.Connection.Close();
                throw ex;
            }
        }

    }

    public class StudentDao
    {
        public bool Add(string name, string no)
        {
            string sql = string.Format("insert into AStudent (Name12,No) values(@name,@no)");
            var db = new DbHelper();
            var cmd = db.GetSqlStringCommond(sql);
            db.AddInParameter(cmd, "@name", DbType.String, name);
            db.AddInParameter(cmd, "@no", DbType.String, no);

            return db.ExecuteNonQuery(cmd) > 0;
        }
    }

    public class ClassDao
    {
        public bool Add(string name)
        {
            string sql = string.Format("insert into AClass(Class) values(@name)");
            var db = new DbHelper();
            var cmd = db.GetSqlStringCommond(sql);
            db.AddInParameter(cmd, "@name", DbType.String, name);
            return db.ExecuteNonQuery(cmd) > 0;
        }
    }
    
    public class 测试业务层
    {
        private ClassDao dal1;
        private StudentDao dal2;
        public 测试业务层()
        {
            dal1 = new ClassDao();
            dal2 = new StudentDao();
        }

        [Transaction]
        public void 多表写入1()
        {
            dal1.Add("第1班");
            dal2.Add("张三1", "0001");
            Console.WriteLine("成功写入了两张表的数据!");
        }

        [Transaction]
        public void 多表写入2()
        {
            var i = dal1.Add("第2班,出错写入的。");
            if (i == true)
            {
                Console.WriteLine("业务发生错误,看事务回滚了没有!");
                throw new Exception("出错了。。。。");
            }
            dal2.Add("张三2", "0002");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            测试业务层 bll = new 测试业务层();
            bll.多表写入1();
            bll.多表写入2();
            Console.ReadLine();
        }
    }

    /// <summary>
    /// 用于统一在业务层管理数据库事务
    /// </summary>
    public class TransactionAttribute : Attribute, IMethodAdvice
    {
        public void Advise(MethodAdviceContext context)
        {
            TransactionScope TransactionScope= new TransactionScope(TransactionScopeOption.RequiresNew);

            try
            {
                context.Proceed();
                TransactionScope.Complete();
                TransactionScope.Dispose();
            }
            catch (Exception ex)
            {
                Transaction.Current.Rollback();
                //在此处你可以记录错误日志
            }
                               
        }
    }
C#怎样用AOP管理多表事务实现统一提交2024版 1

加入电报群

【江湖人士】(jhrs.com)原创文章,作者:江小编,如若转载,请注明出处:https://jhrs.com/2019/26520.html

扫码加入电报群,让你获得国外网赚一手信息。

文章标题:C#怎样用AOP管理多表事务实现统一提交2024版

(3)
江小编的头像江小编
上一篇 2019-01-03 16:49
下一篇 2019-01-09 20:47

热门推荐

Leave a Reply

Sending

国外老牌便宜域名服务商Namecheap注册com域名大优惠,抢到就赚到,优惠码:NEWCOM698
$5.98/年
直达官网