如何拦截 DbContext 中的 ModelBuilder 实例?

How can I intercept the ModelBuilder instance in a DbContext?

我正在编写一个库,它将围绕 Entity Framework Core 5+ 中的 DbContext class 提供新的 API。我已经有了这些新 API 的一个版本,但它需要在最终 DbContext 实施中进行手动干预,例如:

// Code in the library.
public static class AwesomeExtensions
{
    public static ModelBuilder AddAwesomeExtensionsSupportingEntities(this ModelBuilder modelBuilder)
    {
        // Set up custom entities I need to make this work.
        return modelBuilder;
    }
}

// Code somewhere else.
public class MyBusinessDbContext : DbContext
{
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // I would like to avoid doing this here.
        modelBuilder.AddAwesomeExtensionsSupportingEntities();

        // Business as usual (database entities).
    }
}

经过扩展搜索后,我没有在 EF Core API 中找到允许我以非侵入方式执行此操作的扩展点。

这是我目前发现的:

理想情况下,我想通过注册 DbContext 依赖项时提供的 DbContextOptionsBuilder 对象来执行此操作,例如:

// Code in some application.
public void AddServices(IServiceCollection services)
{
    services.AddDbContext<MyBusinessDbContext>(options =>
    {
        // The ideal solution.
        options.UseAwesomeExtensions();
    });
}

如果我只能在 ModelBuilder 的实例提供给 OnModelCreating 方法之前以不需要修改 DbContext 实现的方式拦截它,那对我有帮助。

欢迎提出任何想法。

谢谢。

负责调用 OnModelCreating 的 EF Core 服务被调用 IModelCustomizer 使用单一方法

public void Customize(ModelBuilder modelBuilder, DbContext context);

拦截那个方法可以让你做你想做的事。唯一的问题是 EF Core 没有提供一种简单的方法来 覆盖 现有的实现。唯一可用的方法是 ReplaceService,它是全有或全无,其明显缺点是如果您只想执行基本实现的 pre/post 处理,您需要知道哪个是 class你正在更换。当然,其他一些扩展也可以替换您的实现(最后获胜)。

正确实施需要一堆用于注册自定义 IDbContextOptionsExtension to be able to manipulate directly services collection inside ApplyServices 方法的样板。如果您有兴趣,可以在其他 EF Core 扩展库(例如 LinqKit)中找到示例。

假设没有其他扩展覆盖有问题的服务,并且知道默认的 EF Core 实现目前由 ModelCustomizer class in general or RelationalModelCustomizer class 为关系数据库提供(但目前没有添加任何东西到简单调用 OnModelCreating 的基本实现),简化的实现是继承其中一个并用您的实现替换服务的问题。例如像

namespace Microsoft.EntityFrameworkCore
{
    using Infrastructure;

    public static class AwesomeDbContextOptionsExtensions
    {
        public static DbContextOptionsBuilder UseAwesomeExtensions(
            this DbContextOptionsBuilder optionsBuilder)
            => optionsBuilder.ReplaceService<IModelCustomizer, AwesomeModelCustomizer>();
    }
}

namespace Microsoft.EntityFrameworkCore.Infrastructure
{
    public class AwesomeModelCustomizer : RelationalModelCustomizer
    {
        public AwesomeModelCustomizer(ModelCustomizerDependencies dependencies)
            : base(dependencies) { }
        public override void Customize(ModelBuilder modelBuilder, DbContext context)
        {
            // Do something before context.OnModelCreating(modelBuilder)...
            base.Customize(modelBuilder, context);
            // Do something after context.OnModelCreating(modelBuilder)...
        }
    }
}