I'm very new to Entity Framework, so go easy on me, but I would like to keep the fluent API for my models a little closer to usage and put it inside the model object itself. I couldn't find anything in the documentation or via Google search for a built in way to do this, so I thought perhaps I could just call a static method on the model if it exists from the DbContext.OnModelCreating()
method. Here's what I come up with, but it doesn't seem to work whenever I run Add-Migration
, and Script-Migration
, even if I remove all migrations first. Any idea where I'm going wrong on this? or perhaps a better way to do it?
Trademark.cs (the model)
public class Trademark
{
public string Name { get; set; }
// ...
protected static void OnModelCreating( ModelBuilder modelBuilder )
{
modelBuilder.Entity<Trademark>().HasIndex(x=>x.Name).IsUnique();
}
}
PartDbContext.cs (DbContext
)
public class PartDbContext: DbContext
{
// ...
public DbSet<Trademark> Trademarks { get; set; }
public PartDbContext( DbContextOptions options ): base(options)
{
}
protected override void OnModelCreating( ModelBuilder modelBuilder )
{
dynamic m = modelBuilder.Model.GetType().GetMethod("OnModelCreating",System.Reflection.BindingFlags.Static);
if( m != null )
m.OnModelCreating(modelBuilder);
}
}
I'm thinking the issue is that modelBuilder.Model.GetType()
returns typeof(IMutableModel)
and not typeof(Trademark)
. So, perhaps I just need to know how to get the type of the model of the model from modelBuilder
.
Note: If I put modelBuilder.Entity<Trademark>().HasIndex(x => x.Name).IsUnique();
inside PartDbContext.OnModelCreating()
, the unique constraint is added. So, I know I at least got that bit correct.
For anyone else interested in doing this, I decided to narrow the usage down a bit more and only pass the EntityTypeBuilder
instead of the whole ModelBuilder
.
PartDbContext.cs
protected override void OnModelCreating( ModelBuilder modelBuilder )
{
foreach( var entityType in modelBuilder.Model.GetEntityTypes() )
{
var method = entityType.ClrType.GetMethod("OnEntityCreating",BindingFlags.Static|BindingFlags.NonPublic);
if( method != null )
{
var entityBuilder = new object[] {
typeof(ModelBuilder)
.GetMethod("Entity",new Type[]{})
.MakeGenericMethod(entityType.ClrType)
.Invoke(modelBuilder,null)
};
method.Invoke(null,entityBuilder);
}
}
}
Trademark.cs
public class Trademark
{
public string Name { get; set; }
protected static void OnEntityCreating( EntityTypeBuilder<Trademark> entityBuilder )
{
entityBuilder.HasIndex(x=>x.Name).IsUnique();
}
}
You can iterate all discovered entity types and invoke the custom method (if defined) via reflection:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var parameters = new object[] { modelBuilder };
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
var method = entityType.ClrType.GetMethod("OnModelCreating", BindingFlags.Static | BindingFlags.NonPublic);
if (method != null)
method.Invoke(null, parameters);
}
}