当我有一个实体时,动态地在DbContext中查找指定的通用DbSet

Mas*_*oud 29 c# generics entity-framework code-first dbset

我有以下课程DbContext:

public class Order:BaseEntity
{
   public Number {get; set;}
}
Product:BaseEntity;
{
  public Name {get; set;} 
}

public class Context : DbContext
{
    ....
    public DbSet<Order> Orders { set; get; }
    public DbSet<Product> Products { set; get; }
    ....
}   
Run Code Online (Sandbox Code Playgroud)

我有一个想要添加到我的上下文的对象列表,但我不知道如何DbSet根据每个实体类型动态地找到合适的泛型.

IList<BaseEntity> list = new List<BaseEntity>();
Order o1 = new Order();
o1.Numner = "Ord1";
list.Add(o1);

Product p1 = new Product();
p1.Name = "Pencil";
list.Add(p1);

Context cntx = new Context();  
foreach (BaseEntity entity in list)
{
      cntx.Set<?>().Add(entity);         
}
Run Code Online (Sandbox Code Playgroud)

我怎样才能做到这一点?

Pab*_*meo 42

DbContext有一个名为的方法Set,您可以使用它来获取非泛型的方法DbSet,例如:

var someDbSet = this.Set(typeof(SomeEntity));
Run Code Online (Sandbox Code Playgroud)

所以在你的情况下:

foreach (BaseEntity entity in list)
{
      cntx.Set(entity.GetType()).Add(entity);         
}
Run Code Online (Sandbox Code Playgroud)

  • 我不确定你打算如何使用它,因为你只知道编译时的基类型,但是FYI,还有一个Set <T>()方法返回DbSet <T>. (8认同)

use*_*326 18

问题没有指定EF版本,并且建议的答案不再适用于Entity Framework Core(DbContext在EF核心中至少在此答案的日期没有非通用的set方法).

然而,你仍然可以使用Jon Skeet的答案来解决这个问题.为方便起见,我的代码在下面添加.

**更新:添加了泛型函数调用以及返回IQueryable <T>,这要归功于Shaddix的评论.

public static IQueryable Set(this DbContext context, Type T)
{
    // Get the generic type definition
    MethodInfo method = typeof(DbContext).GetMethod(nameof(DbContext.Set), BindingFlags.Public | BindingFlags.Instance);

    // Build a method with the specific type argument you're interested in
    method = method.MakeGenericMethod(T);

    return method.Invoke(context, null) as IQueryable;
}

public static IQueryable<T> Set<T>(this DbContext context)
{
    // Get the generic type definition 
    MethodInfo method = typeof(DbContext).GetMethod(nameof(DbContext.Set), BindingFlags.Public | BindingFlags.Instance);

    // Build a method with the specific type argument you're interested in 
    method = method.MakeGenericMethod(typeof(T)); 

    return method.Invoke(context, null) as IQueryable<T>;
} 
Run Code Online (Sandbox Code Playgroud)

  • 第二种方法超出了范围,因为原始 Set 方法已经是泛型类型。此外,原始 Set 方法返回一个 DbSet&lt;T&gt; 对象。我不明白如何使用这些扩展。 (4认同)
  • 但这返回一个IQueryable而不是添加到上下文所需的DbSet。 (3认同)

ser*_*kov 10

为了避免错误“System.Reflection.AmbigouslyMatchException:'发现不明确的匹配。'”我使用了以下版本:

    public static IQueryable Set(this DbContext context, Type T)
    {
        var method = typeof(DbContext).GetMethods().Single(p =>
            p.Name == nameof(DbContext.Set) && p.ContainsGenericParameters && !p.GetParameters().Any());
                               
        // Build a method with the specific type argument you're interested in
        method = method.MakeGenericMethod(T);

        return method.Invoke(context, null) as IQueryable;
    }
Run Code Online (Sandbox Code Playgroud)


小智 5

不幸的是,以下建议的版本自 .NET Core 3.0 起不起作用。你仍然可以得到一个IQueryable回报,但你不能再把它投射到DbSet了。

IQueryable<TEntity> as DbSet<TEntity> => null
Run Code Online (Sandbox Code Playgroud)

更糟糕的是,从 EF Core 3.0 开始,新方法FromSqlRawFromSqlInterpolated方法(替换FromSql)只能在查询根上指定,即直接在 上DbSet<>而不是在 上IQueryable。尝试在其他任何地方指定它们将导致编译错误。

https://github.com/dotnet/efcore/issues/15704#issuecomment-493230352