44

I'm using EF Core and DatabaseFirst approach. My dbContext is created automatically by Scaffold-DbContext command.

I need to add some new DbSets into a dbContext and add into OnModelCreating method some additional code but after each scaffolding that added code are erased and I have to add it each time again.

What I want to do is to create another partial dbContext class and mark protected override void OnModelCreating(ModelBuilder modelBuilder) method as partial

but get errors:

A partial method cannot have access modifiers or the virtual, abstract, override, new, sealed, or extern modifiers.

A partial method may not have multiple implementing declarations

Here is a pseudo code:

MyDbContext1.cs - generated by 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 - this code I added each time into dbContext after scaffolding:

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);
        });
    }
}
Dmitry Stepanov
  • 2,776
  • 8
  • 29
  • 45
  • kind of duplicated by [this](https://stackoverflow.com/questions/2088265/splitting-combining-partial-methods) post. It is not possible to split a methods logic via `partial`. Wich method should be executed first, or how should the compiler know, how to merge partial methods? From [microsoft docs](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/partial-classes-and-methods): `A partial method declaration consists of two parts: the definition, and the implementation.` 1/2 – nilsK Sep 05 '18 at 11:05
  • You will have to remove `partial` from your method declaration and put your logic in one of your classes. 2/2 – nilsK Sep 05 '18 at 11:05
  • 1
    EF Core Power Tools does this for you – ErikEJ Sep 05 '18 at 18:37
  • Hey Dmitry, please consider making Simon Weaver's answer the accepted one. Four years later, things have changed a bit. This will help others find the best answer. – Brian MacKay Oct 07 '22 at 05:38
  • hi @BrianMacKay, if I asked my question today I definetelly would have marked his answer as right one. But I asked four years ago and at that time there was not `OnModelCreatingPartial` solution, so the accepted answer helped me a lot. I believe it deserves to be the accepted answer inspite of Simon's answer is best workaround for nowadays. – Dmitry Stepanov Nov 12 '22 at 15:26
  • @DmitryStepanov Thanks for the thoughtful response! StackOverflow is intended to help people find the current correct answer to problems, but your answer caused me to do some research and I discovered there is some debate about this topic, or at least there was: https://meta.stackexchange.com/questions/62252/is-it-poor-form-to-switch-accepted-answers ... So do with that as you will. The community will still be able to find the current best via voting. – Brian MacKay Nov 14 '22 at 18:50

3 Answers3

81

EFCore 3 - They FINALLY fixed this!

You can now implement OnModelCreatingPartial in a partial class like this. Note the partial keyword on both the class and method:

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

If you look at the generated context file - right at the very end of OnModelCreating(...) you'll see...

 OnModelCreatingPartial(modelBuilder);

Note: I use scaffolding, but I needed to manually add HasNoKey for a stored procedure (with a custom return type that wasn't otherwise scaffolded).

Ian Kemp
  • 28,293
  • 19
  • 112
  • 138
Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
  • 6
    Any idea where I can find any official documentation on this OnModelCreatingPartial method or how we should be using it? I mean I can hazard a guess, but I can't see to find it documented anywhere. – Simon Gymer Dec 16 '19 at 10:39
  • @SimonGymer This question is the top Google match and I can't find it either! Here's a blog post though https://irina.codes/extending-ef-core-dbcontext/ – Simon_Weaver Feb 26 '21 at 03:00
  • 1
    @Simon_Weaver how is this official. this is a custom implementation looks like – Emil Mar 16 '21 at 00:23
  • 2
    @Emil I'm not quite sure what you mean. The 'official' part is that the generated code (by the official scaffolder) now calls `OnModelCreatingPartial(...)` if you've defined it. This is much easier than it was before. The code shown above is all you need to do to add to the generated code. You've always been able to modify the scaffolded code, but this allows you to regenerate the scaffolded code without interfering with changes you may have made. – Simon_Weaver May 12 '21 at 21:19
  • 1
    This should be the accepted answer IMO. – Brian MacKay Oct 05 '22 at 21:19
  • 1
    @Brian I usually reserve bold text and !!! for when I think my answer deserves exactly that ;-) – Simon_Weaver Oct 07 '22 at 04:06
43

An alternative would be creating another context class that inherit from MyDbContext that actually include all the custom code. and then use this new class as your context. This way, there is no need to update the generated code.

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);
        });
    }
}
bakka
  • 439
  • 5
  • 4
  • 2
    Considerably better than the accepted answer. The base class scaffolds without any manual changes. – Richard Petheram May 08 '19 at 13:19
  • 1
    I've marked this solution as a right answer because it's really no need to change anything in MyDbContext after scaffolding. – Dmitry Stepanov Jul 12 '19 at 10:23
  • 1
    how this can be done in MVC4 and Entity Framework 6. As below code this only work with Entity core.. public MyDbContext2(DbContextOptions options) : base(options) { } – Ashish Thakur Jul 13 '20 at 11:48
  • Just did this in an EF Core 3 project to extend auto generated classes. Thank you. Best answer. Perfect answer. ;) – Ryan Vettese Dec 11 '20 at 16:19
8

You can't override methods in a partial class because all of the "parts" become a single class. But you can accomplish this by having the main OnModelCreating call a partial method. Like this:

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);
    }
}
David Browne - Microsoft
  • 80,331
  • 6
  • 39
  • 67
  • 5
    That only kicks the can down the road: the contexts isn't generated with the `OnModelCreating2(modelBuilder);` call, so they still have to keep modifying the generated code. It would have been an option if the scaffolding code was extensible, for example by being based on an editable tt template. – Gert Arnold Sep 06 '18 at 07:26
  • good workaround but still need to add these lines OnModelCreating2(ModelBuilder modelBuilder); OnModelCreating2(modelBuilder); – hosam hemaily Sep 27 '18 at 18:27
  • @GertArnold something like this maybe https://stackoverflow.com/a/48152710/492482 – Emil Mar 16 '21 at 00:25