在实体框架中创建通用DbContext工厂 [英] Creating generic DbContext Factory in Entity Framework

查看:142
本文介绍了在实体框架中创建通用DbContext工厂的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用.Net Core 2.1.我正在使用多个DbContext.我为每个上下文创建一个DbContextFactory.但是,我想以一种通用的方式做到这一点.我只想创建一个DbContextFactory.我该如何实现?

I'm using .Net Core 2.1. I'm using more than one DbContext. I'm creating a DbContextFactory for every context. But, I want to do this in a generic way. I want to create only one DbContextFactory. How can I achieve this?

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,它应该表示表以及其中的表之间的关系.您的数据库,仅此而已.

So you have a database, and a class that represent this database: your DbContext, it should represent the tables and the relations between the tables that are in your database, nothing more.

您决定将数据库上的操作与数据库本身分开.这是一件好事,因为如果您的数据库的多个用户想要执行同一操作,则他们可以重新使用代码来执行此操作.

You decided to separate the operations on your database from the database itself. That is a good thing, because if several users of your database want to do the same thing, they can re-use the code to do it.

例如,如果要创建具有多个OrderLine的客户订单,其中包含订购的产品,约定的价格,金额等",则需要对数据库做几件事:检查客户是否已经是否存在,请检查所有产品是否已经存在,检查是否有足够的物品,等等.

For instance, if you want to create "an Order for a Customer with several OrderLines, containing ordered Products, agreed Prices, amount, etc", you'll need to do several things with your database: check if the customer already exists, check if all products already exist, check if there are enough items, etc.

这些事情通常是您不应在DbContext中实现的,而应在单独的类中实现的.

These things are typically things that you should not implement in your DbContext, but in a separate class.

如果添加如下功能:CreateOrder,则多个用户可以重新使用此功能.您只需要测试一次,并且如果您决定更改订单模型中的某些内容,那么只有一个地方需要更改订单的创建.

If you add a function like: CreateOrder, then several users can re-use this function. You'll only have to test this only once, and if you decide to change something in your order model, there is only one place where you'll have to change the creation of an Order.

分离代表数据库的类的其他优点(DbContext) 从处理该数据的类中得出的结论是,无需更改数据库用户即可更轻松地更改内部结构.您甚至可以决定从Dapper更改为Entity Framework,而不必更改用法.这也使得为测试目的模拟数据库更加容易.

Other advantages of separating the class that represents your database (DbContext) from the class that handles this data is that will be easier to change the internal structure without having to change the users of your database. You can even decide to change from Dapper to Entity Framework without having to change usage. This makes it also easier to mock the database for test purposes.

诸如CreateOrder,QueryOrder,UpdateOrder之类的功能已经表明它们不是通用数据库操作,它们是针对Ordering数据库而不是School数据库的.

Functions like CreateOrder, QueryOrder, UpdateOrder already indicate that they are not generic database actions, they are meant for an Ordering database, not for a School database.

这可能会导致工作单元可能不是您想要在单独的类中使用的功能的专有名称.几年前,工作单元主要是要表示数据库而不是特定数据库上的操作,对此我不太确定,因为我很快就看到一个真正的工作单元类可以不能增强我的DbContext的功能.

This might have the effect that unit-of-work might not be a proper name for the functionality you want in the separate class. A few years ago, unit-of-work was mainly meant to represent actions on a database, not really a specific database, I'm not really sure about this, because I saw fairly soon that a real unit-of-work class would not enhance functionality of my DbContext.

您经常会看到以下内容:

Quite often you see the following:

  • 代表您的数据库的DbContext类:您创建的数据库,而不是任何一般的数据库概念
  • 一个Repository类,表示将数据存储在某个地方,如何存储的想法,它可以是DbContext,也可以是CSV文件,或者是出于测试目的而创建的Dictionary类的集合.此存储库具有一个IQueryable,并具有添加/更新/删除的功能(根据需要
  • 一个代表您问题的类:订购模型:添加订单/更新订单/获取客户订单:此类真正了解有关订单的所有信息,例如,它具有OrderTotal,可能找不到该位置在您的订购数据库中.
  • A DbContext class that represents your Database: the database that you created, not any generic idea of databases
  • A Repository class that represent the idea of storing your data somewhere, how this is stored, it could be a DbContext, but also a CSV-file, or a collection of Dictionary classes created for Test purposes. This Repository has an IQueryable, and functions to Add / Update / Remove (as far as needed
  • A class that represents your problem: the ordering model: Add Order / Update Order / Get Order of a Customer: this class really knows everything about an Order, for instance that it has an OrderTotal, which is probably nowhere to be found in your Ordering database.

在DbContext之外,有时您可能需要SQL,例如,以提高调用效率.在外部存储库中,不应看到您正在使用SQL

Outside DbContext you sometimes may need SQL, for instance to improve efficiency of a call. Outside Repository it should not be seen that you are using SQL

考虑将关注点分开:如何保存数据(DbContext),如何对数据进行CRUD(创建,提取,更新等)(存储库),如何使用数据(组合表)

Consider to separate the concerns: how to save your data (DbContext), how to CRUD (create, fetch, update, etc) the data (Repository), how to use the data (combine the tables)

我认为您要在工作单元中执行的操作应该在存储库中完成.您的Ordering类应该创建存储库(创建DbContext),查询几个项目以检查其必须添加/更新的数据,执行添加和更新并保存更改.之后,您的订购类应处理存储库,该存储库又将处理DbContext.

I think what you want to do in your unit-of-work should be done inside the repository. Your Ordering class should create the Repository (which creates the DbContext), query several items to check the data it has to Add / Update, do the Add and Update and save the changes. After that your ordering class should Dispose the Repository, which in turn will Dispose the DbContext.

Repository类将与DbContext类非常相似.它具有代表表的几组.每个集合都将实现IQueryable<...>并允许添加/更新/删除,无论需要什么.

The Repository class will look very similar to the DbContext class. It has several sets that represent the tables. Every set will implement IQueryable<...> and allow to Add / Update / Remove, whatever is needed.

由于功能上的相似性,您可以省略Repository类,而让Ordering类直接使用DbContext.但是,请记住,如果将来您决定不再使用实体框架,而是使用一些较新的概念,或者回到Dapper,甚至更低级别,则更改的范围将会更大. SQL会渗透到您的Ordering类中

Because of this similarity in functions you could omit the Repository class and let your Ordering class use the DbContext directly. However, keep in mind, that changes will be bigger if in future you decide that you don't want to use entity framework anymore but some newer concept, or maybe return back to Dapper, or even more low level. SQL will seep through into your Ordering class

我认为您应该自己回答几个问题:

I think you should answer several questions for yourself:

  • 确实只有一个数据库应该由您的DbContext表示,是否可能是在第二个具有相同布局的数据库中使用了相同的DbContext.考虑一下测试数据库或开发数据库.让程序创建要使用的DbContext会更容易/更容易测试/更容易改变吗?
  • 您的DbContext是否确实存在一组用户:每个人都应该可以删除吗?创造?可能是某些程序只想查询数据(通过电子邮件发送定单的程序),而定单程序需要添加客户".也许另一个程序需要添加和更新产品,以及仓库中的产品数量.考虑为它们创建不同的存储库类.每个存储库都具有相同的DbContext,因为它们都访问同一个数据库
  • 类似地:仅一个数据处理类(上述订购类):处理订单的流程是否应该能够更改产品价格并将商品添加到库存中?

之所以需要工厂,是因为您不想让您的主"程序决定为当前运行目的应创建哪些项目.如果您自己创建项目,则代码会容易得多:

The reason that you need the factories, is because you don't want to let your "main" program decide what items it should create for the purpose it is running right now. Code would be much easier if you created the items yourself:

订购过程的创建顺序:

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

处置过程时,处置库即被处置,而处置库又处置DbContext.

When the Process is Disposed, the Repository is Disposed, which in turns Disposes the DbContext.

通过电子邮件发送订单的过程类似:它不必检查产品,也不必创建客户,它只需要获取数据,并可能更新已通过电子邮件发送订单,或者更新电子邮件. -邮寄失败.

Something similar for the process that e-mails the Orders: It does not have to check the products, nor create customers, it only has to fetch data, and maybe update that an order has been e-mailed, or that e-mailing failed.

 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,并以不同的方式记录数据,为存储库提供不同的DbContext,并将数据保存在不同的数据库中,为流程提供一个不同的存储库,然后您将使用Dapper执行查询.

See how much easier you make the creation process, how much more versatile you make it: give the DbContext a different JwtHelper, and the data is logged differently, give the Repository a different DbContext and the data is saved in a different database, give the Process a different Repository, and you'll use Dapper to execute your queries.

测试将更容易:创建一个使用列表保存表的存储库,并使用测试数据对您的过程进行测试将很容易

Testing will be easier: create a Repository that uses Lists to save the tables, and testing your process with test data will be easy

数据库中的更改将更加容易.例如,如果您以后决定将数据库分为一个数据库,分别用于库存和股票价格,一个数据库用于客户和订单",则只需更改一个存储库.没有任何进程会注意到此更改.

Changes in databases will be easier. If for instance you later decide to separate your databases into one for your stock and stock prices and one for Customers and Orders, only one Repository needs to change. None of the Processes will notice this change.

不要让类决定他们需要哪些对象.让该类的创建者说:嘿,您需要一个DbContext?使用它!"这样就省去了工厂之类的需求

Don't let the classes decide which objects they need. Let the creator of the class say: "hey, you need a DbContext? Use this one!" This will omit the need of factories and such

将您的实际数据库(DbContext)与存储和检索数据(Repository)的概念分开,使用一个单独的类来处理数据,而不知道如何存储或检索该数据(过程类)

Separate your actual database (DbContext) from the concept of storing and retrieving data (Repository), use a separate class that handles the data without knowing how this data is stored or retrieved (The process class)

创建几个存储库,这些存储库只能访问执行任务所需的数据(+在预期更改后可以预见的数据).不要创建太多的存储库,但是也不能创建任何能够完成所有工作的存储库.

Create several Repositories that can only access the data they need to perform the task (+data that can be foreseen in future after expected changed). Don't make too much Repositories, but also not one that can do everything.

创建仅执行其预期工作的流程类.不要创建一个包含20个不同任务的流程类.这样只会更加难以描述应执行的操作,更加难以测试和更改任务

Create process classes that do only what they are meant to do. Don't create one process class with 20 different tasks. It will only make it more difficult to describe what it should do, more difficult to test it and more difficult to change the task

这篇关于在实体框架中创建通用DbContext工厂的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆