【问题标题】:How can I intercept the ModelBuilder instance in a DbContext?如何拦截 DbContext 中的 ModelBuilder 实例?
【发布时间】:2021-12-04 15:28:26
【问题描述】:

我正在编写一个库,它将围绕 Entity Framework Core 5+ 中的 DbContext 类提供新的 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 中找到允许我以非侵入方式执行此操作的扩展点。

这是我目前发现的:

  • CustomDbContext 类:我可以从 DbContext 继承并覆盖 OnModelCreating 方法,但这并不比我现在所做的更好。
  • DbContextOptionsBuilder.UseModel:我认为这可能是我可以使用但增加了太多复杂性的东西。通过使用此 API,框架将不会调用 OnModelCreating 方法。
  • IEntityTypeConfiguration<TEntity>:我支持这个,但它还要求你可以访问ModelBuilder 实例,然后你可以使用ModelBuilder.ApplyConfigurationsFromAssembly 方法。

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

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

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

欢迎提出任何想法。

谢谢。

【问题讨论】:

    标签: c# entity-framework .net-core entity-framework-core


    【解决方案1】:

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

    public void Customize(ModelBuilder modelBuilder, DbContext context);
    

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

    正确实现需要一堆样板来注册自定义IDbContextOptionsExtension,以便能够直接在ApplyServices 方法中操作服务集合。如果您有兴趣,可以在其他 EF Core 扩展库(例如 LinqKit)中找到示例。

    假设没有其他扩展覆盖有问题的服务,并且知道当前默认的 EF Core 实现通常由 ModelCustomizer 类或关系数据库的 RelationalModelCustomizer 类提供(但目前没有添加任何内容到简单地调用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)...
            }
        }
    }
    

    【讨论】:

    • 这非常有用。我会试一试,然后发回我的结果。我还将看看 LinqKit。谢谢。
    • 非常感谢。我决定采用涉及IDbContextOptionsExtension 接口的正确方法。我可以使用LinqKit 中的代码作为我的起点。
    • 我对 EFC 的了解大部分来自于实验/遍历源代码。关于文档,如果存在这样的地方,我也希望看到它:)
    • 嗨@Ivan。只是为了让您知道您对这个问题的帮助使我能够创建一个solution for this problem。再次感谢!
    • @yv989c 太好了!因此,您正在做类似于EntityFrameworkCore.MemoryJoin 的事情,但针对Contains 并使用不同的方法来传递值。
    猜你喜欢
    • 1970-01-01
    • 2011-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多