在 Asp.Net 核心中注入 DbContext。具体类型还是接口?

Inject DbContext in Asp.Net Core. Concrete type or interface?

在我正在注入的 Asp.Net 核心项目上 Entity Framework DbContext:

public MessageRepository(MyDbContext context) {
}

配置为:

services
  .AddEntityFramework()
  .AddSqlServer()
  .AddDbContext<Context>(x => x.UseSqlServer(connectionString);

我应该创建一个接口 IMyDbContext,然后注入它吗?

public class MyDbContext : DbContext, IMyDbContext { }

public MessageRepository(IMyDbContext context) {
}

在所有 ASP.NET 核心示例中,我看到具体类型 MyDbContext 正在被注入,而不是接口 ...

我应该选择什么选项?

我们总是注入一个接口,因为它更容易在单元和集成测试中模拟。

  1. 您愿意更改 MessageRepository 构造函数的签名吗?它依赖于具体类型。
  2. 您是否为您的代码编写测试?使用和接口可以更容易地模拟数据库上下文。

如果您对以上一项或多项回答“否”,请注入具体类型;否则,注入接口。

[编辑] 使用以下内容。

context services.AddScoped<IApplicationDbContext>(provider => provider.GetService<ApplicationDbContext>());

在我看来,你应该像@Uli 所说的那样始终依赖一个接口,但是当涉及到 DbContext 时,你需要记住你正在公开 EntityFramework Core 的 DbContext 的所有方法

public class MyDbContext : DbContext, IMyDbContext { }

在那种情况下,您不需要实现任何公开的方法,因为 DbContext 会为您处理。

但是,如果 EF 代码更改了 DbContext 并且您对项目进行了更新,那么您将处于每次更新 IMyDbContext 以及所有单元测试的痛苦境地。在我看来,这会让你很头疼。

这个answers/questions可以帮助你理解为什么

Unit testing EF - how to extract EF code out from BL?

目前我自己在做一个项目,我决定使用 2 个这样的界面

public interface IDbContext : IDisposable
{
    DbContext Instance { get; }
}

public interface IApplicationDbContext : IDbContext
{
    DbSet<MyEntity> MyEntities { get; set; }
    ...
}

然后我的具体 DbContext 将只实现应用程序上下文接口

public class ApplicationDbContext : DbContext, IApplicationDbContext
{
    public DbContext Instance => this

    public DbSet<MyEntity> MyEntities { get; set; }
}

这允许我将应用程序上下文的实现作为应用程序上下文接口注入,同时还允许我通过实例访问 DbContext 方法 属性 getter 而无需添加所需的方法从 DbContext class 到接口。

到现在为止,效果很好。