簡體   English   中英

在實體框架中創建通用 DbContext 工廠

[英]Creating generic DbContext Factory in Entity Framework

我正在使用.Net Core 2.1。 我正在使用多個DbContext 我正在為每個上下文創建一個DbContextFactory 但是,我想以一種通用的方式做到這一點。 我只想創建一個DbContextFactory 我怎樣才能做到這一點?

MyDbContextFactory.cs

public interface IDbContextFactory<TContext> where TContext : DbContext
{
    DbContext Create();
}

public class MyDbContextFactory : IDbContextFactory<MyDbContext>
{
    public IJwtHelper JwtHelper { get; set; }

    public MyDbContextCreate()
    {
        return new MyDbContext(this.JwtHelper);
    }

    DbContext IDbContextFactory<MyDbContext>.Create()
    {
        throw new NotImplementedException();
    }
}

UnitOfWork.cs

public class UnitOfWork<TContext> : IUnitOfWork<TContext> where TContext : DbContext
{
    public static Func<TContext> CreateDbContextFunction { get; set; }
    protected readonly DbContext DataContext;

    public UnitOfWork()
    {
        DataContext = CreateDbContextFunction();
    }
 }

MyDbContext.cs

public class MyDbContext : DbContext
{
    private readonly IJwtHelper jwtHelper;

    public MyDbContext(IJwtHelper jwtHelper) : base()
    {
        this.jwtHelper = jwtHelper;
    }
}

所以你有一個數據庫和一個代表這個數據庫的類:你的DbContext ,它應該代表你的數據庫中的表和表之間的關系,僅此而已。

您決定將數據庫上的操作與數據庫本身分開。 這是一件好事,因為如果您的數據庫的多個用戶想要做同樣的事情,他們可以重復使用代碼來做這件事。

例如,如果您想創建“具有多個 OrderLines 的客戶訂單,其中包含訂購的產品、商定的價格、金額等”,您需要對您的數據庫做幾件事:檢查客戶是否已經存在,檢查如果所有產品都已經存在,請檢查是否有足夠的項目等。

這些東西通常不應該在 DbContext 中實現,而是在單獨的類中實現。

如果添加類似CreateOrder的函數,則多個用戶可以重復使用此函數。 您只需測試一次,如果您決定更改訂單模型中的某些內容,則只有一個地方您必須更改訂單的創建。

將表示您的數據庫的類 (DbContext) 與處理此數據的類分開的其他優點是可以更輕松地更改內部結構,而無需更改數據庫的用戶。 您甚至可以決定從 Dapper 更改為 Entity Framework,而無需更改使用。 這也使得為了測試目的而模擬數據庫變得更加容易。

CreateOrder、QueryOrder、UpdateOrder 等函數已經表明它們不是通用數據庫操作,它們適用於 Ordering 數據庫,而不是 School 數據庫。

這可能會導致工作單元可能不是您在單獨類中想要的功能的正確名稱。 幾年前,工作單元主要用於表示數據庫上的操作,而不是真正的特定數據庫,我對此不太確定,因為我很快就看到一個真正的工作單元類會不增強我的 DbContext 的功能。

您經常會看到以下內容:

  • 代表您的數據庫的 DbContext 類:您創建的數據庫,而不是任何通用的數據庫概念
  • 一個 Repository 類,代表將數據存儲在某處的想法,它是如何存儲的,它可以是 DbContext,也可以是 CSV 文件,或為測試目的創建的 Dictionary 類的集合。 這個存儲庫有一個 IQueryable,以及添加/更新/刪除的功能(根據需要
  • 代表您的問題的類:訂購模型:添加訂單/更新訂單/獲取客戶的訂單:此類確實了解有關訂單的所有信息,例如它有一個 OrderTotal,這可能在您的訂購中找不到數據庫。

在 DbContext 之外,您有時可能需要 SQL,例如提高調用效率。 外部存儲庫不應看到您正在使用 SQL

考慮分離關注點:如何保存數據(DbContext),如何 CRUD(創建、獲取、更新等)數據(存儲庫),如何使用數據(組合表)

我認為您想在工作單元中執行的操作應該在存儲庫中完成。 您的 Ordering 類應該創建存儲庫(創建 DbContext),查詢幾個項目以檢查它必須添加/更新的數據,執行添加和更新並保存更改。 之后,您的排序類應該處理存儲庫,而存儲庫又將處理 DbContext。

Repository 類看起來與 DbContext 類非常相似。 它有幾個代表表格的集合。 每個集合都將實現IQueryable<...>並允許添加/更新/刪除,無論需要什么。

由於函數的這種相似性,您可以省略 Repository 類並讓您的 Ordering 類直接使用 DbContext。 但是,請記住,如果將來您決定不再使用實體框架而是使用一些更新的概念,或者可能返回到 Dapper 甚至更低級別,那么更改將會更大。 SQL 將滲透到您的 Ordering 類中

選擇什么

我認為你應該自己回答幾個問題:

  • 是否真的只有一個數據庫應該由您的 DbContext 表示,是否應該在具有相同布局的第二個數據庫中使用相同的 DbContext。 想想測試數據庫或開發數據庫。 讓您的程序創建要使用的 DbContext 不是更容易/更好的可測試性/更好的可變性嗎?
  • 您的 DbContext 是否真的有一組用戶:每個人都應該有刪除的可能性嗎? 去創造? 會不會是某些程序只想查詢數據(發送訂單郵件的程序),而訂單程序需要添加客戶。 也許另一個程序需要添加和更新產品,以及倉庫中的產品數量。 考慮為它們創建不同的存儲庫類。 每個 Repository 都獲得相同的 DbContext,因為它們都在訪問同一個數據庫
  • 類似地:只有一個數據處理類(上面提到的訂購類):處理訂單的過程是否應該能夠更改產品價格並將商品添加到庫存中?

您需要工廠的原因是因為您不想讓您的“主”程序決定它應該為它現在運行的目的創建哪些項目。 如果您自己創建項目,代碼會容易得多:

訂購流程的創建順序:

 IJwtHelper jwtHelper = ...;

 // The product database: all functionality to do everything with Products and Orders
 ProductDbContext dbContext = new ProductDbContext(...)
 {
    JwtHelper = jwtHelper,
    ...
 };

 // The Ordering repository: everything to place Orders,
 // It can't change ProductPrices, nor can it stock the wharehouse
 // So no AddProduct, not AddProductCount,
 // of course it has TakeNrOfProducts, to decrease Stock of ordered Products
 OrderingRepository repository = new OrderingRepository(...) {DbContext = dbContext};

 // The ordering process: Create Order, find Order, ...
 // when an order is created, it checks if items are in stock
 // the prices of the items, if the Customer exists, etc.
 using (OrderingProcess process = new OrderingProcess(...) {Repository = repository})
{
     ... // check if Customer exists, check if all items in stock, create the Order
     process.SaveChanges();
}

當 Process 被 Disposed 時,Repository 被 Disposed,而 Repository 又 Dispose DbContext。

通過電子郵件發送訂單的過程類似:它不必檢查產品,也不需要創建客戶,它只需要獲取數據,並且可能會更新訂單已通過電子郵件發送,或者電子郵件發送失敗.

 IJwtHelper jwtHelper = ...;

 // The product database: all functionality to do everything with Products and Orders
 ProductDbContext dbContext = new ProductDbContext(...) {JwtHelper = jwtHelper};

 // The E-mail order repository: everything to fetch order data
 // It can't change ProductPrices, nor can it stock the wharehouse
 // It can't add Customers, nor create Orders
 // However it can query a lot: customers, orders, ...
 EmailOrderRepository repository = new EmailOrderRepository(...){DbContext = dbContext};

 // The e-mail order process: fetch non-emailed orders,
 // e-mail them and update the e-mail result
 using (EmailOrderProcess process = new EmailOrderProcess(...){Repository = repository}
 {
     ... // fetch the orders that are not e-mailed yet
         // email the orders
         // warning about orders that can't be emailed
         // update successfully logged orders
     repository.SaveChanges();

看看你使創建過程變得多么容易,你使它變得多才多藝:給 DbContext 一個不同的 JwtHelper,數據記錄不同,給 Repository 一個不同的 DbContext,數據保存在不同的數據庫中,給處理不同的存儲庫,您將使用 Dapper 執行查詢。

測試會更容易:創建一個使用 Lists 來保存表格的 Repository,使用測試數據測試你的流程會很容易

數據庫的更改將更容易。 例如,如果您后來決定將數據庫分成一個用於股票和股票價格,一個用於客戶和訂單,則只需更改一個存儲庫。 任何進程都不會注意到這種變化。

結論

不要讓類決定他們需要哪些對象。 讓類的創建者說:“嘿,你需要一個 DbContext?用這個!” 這將省略工廠等的需要

將您的實際數據庫 (DbContext) 與存儲和檢索數據的概念 (Repository) 分開,使用一個單獨的類來處理數據而不知道如何存儲或檢索這些數據(流程類)

創建幾個存儲庫,這些存儲庫只能訪問執行任務所需的數據(+在預期更改后可以預見的未來數據)。 不要做太多的存儲庫,但也不是一個可以做所有事情的存儲庫。

創建只做它們應該做的事情的過程類。 不要創建一個包含 20 個不同任務的流程類。 它只會讓描述它應該做什么變得更加困難,更難以測試它並且更難以改變任務

如果您想重用現有的實現,EF Core 5 現在提供了開箱即用的 DbContext Factory: https ://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-5.0 /whatsnew#dbcontextfactory

確保正確處理它,因為它的實例不是由應用程序的服務提供商管理的。

請參閱 Microsoft 文檔使用 DbContext 工廠

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM