如何在EntityFramework Core中使用部分类和部分OnModelCreating方法扩展DbContext

44

我正在使用EF Core和DatabaseFirst方法。我的dbContext是通过 Scaffold-DbContext 命令自动生成的。

我需要向dbContext中添加一些新的DbSet,并在OnModelCreating方法中添加一些额外的代码,但是每次脚手架生成之后,这些添加的代码都会被擦除,我必须再次添加它们。

我的想法是创建另一个partial dbContext类并将protected override void OnModelCreating(ModelBuilder modelBuilder)方法标记为partial

但遇到了错误:

partial方法不能有访问修饰符或virtual、abstract、override、new、sealed或extern修饰符。

partial方法不允许具有多个实现声明

这里是伪代码:

MyDbContext1.cs - 由Scaffold-DbContext生成

public partial class MyDbContext : DbContext
{
    public MyDbContext()
    {
    }

    public MyDbContext(DbContextOptions<MyDbContext> options)
        : base(options)
    {
    }

    public virtual DbSet<Client> Clients { get; set; }

    protected override partial void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Client>(entity =>
        {
            // some code ...
        }
    }
}

MyDbContext2.cs - 在使用脚手架后,我每次都会将这段代码添加到dbContext中:

public partial class MyDbContext
{
    public virtual DbSet<JustAnotherEntity> AnotherEntity { get; set; }

    protected override partial void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<JustAnotherEntity>(entity =>
        {
            entity.HasKey(e => new {e.Id, e.IdAction, e.IdState})
                .ForSqlServerIsClustered(false);
        });
    }
}

这种问题已经被这个帖子重复了。不可能通过partial来分割方法逻辑。哪个方法应该先执行,或者编译器如何知道如何合并部分类?根据微软文档部分类声明由两部分组成:定义和实现。2/2 - nilsK
你需要从方法声明中删除 partial,并将逻辑放在你的类之一中。2/2 - nilsK
1
EF Core Power Tools可以为您完成此操作。 - ErikEJ
嗨,Dmitry,请考虑将Simon Weaver的答案设为被采纳的答案。四年后,情况有些变化。这将帮助其他人找到最佳答案。 - Brian MacKay
嗨@BrianMacKay,如果我今天问我的问题,我肯定会将他的答案标记为正确答案。但是我四年前问过,那时还没有OnModelCreatingPartial解决方案,所以被接受的答案对我帮助很大。我认为它应该成为被接受的答案,尽管Simon的答案是现在最好的解决方法。 - Dmitry Stepanov
@DmitryStepanov 感谢您的周到回复!StackOverflow旨在帮助人们找到解决问题的当前正确答案,但是您的答案促使我进行了一些研究,我发现这个话题存在一些争议,或者至少曾经存在:https://meta.stackexchange.com/questions/62252/is-it-poor-form-to-switch-accepted-answers ... 所以您可以根据自己的意愿处理。社区仍将通过投票找到当前最佳答案。 - Brian MacKay
3个回答

82

EFCore 3 - 他们终于修复了这个问题!

您现在可以像这样在部分类中实现OnModelCreatingPartial。注意partial关键字同时用于类和方法

public partial class RRStoreContext : DbContext
{
    partial void OnModelCreatingPartial(ModelBuilder builder)
    {
        builder.Entity<RepeatOrderSummaryView>().HasNoKey();
    }
}

如果您查看生成的上下文文件 - 就在 OnModelCreating(...) 的最后,您会看到...

 OnModelCreatingPartial(modelBuilder);

注意:我使用脚手架,但是对于一个存储过程(带有自定义返回类型,并且不包含在脚手架中),我需要手动添加HasNoKey

6
你知道在哪里可以找到关于 OnModelCreatingPartial 方法的官方文档,或者我们应该如何使用它吗?我的意思是,我可能能够猜测,但我似乎找不到任何文档记录它。 - Simon Gymer
@SimonGymer 这个问题是谷歌搜索的最佳匹配,我也找不到!不过这里有一篇博客文章 https://irina.codes/extending-ef-core-dbcontext/ - Simon_Weaver
1
@Simon_Weaver 这怎么可能是官方的,看起来像是自定义实现。 - Emil
2
@Emil 我不太确定你的意思。'官方'部分是,由官方脚手架生成的代码现在会调用OnModelCreatingPartial(...),如果您已经定义了它。这比以前容易得多。上面显示的代码是您需要添加到生成的代码中的全部内容。您一直可以修改脚手架代码,但这使您可以重新生成脚手架代码,而不会干扰您可能进行的更改。 - Simon_Weaver
1
在我看来,这应该是被采纳的答案。 - Brian MacKay
1
@Brian 我通常会将粗体和感叹号保留给我认为值得这样做的答案;-) - Simon_Weaver

42

另一种方法是创建一个从 MyDbContext 继承并实际包含所有自定义代码的上下文类,然后使用这个新类作为您的上下文。这样,就不需要更新生成的代码。

public class MyDbContext2 : MyDbContext 
{
    public MyDbContext2()
    {
    }

    public MyDbContext2(DbContextOptions<MyDbContext> options)
        : base(options)
    {
    }

    public virtual DbSet<JustAnotherEntity> AnotherEntity { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<JustAnotherEntity>(entity =>
        {
            entity.HasKey(e => new {e.Id, e.IdAction, e.IdState})
                .ForSqlServerIsClustered(false);
        });
    }
}

2
比接受的答案要好得多。基类脚手架没有任何手动更改。 - Richard Petheram
1
我已将此解决方案标记为正确答案,因为在脚手架之后,MyDbContext 中实际上不需要更改任何内容。 - Dmitry Stepanov
1
如何在MVC4和Entity Framework 6中实现此功能。与下面的代码不同,这仅适用于Entity Core。public MyDbContext2(DbContextOptions<MyDbContext> options) : base(options) { } - Ashish Thakur
刚在一个EF Core 3项目中完成了这个操作,以扩展自动生成的类。谢谢。最佳答案。完美答案。;) - Ryan Vettese

8
你无法在部分类中重写方法,因为所有"部分"将成为一个类。 但是你可以通过使主要的OnModelCreating调用部分类方法来完成此操作。 就像这样:
public partial class Db : DbContext
{
    partial void OnModelCreating2(ModelBuilder modelBuilder)
    {
       //additional config
    }
}

public partial class Db : DbContext
{

    public DbSet<Person> Persons { get; set; }

    partial void OnModelCreating2(ModelBuilder modelBuilder);
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        OnModelCreating2(modelBuilder);
    }
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer("Server=localhost;database=efcore2test;integrated security=true");
        base.OnConfiguring(optionsBuilder);
    }
}

5
仅仅将问题拖延下去:调用“OnModelCreating2(modelBuilder);”并不能生成上下文,因此他们仍然必须不断修改生成的代码。如果脚手架代码是可扩展的,比如基于可编辑的tt模板,那么这可能是一种选择。 - Gert Arnold
不错的解决方法,但仍需要添加这些行:OnModelCreating2(ModelBuilder modelBuilder); OnModelCreating2(modelBuilder); - hosam hemaily
@GertArnold 或许可以像这个链接里的代码一样 https://dev59.com/_loU5IYBdhLWcg3wsYTM#48152710 - Emil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接