欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

利用AOP实现SqlSugar自动事务

程序员文章站 2023-12-14 14:17:04
本文实例为大家分享了如何利用aop实现sqlsugar自动事务,供大家参考,具体内容如下 先看一下效果,带接口层的三层架构: bl层: public cla...

本文实例为大家分享了如何利用aop实现sqlsugar自动事务,供大家参考,具体内容如下

先看一下效果,带接口层的三层架构:

bl层:

 public class studentbl : istudentservice
   {
     private ilogger mlogger;
     private readonly istudentda mstudentda;
     private readonly ivalueservice mvalueservice;

     public studentservice(istudentda studentda,ivalueservice valueservice)
     {
       mlogger = logmanager.getcurrentclasslogger();
       mstudentda = studentda;
       mvalueservice = valueservice;

     }

     [transactioncallhandler]
     public ilist<student> getstudentlist(hashtable paramshash)
     {
       var list = mstudentda.getstudents(paramshash);
       var value = mvalueservice.findall();
       return list;
     }
   }

假设getstudentlist方法里的mstudentda.getstudents和mvalueservice.findall不是查询操作,而是更新操作,当一个失败另一个需要回滚,就需要在同一个事务里,当一个出现异常就要回滚事务。

特性transactioncallhandler就表明当前方法需要开启事务,并且当出现异常的时候回滚事务,方法执行完后提交事务。

da层:

 public class studentda : istudentda
   {

     private sqlsugarclient db;
     public studentda()
     {
       db = sugarmanager.getinstance().sqlsugarclient;
     }
     public ilist<student> getstudents(hashtable paramshash)
     {
       return db.queryable<student>().as("t_student").with(sqlwith.nolock).tolist();
     }
   }

对sqlsugar做一下包装

 public class sugarmanager
   {
     private static concurrentdictionary<string,sqlclient> _cache =
       new concurrentdictionary<string, sqlclient>();
     private static threadlocal<string> _threadlocal;
     private static readonly string _connstr = @"data source=localhost;port=3306;initial catalog=thy;user id=root;password=xxxxxx;charset=utf8";
     static sugarmanager()
     {
       _threadlocal = new threadlocal<string>();
     }

     private static sqlsugarclient creatinstance()
     {
       sqlsugarclient client = new sqlsugarclient(new connectionconfig()
       {
         connectionstring = _connstr, //必填
         dbtype = dbtype.mysql, //必填
         isautocloseconnection = true, //默认false
         initkeytype = initkeytype.systemtable
       });
       var key=guid.newguid().tostring().replace("-", "");
       if (!_cache.containskey(key))
       {
         _cache.tryadd(key,new sqlclient(client));
         _threadlocal.value = key;
         return client;
       }
       throw new exception("创建sqlsugarclient失败");
     }
     public static sqlclient getinstance()
     {
       var id= _threadlocal.value;
       if (string.isnullorempty(id)||!_cache.containskey(id))
         return new sqlclient(creatinstance());
       return _cache[id];
     }


     public static void release()
     {
       try
       {
         var id = getid();
         if (!_cache.containskey(id))
           return;
         remove(id);
       }
       catch (exception e)
       {
         throw e;
       }
     }
     private static bool remove(string id)
     {
       if (!_cache.containskey(id)) return false;

       sqlclient client;

       int index = 0;
       bool result = false;
       while (!(result = _cache.tryremove(id, out client)))
       {
         index++;
         thread.sleep(20);
         if (index > 3) break;
       }
       return result;
     }
     private static string getid()
     {
       var id = _threadlocal.value;
       if (string.isnullorempty(id))
       {
         throw new exception("内部错误: sqlsugarclient已丢失.");
       }
       return id;
     }

     public static void begintran()
     {
       var instance=getinstance();
       //开启事务
       if (!instance.isbegintran)
       {
         instance.sqlsugarclient.ado.begintran();
         instance.isbegintran = true;
       }
     }

     public static void committran()
     {
       var id = getid();
       if (!_cache.containskey(id))
         throw new exception("内部错误: sqlsugarclient已丢失.");
       if (_cache[id].trancount == 0)
       {
         _cache[id].sqlsugarclient.ado.committran();
         _cache[id].isbegintran = false;
       }
     }

     public static void rollbacktran()
     {
       var id = getid();
       if (!_cache.containskey(id))
         throw new exception("内部错误: sqlsugarclient已丢失.");
       _cache[id].sqlsugarclient.ado.rollbacktran();
       _cache[id].isbegintran = false;
       _cache[id].trancount = 0;
     }

     public static void trancountaddone()
     {
       var id = getid();
       if (!_cache.containskey(id))
         throw new exception("内部错误: sqlsugarclient已丢失.");
       _cache[id].trancount++;
     }
     public static void trancountmunisone()
     {
       var id = getid();
       if (!_cache.containskey(id))
         throw new exception("内部错误: sqlsugarclient已丢失.");
       _cache[id].trancount--;
     }
   }

_cache保存sqlsugar实例,_threadlocal确保同一线程下取出的是同一个sqlsugar实例。

不知道sqlsugar判断当前实例是否已经开启事务,所以又将sqlsugar包了一层。

 public class sqlclient
   {
     public sqlsugarclient sqlsugarclient;
     public bool isbegintran = false;
     public int trancount = 0;

     public sqlclient(sqlsugarclient sqlsugarclient)
     {
       this.sqlsugarclient = sqlsugarclient;
     }
   }

isbegintran标识当前sqlsugar实例是否已经开启事务,trancount是一个避免事务嵌套的计数器。

一开始的例子

 [transactioncallhandler]
      public ilist<student> getstudentlist(hashtable paramshash)
      {
        var list = mstudentda.getstudents(paramshash);
        var value = mvalueservice.findall();
        return list;
      }

transactioncallhandler表明该方法要开启事务,但是如果mvalueservice.findall也标识了transactioncallhandler,又要开启一次事务?所以用trancount做一个计数。

使用castle.dynamicproxy

要实现标识了transactioncallhandler的方法实现自动事务,使用castle.dynamicproxy实现bl类的代理

castle.dynamicproxy一般操作

 public class myclass : imyclass
  {
    public void mymethod()
    {
      console.writeline("my mehod");
    }
 }
 public class testintercept : iinterceptor
   {
     public void intercept(iinvocation invocation)
     {
       console.writeline("before");
       invocation.proceed();
       console.writeline("after");
     }
   }

  var proxygenerate = new proxygenerator();
  testintercept t=new testintercept();
  var pg = proxygenerate.createclassproxy<myclass>(t);
  pg.mymethod();
  //输出是
  //before
  //my mehod
  //after

before就是要开启事务的地方,after就是提交事务的地方

最后实现

 public class transactioninterceptor : iinterceptor
   {
     private readonly ilogger logger;
     public transactioninterceptor()
     {
       logger = logmanager.getcurrentclasslogger();
     }
     public void intercept(iinvocation invocation)
     {
       methodinfo methodinfo = invocation.methodinvocationtarget;
       if (methodinfo == null)
       {
         methodinfo = invocation.method;
       }

       transactioncallhandlerattribute transaction =
         methodinfo.getcustomattributes<transactioncallhandlerattribute>(true).firstordefault();
       if (transaction != null)
       {
         sugarmanager.begintran();
         try
         {
           sugarmanager.trancountaddone();
           invocation.proceed();
           sugarmanager.trancountmunisone();
           sugarmanager.committran();
         }
         catch (exception e)
         {
           sugarmanager.rollbacktran();
           logger.error(e);
           throw e;
         }

       }
       else
       {
         invocation.proceed();
       }
     }
   }
   [attributeusage(attributetargets.method, inherited = true)]
   public class transactioncallhandlerattribute : attribute
   {
     public transactioncallhandlerattribute()
     {

     }
   }

autofac与castle.dynamicproxy结合使用

创建代理的时候一个bl类就要一次操作

 proxygenerate.createclassproxy<myclass>(t);

而且项目里bl类的实例化是交给ioc容器控制的,我用的是autofac。当然autofac和castle.dynamicproxy是可以结合使用的

using system.reflection;
using autofac;
using autofac.extras.dynamicproxy;
using module = autofac.module;
public class businessmodule : module
  {
    protected override void load(containerbuilder builder)
    {
      var business = assembly.load("fty.business");
      builder.registerassemblytypes(business)
        .asimplementedinterfaces().interceptedby(typeof(transactioninterceptor)).enableinterfaceinterceptors();
      builder.registertype<transactioninterceptor>();
    }
  }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

上一篇:

下一篇: