TrackableEntities / EntityFrameworkCore.Scaffolding.Handlebars

Scaffold EF Core models using Handlebars templates.
MIT License
210 stars 53 forks source link

Extend OnModelCreating with Partial Method #29

Closed Perlkonig closed 5 years ago

Perlkonig commented 6 years ago

This library is the bomb! This has greatly simplified my database-first workflow. Thank you!

I want to add some code at the end of the generated OnModelCreating function (to deal with DateTime kinds). Is there a way to add that to the template? In DbContext.hbs I see {{{on-model-creating}}}, but I'm not clear on if there's a way to extend or add to that.

Or is there a way to add a .HasConversion line to every generated DateTime field?

tonysneed commented 6 years ago

{{{on-model-creating}}} just adds the entire method that is generated by EF Core scaffolding.

I think the easiest way to deal with this scenario is for you to create a partial DbContext derived class, then include the OnModelCreating override there and do whatever you need to do.

If, however, you want to generate the code for this method, then extend HbsCSharpDbContextGenerator in a NET Standard 2.0 class library and override GenerateOnModelCreating. You would then need to register your class in ScaffoldingDesignTimeServices.ConfigureDesignTimeServices. Then you'll have full control -- although this approach requires more effort. :)

Perlkonig commented 6 years ago

Thanks for your help! So if I understand what you're saying, I should create a non-scaffolded partial DbContext class that inherits from the scaffolded one. And in that derived class's OnModelCreating function, call the base one that was scaffolded and then just drop my date handling code in after that. I don't know why I didn't think of that before. Thanks so much!

tonysneed commented 6 years ago

Actually partial classes do not involve inheritance. The two .cs files are just one class, so the class name needs to be the same in both files and include the partial keyword. See http://www.tutorialsteacher.com/csharp/csharp-partial-class

Perlkonig commented 6 years ago

But in this case there will be two OnModelCreating functions, no? The one scaffolding creates and the one that adds the date handing? I assumed that wasn't permissible.

I inherited for now and it works, but if there's a better way, I want to learn it! I'm still new to the ways of .NET. Thanks again.

tonysneed commented 6 years ago

Why don’t you remove the {{on-model-creating}} from the template and only have it in your custom partial class?

If you can do that, you don’t need to inherit. You can instead use partial classes.

Perlkonig commented 6 years ago

Don't I need the one the scaffolding created? It's quite extensive. I'm trying to set things up so if I make a change to the database I can regenerate the entire schema with zero manual intervention. I'm in a database-first situation.

tonysneed commented 6 years ago

OK so then you should use a partial method that is called from the generated OnModelCreating method and implemented in your partial class. See the article I referenced earlier. That is the proper way to accomplish what you want to do.

tonysneed commented 6 years ago

Of course for that we would need to modify code generated for OnModelCreating. So in the meantime your workaround should suffice.

Perlkonig commented 6 years ago

Many thanks.

meurig commented 5 years ago

I had the same requirement for extending OnModelCreating.

I ended up replacing the last three lines of the generated DbContext class with the following:

            OnModelCreatingPartial(modelBuilder);
        }

        partial void OnModelCreatingPartial(ModelBuilder modelBuilder);

    }

}

As suggested here: https://github.com/ErikEJ/EFCorePowerTools/issues/20

I'm currently doing this with a powershell script which gets run after the scaffolding, but it would be great if these lines could be added to the GenerateOnModelCreating method here:

https://github.com/TrackableEntities/EntityFrameworkCore.Scaffolding.Handlebars/blob/master/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsCSharpDbContextGenerator.cs#L225

In case it helps anyone in the meantime, here's my hacky powershell code:

        $files = Get-ChildItem -Path $projectDir -Filter *Context.cs
        foreach ($item in $files)
        {
            $search = Get-content $item.FullName
            # strip last 3 lines
            $search = $search[0..($search.count - 4)]
            # add some new lines of our making!
            $search += ""
            $search += "            OnModelCreatingPartial(modelBuilder);"
            $search += "        }"
            $search += ""
            $search += "        partial void OnModelCreatingPartial(ModelBuilder modelBuilder);"
            $search += ""
            $search += "    }"
            $search += "    }"
            $search | Set-Content $item.FullName
        }
tonysneed commented 5 years ago

@meurig Re-opening this issue as a feature request for adding a partial method to OnModelCreating in the DbContext class.

JochemVH1 commented 3 years ago

{{{on-model-creating}}} just adds the entire method that is generated by EF Core scaffolding.

I think the easiest way to deal with this scenario is for you to create a partial DbContext derived class, then include the OnModelCreating override there and do whatever you need to do.

If, however, you want to generate the code for this method, then extend HbsCSharpDbContextGenerator in a NET Standard 2.0 class library and override GenerateOnModelCreating. You would then need to register your class in ScaffoldingDesignTimeServices.ConfigureDesignTimeServices. Then you'll have full control -- although this approach requires more effort. :)

Hello extending the 'HbsCSharpDbContextGeneror' is exactly the thing I need right now. I currently created some custom handlebar helpers via AddHandlebarsBlockHelpers in ConfigureDesignTimeServices. Im just wondering what is meant by: "You would then need to register your class in ScaffoldingDesignTimeServices.ConfigureDesignTimeServices". Does this mean I have to register this for dependency injection?

tonysneed commented 3 years ago

@JochemVH1 Yes precisely. Call services.AddSingleon in DesignTimeServices class.

JochemVH1 commented 3 years ago

@tonysneed might be a stupid question but do I need to remove the standard implementation? pointing to ICSharpDbContextGenerator? Somehow my derived class doesnt get hit while debugging

JochemVH1 commented 3 years ago

@tonysneed nevermind I had to make a call to register the class after the call to AddHandleBarsScaffoling

JochemVH1 commented 3 years ago

great it works now, so glad I stumbled across this post

tonysneed commented 3 years ago

@JochemVH1 For further reference, and for others trying this approach, please see Full Control: Extend Handlebars Generators in my EF Core Community Standup demo.