3

How do I find Primary Key from an Entity Framework Core 2 Database Scaffold using Reflection .dll?

We need to find Primary Key Member given an EntityName, and return Primary Key Member as string. Conducted Reverse Scaffolding on Sql Server database .

modelBuilder.Entity<Product>(entity =>
{
    entity.HasKey(e => e.ProductInfoId)
     .HasName("PK_ProductInfoId");

Attempting Solution:

Trying to write next line code, given string Entity Name: "Product".

var assembly = Assembly.LoadFrom(@"C:\OnlineShoppingStore\bin\Debug\netcoreapp2.2\OnlineShoppingStore.dll");

assembly.GetTypes().Where(d=>d.Name = "OnlineStoreDbContext")

etc GetProperties().Where(e=>e.Name == "Product"))

Other Resource:

Prefer to do this with Reflection, rather than instantiating context since this is for code generation tool, will conduct for 10 db projects.

Generic Repository in C# Using Entity Framework

  • 1
    Don't you have/can't you obtain `DbContext` derived class instance? The information you are asking for is provided by EF Core metadata API which requires `DbContext` instance. Like here https://stackoverflow.com/questions/55867725/generic-repository-in-c-sharp-using-entity-framework/55880724#55880724 – Ivan Stoev Dec 30 '19 at 10:40
  • provided reply above –  Dec 30 '19 at 11:03

1 Answers1

3

Expanding on suggestions from comments, you can instantiate the context in your code and invoke all EF model inspection APIs as outlined in Ivan's answer like so:

var assembly = Assembly.LoadFrom(@"C:\OnlineShoppingStore\bin\Debug\netcoreapp2.2\OnlineShoppingStore.dll");
var contextType = assembly.GetTypes().First(d => d.Name == "OnlineStoreDbContext");
var ctx = Activator.CreateInstance(contextType) as DbContext; // instantiate your context. this will effectively build your model, so you must have all required EF references in your project
var p = ctx.Model.FindEntityType(assembly.GetTypes().First(d => d.Name == "Product")); // get the type from loaded assembly
//var p = ctx.Model.FindEntityType("OnlineStoreDbContext.Product"); // querying model by type name also works, but you'd need to correctly qualify your type names
var pk = p.FindPrimaryKey().Properties.First().Name; // your PK property name as built by EF model

UPD: Forget thought experiments, it clearly is confusing. The method above does not require you to reference your other projects and requires no prior knowledge of types except for string names (which you have as it seems). When you instantiate your db context, the base class constructor in EF will process all your custom overrides and will return you a fully built model (i.e. all properties will be accounted for). Again, no reference will be required as long as you only use EF metadata API (which is available on base DBContext class). Hopefully that clarifies.

UPD2: following up on my IL Emit idea in comments. See the implementation and a bit more background in my blog. This enabled me to eliminate the exception (still have to have at least one DB Provider available though).

timur
  • 14,239
  • 2
  • 11
  • 32
  • I outlined an alternative that might work for you (I believe with caveats). Is there a reason you can't just instantiate the contexts? Since it's a code generator performance is probably not a concern? – timur Dec 30 '19 at 20:17
  • Neither of the outlined ways require you to have any references to your assemblies. I probably have not pointed it out well enough. The only reference you will need it the reference to EF core nuget package itself. Your own assemblies can be traversed with no strong dependency or referencing whatsoever. – timur Dec 30 '19 at 20:19
  • Check out the code above. It instantiates the context and lets you query the model as built. You still need no reference to your other assembly – timur Dec 30 '19 at 21:05
  • Okay I think I confused you. Let me update the answer and see if that clarifies a bit. – timur Dec 30 '19 at 21:10
  • receiving error on FindEntityType, System.InvalidOperationException: 'No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions object in its constructor and passes it to the base constructor for DbContext.'rd –  Dec 31 '19 at 03:25
  • kind of wish I could find properties without instantiating this whole thing –  Dec 31 '19 at 03:26
  • This is a bit crazy approach, but you could potentially use Emit IL to dynamically inherit from your db contexts and inject your own minimal OnConfiguring override so you have easier time instantiating your models. This is a lot of pain to set up however. – timur Dec 31 '19 at 23:58