在 ABP vNext 中編寫倉儲單元測試的問題一則

来源:https://www.cnblogs.com/myzony/archive/2019/09/18/11540330.html
-Advertisement-
Play Games

一、問題 新項目是基於 ABP vNext 框架進行開發的,所以我要求為每層編寫單元測試。在同事為某個倉儲編寫單元測試的時候,發現了一個奇怪的問題。他的對某個聚合根的 A 欄位進行了更新,隨後對某個導航屬性 B 也進行了變更,最後通過倉儲提供的 方法對變更的數據進行持久化。 結果再次查出來的時候,發 ...


一、問題

新項目是基於 ABP vNext 框架進行開發的,所以我要求為每層編寫單元測試。在同事為某個倉儲編寫單元測試的時候,發現了一個奇怪的問題。他的對某個聚合根的 A 欄位進行了更新,隨後對某個導航屬性 B 也進行了變更,最後通過倉儲提供的 UpdateAsync() 方法對變更的數據進行持久化。

結果再次查出來的時候,發現聚合根的 A 欄位倒是更新了,但是導航屬性 B 的內部欄位沒有進行變更。例如在下麵的實例當中,聚合根的 Name 欄位變更成功,但是導航屬性的 Street 欄位變更失敗了。

二、原因

數據沒有更新到,說明問題肯定出在 UpdateAsync 方法內部,通過打斷點單步步入之後,也沒發現有什麼奇怪的地方,是使用的 ABP vNext 提供的預設倉儲實現。

又在想是否跟實體追蹤有關,然後看同事寫得單元測試代碼,發現他是先使用的 GetAsync() 方法獲取到實體,然後手動變更了實體的屬性。變更完成之後,通過倉儲提供的 UpdateAsync() 方法進行更新。

看了很久發現它們並不是公用的一個工作單元,這就導致 GetAsync()UpdateAsync() 方法內部得到的 DbContext 是不一樣的。在 EF Core 內部針對這種情況,稱之為 Disconnected entities斷開連接的實體,這個時候需要用戶手動 Attch 追蹤導航屬性。

三、解決

所以有兩種解決辦法,第一種方法是保證使用 GetAsync()UpdateAsync() 方法時,它們都處於一個工作單元下,例如下麵的偽代碼。

private readonly IUnitOfWorkManager _uowMgr;
private readonly IRepository<TestUser, Guid> _repository;

[Fact]
public async Task Resolve1()
{
    // 創建初始數據。
    var entityId = Guid.NewGuid();
    await _repository.InsertAsync(new TestUser
    {
        Id = entityId,
        Name = "張三",
        Address = new TestUserAddress
        {
            City = "成都市",
            Street = "春熙路"
        }
    });

    using (var outerUow = _uowMgr.Begin())
    {
        var entity = await _repository.GetAsync(entityId);
        entity.Name = "李四";
        entity.Address.Street = "琴台路";

        await _repository.UpdateAsync(entity);
        await outerUow.CompleteAsync();
    }
    
    // 最後查詢街道是否成功修改。
    var result = await _repository.GetAsync(entityId);
    result.Name.ShouldBe("李四");
    result.Address.Street.ShouldBe("琴台路");
}

第二種方法變動則要大一些, 導航屬性沒有更新的根本原因,是因為在第二個工作單元中沒有追蹤到這個屬性,你只需要手動附加該導航屬性即可。在下麵的例子中,我們重寫了 UpdateAsync() 方法,手動跟蹤導航屬性,也能夠達到上述效果。

public class TestUserRepository : EfCoreRepository<XXXDbContext,TestUser,Guid>
{
    public TestUserRepository(IDbContextProvider<XXXDbContext> dbContextProvider) : base(dbContextProvider)
    {
    }

    public override IQueryable<TestUser> WithDetails()
    {
        return GetQueryable().Include(x => x.Address);
    }

    public override Task<TestUser> UpdateAsync(TestUser entity, bool autoSave = false, CancellationToken cancellationToken = new CancellationToken())
    {
        DbContext.Attach(entity.Address).State = EntityState.Modified;
        return base.UpdateAsync(entity, autoSave, cancellationToken);
    }
}

四、參考資料


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

-Advertisement-
Play Games
更多相關文章
  • 前提 入行已經7,8年了,一直想做一套漂亮點的自定義控制項,於是就有了本系列文章。 GitHub:https://github.com/kwwwvagaa/NetWinformControl 碼雲:https://gitee.com/kwwwvagaa/net_winform_custom_contr ...
  • <Style x:Key="workButtonStyle" TargetType="{x:Type Button}"> <Style.Triggers> <Trigger Property="IsMouseOver" Value="False"> <Setter Property="FontSiz... ...
  • C#上手練習2(FOR語句) ...
  • 1.索引器的索引值類型不限定為整數 2.索引器允許重載 3.索引器不是一個變數 4.索引器以函數簽名方式this標識,而屬性採用名稱來標識,名稱可以任意 5.索引器不能使用static來進行聲明,屬性可以。索引器永遠屬於實例成員,因此不能聲明為static。 ...
  • 1、列印字元串。 2、調用簡單方法,方法里有if語句、Swich語句。 ...
  • lambda演變歷史 .NetFramework1.0 1.1下,lambda表達式是這樣去寫的,首先聲明一個無參無返回值delegate委托,再聲明一個無參無返回值的方法,把這個方法當做參數一樣傳遞給委托 你也可以聲明一個有參數和有返回值的委托,就像聲明方法一樣: .NetFramework2.0 ...
  • 又一個新的名詞(taghelper),通過taghelper是可以操作html標簽、條件輸出、更是自由添加內外元素。當然也內置了挺多的asp 開頭的taghelper。 下麵文章中也簡單的帶大家實現一個taghelper; 創建自定義html元素 創建一個類ButtonTagHelper tagNa ...
  • 準備: ADFS安裝配置 https://www.cnblogs.com/luoyedemeng/articles/9837685.html 添加一個Providers 配置ADFS 設置你的identityServer地址 設置你的urn 我的是:urn:identityServer 接下來一路下 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...