C# EF Core:如何以编程方式访问数据库集以用于种子目的

C# EF Core:如何以编程方式访问数据库集以用于种子目的,c#,entity-framework,entity-framework-core,seeding,C#,Entity Framework,Entity Framework Core,Seeding,在EF Core 3.1+中,我想使用seed()扩展方法以编程方式从任何DbContext中为所有数据库集设置种子,并使用数据。Seed()方法在非动态情况下运行良好 对于手动解决方案,我可以添加代码(可以为每个实体添加记录),如: 对于适用于任何DbContext的动态解决方案,我的方法是这样的代码: var entityTypes = myContext.Model.GetEntityTypes(); foreach (var et in entityTypes) { myCon

在EF Core 3.1+中,我想使用
seed()
扩展方法以编程方式从任何DbContext中为所有数据库集设置种子,并使用数据。
Seed()
方法在非动态情况下运行良好

对于手动解决方案,我可以添加代码(可以为每个实体添加记录),如:

对于适用于任何DbContext的动态解决方案,我的方法是这样的代码:

var entityTypes = myContext.Model.GetEntityTypes();

foreach (var et in entityTypes)
{
    myContext.Set(et.ClrType).ToList().Seed();
}
即引用扩展方法:

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);
    }
}
你知道如何获得对数据库集的通用编程访问,以便向每个数据库集添加数据吗

==编辑==========================================================

我更新了扩展方法,因为我意识到重复是针对返回的多个方法的,所以我让该部分按如下方式工作。此代码正确返回DBSet(排序):


有什么想法吗?

DbContext有多个参数不同的
Set
方法

获得正确的
MethodInfo
的简单方法是创建一个匹配的委托

var method=new Func(_context.Set).method.GetGenericMethodDefinition();
MakeGenericMethod(T).Invoke(…);
当然,如果已经为每种类型定义了DbSet属性,则可以循环这些属性

另一个问题是
set.ToList().Add(…
)。调用
ToList
将整个表加载到一个列表对象中,该对象不知道它来自的数据库集。您需要调用
DbSet.Add
,而不是
list.Add

我会将您的整个seed方法移动到一个泛型方法中,对每个类型进行泛型,然后调用它

private void Seed(DbContext context,T obj)=>
context.Set.Add(obj);
var method=new Action(Seed.method.GetGenericMethodDefinition();
foreach(entityTypes中的变量et)
MakeGenericMethod(et.ClrType).Invoke(…);

对于EF Core来说,这似乎是一件简单的事情,但事实并非如此,也可能不可能

从这一点来看,最近对EF的修改似乎使这一点变得更加困难

如果您使用“DbContent.Set”通过反射跳转得到一个DbSet列表,您会发现这些实际上是“InternalDbSet”类型,并带有一系列关于不应如何使用它们的警告。无法将它们转换到DbSet或像IDbSet这样的接口


但是如果有人有一个好的解决方案,请发帖,因为我现在很想知道是否可以做到。

尝试用
typeof(DbContext)替换
\u context.GetType()
@ivan stoev-谢谢,但是使用typeof(DbContext)我们仍然会得到同样的错误。EF Core 5.0?关于
typeof(DbContext.GetMethod)(nameof(DbContext.Set)呢,键入.EmptyTypes)
然后。关于ToList()的观点很好。我会看一看。谢谢
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);
    }
}
   Categories.AddRange(Categories.ToList().Seed(10));
   Products.AddRange(Products.ToList().Seed(20));
   ...
   SaveChanges();
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);
}
// 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();