43

我有以下课程和DbContext

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

public class Context : DbContext
{
    ....
    public DbSet<Order> Orders { set; get; }
    public DbSet<Product> Products { set; get; }
    ....
}   

我也有一个想要添加到我的上下文中的对象列表,但我不知道如何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);         
}

我怎样才能做到这一点?

4

6 回答 6

60

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

var someDbSet = this.Set(typeof(SomeEntity));

所以在你的情况下:

foreach (BaseEntity entity in list)
{
      cntx.Set(entity.GetType()).Add(entity);         
}
于 2014-02-03T17:31:23.960 回答
33

该问题未指定 EF 版本,并且建议的答案不再适用于 Entity Framework Core(在 EF Core 中,至少在此答案的日期DbContext没有非泛型方法)。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>;
} 
于 2017-11-24T00:22:10.887 回答
5

为避免错误“System.Reflection.AmbiguousMatchException: 'Ambiguous match found.'”我使用了以下版本:

    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;
    }
于 2019-12-27T05:39:47.257 回答
5

不幸的是,以下提议的版本自 .NET Core 3.0 起就不起作用。你仍然可以得到一个IQueryable背部,但你不能再施放它DbSet了。

IQueryable<TEntity> as DbSet<TEntity> => null

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

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

于 2020-01-24T10:23:43.643 回答
0

在 EF Core 中,我们只有具有必须是 class 类型的约束Set<TEntity>()的方法,因此这可以防止您意外传递 EF 无法找到任何现有 DbSet 的接口类型。TEntity

因此,如果可能的话,您始终可以将泛型类型参数限制为与Set<TEntity>()要求相同,例如这将不起作用:

public void GetDbSet<T>(DbContext db) {
    db.Set<T>()
//     ~~~~~~~~
// Error CS0452 The type 'T' must be a reference type in order to use it as parameter 'TEntity' in the generic type or method 'DbContext.Set<TEntity>()'
}

但这将:

public void GetDbSet<T>(DbContext db) where T : class {
    db.Set<T>()
}
于 2022-02-14T23:20:52.783 回答
-3

除了 Pablo 的回答,您还可以在项目中添加一个类:

namespace System.Data.Entity
{
    public static class EntityFrameworkExtensions
    {
        public static IEnumerable<object> AsEnumerable(this DbSet set)
        {
            foreach (var entity in set)
            {
                yield return entity;
            }
        }
    }
}

此类AsEnumerableDbSet实例添加扩展方法。

当您想使用它时,例如Count或过滤一行:

var someDbSet = this.Set(typeof(SomeEntity));
var count = (from a in someDbSet.AsEnumerable() select a).Count();
于 2017-07-07T06:35:05.680 回答