<<ABP框架>> 工作單元

来源:http://www.cnblogs.com/kid1412/archive/2016/10/26/5996459.html
-Advertisement-
Play Games

文檔目錄 本節內容: 簡介 在ABP中管理連接和事務 約定的工作單元 UnitOfWork 特性 IUnitOfWorkManager 工作單元詳情 禁用工作單元 非事務性工作單元 工作單元方法調用另一個方法 工作單元域 自動保存修改 IRepository.GetAll() 方法 UnitOfWo ...


文檔目錄

 

本節內容:

 

簡介

在一個使用資料庫的應用里,連接和事務是非常重要的,何時打開一個連接,何時開啟一個事務,如果釋放連接等等。ABP通過工作單元系統管理資料庫的連接和事務。

 

在ABP中管理連接和事務

當進行一個工作單元方法,ABP打開一個資料庫連接(可能不是馬上打開,在第一次用到資料庫時打開,這要看ORM供應器的實現了)和開始一個事務,所以你可以安全地在這個方法內使用連接,在這個方法的最後,提交事務並釋放連接,如果這個方法拋出任何的異常,回滾事務並釋放連接,這種方式,一個工作單元就是原子性的。ABP自動完成這些操作。

如果一個工作單元方法調用另一個工作單元方法,兩個會使用同一個連接和事務,第一個方法管理連接和事務,另一個使用它們。

 

約定的工作單元方法

有些方法預設為工作單元方法:

假設我們有一個應用服務方法,如下所示:

public class PersonAppService : IPersonAppService
{
    private readonly IPersonRepository _personRepository;
    private readonly IStatisticsRepository _statisticsRepository;

    public PersonAppService(IPersonRepository personRepository, IStatisticsRepository statisticsRepository)
    {
        _personRepository = personRepository;
        _statisticsRepository = statisticsRepository;
    }

    public void CreatePerson(CreatePersonInput input)
    {
        var person = new Person { Name = input.Name, EmailAddress = input.EmailAddress };
        _personRepository.Insert(person);
        _statisticsRepository.IncrementPeopleCount();
    }
}

在CreatePerson方法里,我們使用人員倉儲插入一個人員,並使用統計倉儲遞增人員總數,這兩個倉儲共用相同的連接和事務,因為應用服務方法預設為一個工作單元。當進入CreatePerson方法時,ABP打開一個連接並開始一個事務,在方法結束時,如果沒有異常出現,就提交事務並釋放連接,這種方式,所有資料庫操作,在CreatePerson方法里都變成是原子性的。

 

控制工作單元

上面的方法里,工作單元是暗中工作的。web應用里大部分情況,我們都不用控制工作單元。如果你想在某些地方控制工作單元,你可以顯式地使用它,有兩種方法可以實現。

 

UnitOfWork 特性

優先推薦的方法是:使用UnitOfWork特性,例如:

[UnitOfWork]
public void CreatePerson(CreatePersonInput input)
{
    var person = new Person { Name = input.Name, EmailAddress = input.EmailAddress };
    _personRepository.Insert(person);
    _statisticsRepository.IncrementPeopleCount();
}

因此,CreatePerson方法成為一個工作單元,管理資料庫連接和事務,兩個倉儲使用相同的工作單元,註意,如果在一個應用服務方法里,不需要使用UnitOfWork特性。 Also see 'unit of work method restrictions' section.

UnitOfWork特性有些選項,參考下方的“工作單元詳情”。

 

IUnitOfWorkManager

第二種方法是:使用IUnitOfWorkManager.Begin(...)方法,如下所示:

public class MyService
{
    private readonly IUnitOfWorkManager _unitOfWorkManager;
    private readonly IPersonRepository _personRepository;
    private readonly IStatisticsRepository _statisticsRepository;

    public MyService(IUnitOfWorkManager unitOfWorkManager, IPersonRepository personRepository, IStatisticsRepository statisticsRepository)
    {
        _unitOfWorkManager = unitOfWorkManager;
        _personRepository = personRepository;
        _statisticsRepository = statisticsRepository;
    }

    public void CreatePerson(CreatePersonInput input)
    {
        var person = new Person { Name = input.Name, EmailAddress = input.EmailAddress };

        using (var unitOfWork = _unitOfWorkManager.Begin())
        {
            _personRepository.Insert(person);
            _statisticsRepository.IncrementPeopleCount();

            unitOfWork.Complete();
        }
    }
}

 如上所示,你可以映射並使用IUnitOfWorkManager(有些基類預設已經被註入UnitOfWorkManager:Mvc控制器、應用服務領域服務等),因此,你可以創建更多有域限制的工作單元。用這種方法,你應該手動調用Complete方法,如果你不調用,事務會回滾,修改不會被保存。

Begin方法有幾個設置工作單元選項的重載,如果沒有好的理由,最好使用UnitOfWork特性。

 

工作單元詳情

禁用工作單元

你如果想為約定的工作單元方法禁用工作單元,使用UnitOfWork特性的IsDisabled屬性。例如:

[UnitOfWork(IsDisabled = true)]
public virtual void RemoveFriendship(RemoveFriendshipInput input)
{
    _friendshipRepository.Delete(input.Id);
}

通常,你不會想這麼做,但在有些情況下你需要禁用工作單元:

  • 你可能想限制工作單元的域,如同上面所述,就使用UnitOfWorkScope。

註意:如果一個工作單元方法調用這個RemoveFriendship方法,禁用特性會被忽略,該方法使用與調用者方法相同的工作單元。所以小心使用禁用特性。同樣地,上面的代碼也會工作得很好,因為倉儲方法預設是個工作單元。

 

非事務性工作單元

一個工作單元預設就是事務性的,因此ABP開始/提交/回滾資料庫級別事務。在一些特殊情況下,事務可能會引起問題,因為它可能鎖住資料庫的一些行或表。這種情況下,你可能想禁用資料庫級別事務。UnitOfWork特性可以在它的構造器里,獲得一個boolean值作為非事務性標識。使用示例:

[UnitOfWork(isTransactional: false)]
public GetTasksOutput GetTasks(GetTasksInput input)
{
    var tasks = _taskRepository.GetAllWithPeople(input.AssignedPersonId, input.State);
    return new GetTasksOutput
            {
                Tasks = Mapper.Map<List<TaskDto>>(tasks)
            };
}

我建議[UnitOfWork(isTransactional:false)]這樣使用這個特性,我認為這樣更可讀更明確。但是你也可以[UnitOfWork(false)]這樣用。

註意:ORM框架(如NHibernate和EntityFramework)內部使用一個單獨的命令保存修改。假設你在一個非事務工作單元里更新一些實體,即使是這種況下,所有更新也是在工作單元的最後,執行一個單獨的資料庫命令。但是,如果在一個非事務性工作單元里,直接執行一條Sql語句,它會被直接執行並且不會被回滾。

非事務性工作單元有一個限制,如果你已經在一個事務性工作單元域內,把isTransactional設置false也會被忽略(在一個事務性工作單元里,使用事務域選項創建一個非事務性工作單元)。

使用非事務性工作單元要小心,因為大部分情況下,應當用事務性保證數據完整性。如果你只是讀取數據,而不做修改,那麼就可以安全地使用非事務性。

 

一個工作單元方法調用另一個方法

工作單元是環繞的,如果一個工作單元方法調用另一個工作單元方法,他們共用相同的連接和事務,第一個方法管理連接和事務,另一個方法使用它們。

 

工作單元域

你可以在一個事務里創建另一個獨立的事務,或在一個事務里創建一個非事務性的域,.NET為它定義了TransactionScopeOption。你可以通過設置工作單元域選項來控制它。

 

自動保存修改

如果一個方法是工作單元,ABP會自動在這個方法的最後,保存所有修改。假設我們需要一個方法更新person的name:

[UnitOfWork]
public void UpdateName(UpdateNameInput input)
{
    var person = _personRepository.Get(input.PersonId);
    person.Name = input.NewName;
}

這就是所有代碼,name會被修改!我們甚至不用調用_personRepository.Update方法。在一個工作單元里,ORM框架跟蹤實體的所有修改,並反射到資料庫。

註意:不需要為約定的工作單元方法聲明UnitOfWork。

 

IRepository.GetAll() 方法

當你在一個倉儲方法外調用GetAll(),它必須要有一個打開的資料庫連接,因為它只是返回IQueryable。這是必須的,因為IQuery的延遲執行。它沒有執行資料庫查詢,除非你調用ToList()或在一個foreach迴圈里使用IQueryable(或以某種方式訪問查詢里的項),所以當你調用ToList()方法時,資料庫連接必須可用。

考慮如下示例:

[UnitOfWork]
public SearchPeopleOutput SearchPeople(SearchPeopleInput input)
{
    //Get IQueryable<Person>
    var query = _personRepository.GetAll();

    //Add some filters if selected
    if (!string.IsNullOrEmpty(input.SearchedName))
    {
        query = query.Where(person => person.Name.StartsWith(input.SearchedName));
    }

    if (input.IsActive.HasValue)
    {
        query = query.Where(person => person.IsActive == input.IsActive.Value);
    }

    //Get paged result list
    var people = query.Skip(input.SkipCount).Take(input.MaxResultCount).ToList();

    return new SearchPeopleOutput { People = Mapper.Map<List<PersonDto>>(people) };
}

SearchPeople方法必須是工作單元,因為IQueryable的ToList()方法在這個方法里被調用。並且當IQueryable.ToList()被執行時,資料庫連接必須打開著。

大多數情況,在一個web應用里,使用GetAll方法是安全的,因為所以控制的Action預設都是工作單元並且資料庫連接在整個請求里都是可用的。

 

UnitOfWrok 特性限制

可以使用UnitOfWork為:

  • 類的所有public或public virtual並使用在介面之上的方法(如一個應用服務就使用在介面之上)。
  • 類的所有public virtual並自註入的方法(如Mvc控制器和Web Api控制器)。
  • 所有protected virtual方法。

建議一直使用virtual方法,你可以不為private方法這麼做,因為ABP為它們使用動態代理,而private方法對於繼承它的類來說是看不見的。如果不使用依賴註入並自己實例化這個類,UnitOfWork特性(和任何代理)都無法工作。

 

選項

有些選項可以改變一個工作單元的行為。

首先,我們可以在啟動配置里修改所有工作單元的預設值,這通常在我們模塊的預初始化方法里。

public class SimpleTaskSystemCoreModule : AbpModule
{
    public override void PreInitialize()
    {
        Configuration.UnitOfWork.IsolationLevel = IsolationLevel.ReadCommitted;
        Configuration.UnitOfWork.Timeout = TimeSpan.FromMinutes(30);
    }

    //...other module methods
}

其次,我們可以為一個特殊工作單元重寫預設值,為此,UnitOfWork特性構造器和IUnitOfWorkManager.Begin方法有獲取選項的重載。

最後,你可以通過啟動配置,為Asp.net Mvc、Web Api和Asp.net Core Mvc控制器配置工作單元特性預設值(查看它們各自的文檔)。

 

方法

UnitOfWork系統悄無聲息、無縫地工作著,但是在有些特殊情況下,你需要調用它們的方法。

你可以用如下作一種方式,訪問當前工作單元:

  • 如果你的類是從有些特殊基類(ApplicationService、AbpController、AbpApiController等)繼承而來,可以直接使用CurrentUnitOfWork屬性。
  • 你可以把IunitOfWorkManager註入到任何類里,然後使用IUnitOfWorkManager.Current屬性。

 

SaveChanges

ABP在一個工作單元的最後,保存所有修改,你不必做任何事,但是有時你想在一個工作單元操作的中間保存修改到資料庫,例如在EntityFramework里,為獲取新實體的Id,先進行保存。

你可以使用當前工作單元的SaveChanges或SaveChangesAsync方法。

註意:如果當前工作單元是事務性的,所有的修改在出現異常情況下回滾,不然保存。

 

事件

一個工作單元有Completed、Failed和Disposed事件,你可以註冊這些事件然後執行需要的操作。例如,你想在當前工作單元成功完成後運行一些代碼:

public void CreateTask(CreateTaskInput input)
{
    var task = new Task { Description = input.Description };

    if (input.AssignedPersonId.HasValue)
    {
        task.AssignedPersonId = input.AssignedPersonId.Value;
        _unitOfWorkManager.Current.Completed += (sender, args) => { /* TODO: Send email to assigned person */ };
    }

    _taskRepository.Insert(task);
}

 


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

-Advertisement-
Play Games
更多相關文章
  • 不要急,源代碼分享在最底部,先問大家一個問題,你在寫開放的API介面時是如何保證數據的安全性的?先來看看有哪些安全性問題在開放的api介面中,我們通過http Post或者Get方式請求伺服器的時候,會面臨著許多的安全性問題,例如: 1. 請求來源(身份)是否合法? 2. 請求參數被篡改? 3. 請... ...
  • 一、概念 Moq是利用諸如Linq表達式樹和Lambda表達式等·NET 3.5的特性,為·NET設計和開發的Mocking庫。Mock字面意思即模擬,模擬對象的行為已達到欺騙目標(待測試對象)的效果. Moq模擬類類型時,不可模擬密封類,不可模擬靜態方法(適配器可解決),被模擬的方法及屬性必須被v ...
  • 在編寫開發框架的時候,經常會遇到要找出應用所用到的所有程式集和類,然後進行下一步的處理。 例如,我們有一個通用控制項類BaseControl,各種富文本編輯器控制項、表格控制項、分頁控制項等都繼承於通用控制項類BaseControl。甚至CMS這個項目的評論等控制項也會繼承該通用控制項類BaseControl。我 ...
  • startup startup asp.net core 的入口,在構造函數中完成環境參數的配置。 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory ...
  • 在很多情況下,我們開發都需要有一個快速的代碼生成工具用來提高開發效率,代碼生成工具很多信息都是讀取資料庫的表、視圖等元數據進行對象表信息的完善,有了這些信息,我們就可以在普通的實體類代碼裡面添加屬性欄位的中文註釋,或者在Winform或者Web界面的快速生成的時候,可以在查詢框或者界面編輯的時候,充... ...
  • 目前.Net Core上沒有System.Drawing這個類庫,想要在.Net Core上處理圖片得另闢蹊徑。 微軟給出了將來取代System.Drawing的方案,偏向於使用一個單獨的服務端進行各種圖片處理 "https://github.com/dotnet/corefx/issues/202 ...
  • 實現字元或數字的組合排列。例如:ab 的所有組合為: ab,ba ;ab的所有不重覆排列為:ab ...
  • 按照領域驅動設計的思路,我們搭建開發框架的解決方案如下: *該解決方案正在改造過程中,會隨著改造的過程逐步完善。 解決方案目錄 對應領域設計層 說明 Infrastructure 基礎設施層 開發的底層類庫 Core 包括緩存、配置、日誌、常用工具、數據訪問等核心組件 Core.Caching.Re ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...