In EF Core 3.1+, I would like to seed all DBSets from ANY DbContext with data programmatically with a Seed()
extension method. The Seed()
method works beautifully in a non-dynamic situation.
For the manual solution, I can add code (that works adding records for each entity) like:
Categories.AddRange(Categories.ToList().Seed(10));
Products.AddRange(Products.ToList().Seed(20));
...
SaveChanges();
My approach for the dynamic solution that would work for any DbContext is code like this:
var entityTypes = myContext.Model.GetEntityTypes();
foreach (var et in entityTypes)
{
myContext.Set(et.ClrType).ToList().Seed();
}
That is referencing a extension method:
public static class Queryables
{
public static IQueryable<object> Set(this DbContext _context, Type t)
{
return (IQueryable<object>)_context.GetType()
.GetMethod("Set")
.MakeGenericMethod(t)
.Invoke(_context, null);
}
}
but returns an error:
System.Reflection.AmbiguousMatchException: 'Ambiguous match found.'
This is for a generic solution using EF 3.1+ where I want to loop through and seed whatever DbSets are in the current context.
I can manually add code (that works adding records for each entity) like:
Categories.AddRange(Categories.ToList().Seed(10));
Products.AddRange(Products.ToList().Seed(20));
...
SaveChanges();
Any idea how to get generic programmatic access to the DBSets so that I can add data to each one?
=== Edit ==========================================================
I updated the extension method as I realized that the duplicate was for multiple methods returned, so I got that part working as below. This code returns the DBSet correctly (sort of):
public static IQueryable<object> Set(this DbContext _context, Type T)
{
return (IQueryable<object>)_context.GetType()
.GetMethods()
.First(p => p.Name == "Set" && p.ContainsGenericParameters)
.MakeGenericMethod(T)
.Invoke(_context, null);
}
Now the code runs without throwing an error, but the item is not added.
Simplifying the code so that the Seed() function is not a factor, I decided to test by just adding a single record as below. I can see the DBSet but still no new record.
// Does no throw an error but does not add the item
dynamic newItem = Activator.CreateInstance(et.ClrType);
var set = myContext.Set(et.ClrType);
set.ToList().Add(newItem); // Simplified from .Seed();
Any ideas on this?