利用AOP實現SqlSugar自動事務

来源:http://www.cnblogs.com/jaycewu/archive/2017/10/25/7733114.html
-Advertisement-
Play Games

先看一下效果,帶介面層的三層架構: BL層: 假設GetStudentList方法里的mStudentDa.GetStudents和mValueService.FindAll不是查詢操作,而是更新操作,當一個失敗另一個需要回滾,就需要在同一個事務里,當一個出現異常就要回滾事務。 特性Transact ...


先看一下效果,帶介面層的三層架構:

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>();
        }
    }

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • keepalived是什麼 keepalived直譯就是保持存活,在網路裡面就是保持線上了,也就是所謂的高可用或熱備,用來防止單點故障(單點故障是指一旦某一點出現故障就會導致整個系統架構的不可用)的發生,keepalived實現的基礎是vrrp,至於vrrp是什麼請直接看這裡 "vrrp" ,下麵我 ...
  • iptables的使用 基本用法: 詳細用法說明: 1)table 2)subcommand選項 1》鏈管理 2》查看: 3》規則管理: 3)鏈chain: 4)match,匹配條件 1》基本匹配條件: 2》擴展匹配條件: 1 隱式擴展 :對某一種協議的擴展 1.tcp 協議, m tcp,擴展選項 ...
  • 由於記憶力有限,把平時常用的Linux命令整理出來,以便隨時查閱。 ls (list 顯示當前目錄下文件和目錄 ls -l 詳細顯示 =ll ) ifconfig ( ifconfig ,查看本機 IP地址,子網掩碼等 ) :wq ( 保存退出 ) :q! (不保存退出) ...
  • umask 設定文件創建時的預設模式,對於文件最大值為6,對於目錄最大值為7。 系統不允許創建一個文本文件就賦予可執行許可權,而目錄對應位表示進入或可搜索許可權,故最大值為7 find -perm 八進位表示許可權值前不加橫杠表示所屬主,加了表示所有用戶 -prune 若同時指定-depth選項,則該選項 ...
  • 瞭解I2C匯流排協議; 掌握S3C2410/S3C2440中I2C介面的使用方法 ...
  • 企業面試題2:使用for迴圈在/oldboy目錄下通過隨機小寫10個字母加固定字元串oldboy批量創建10個html文件,名稱例如為: [root@oldboy oldboy]# sh /server/scripts/oldboy.sh [root@oldboy oldboy]# ls coaol ...
  • 1.查詢的模糊匹配 儘量避免在一個複雜查詢裡面使用 LIKE '%parm1%'—— 紅色標識位置的百分號會導致相關列的索引無法使用,最好不要用. 解決辦法: 其實只需要對該腳本略做改進,查詢速度便會提高近百倍。改進方法如下: a、修改前臺程式——把查詢條件的供應商名稱一欄由原來的文本輸入改為下拉列 ...
  • 理解需求:理清業務邏輯,根據業務場景,選擇合適的解決方案 按照開發語言的編程規範開發 對象的序列化:處理ajax請求時,一般都是將後臺要返回的數據序列化成json對象,這樣便於頁面獲取json數據 如果後臺返回給頁面的數據已經是json對象了,但是前臺仍然獲取不到,可以用 $.parseJSON(d ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...