簡體   English   中英

當我有一個實體時,動態地在 DbContext 中查找指定的通用 DbSet

[英]Find a specified generic DbSet in a DbContext dynamically when I have an entity

我有以下課程和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);         
}

我怎樣才能做到這一點?

DbContext有一個名為Set的方法,您可以使用它來獲取非泛型DbSet ,例如:

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

所以在你的情況下:

foreach (BaseEntity entity in list)
{
      cntx.Set(entity.GetType()).Add(entity);         
}

該問題沒有指定 EF 版本,並且建議的答案不再適用於 Entity Framework Core(在 EF Core 中, DbContext沒有非通用的Set方法,至少在此答案的日期)。

然而,您仍然可以使用 Jon Skeet 對這個問題的回答來獲得一個有效的擴展方法。 為方便起見,下面添加了我的代碼。

更新:由於來自 Shaddix 的評論,添加了通用函數調用並返回IQueryable<T>

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>;
} 

不幸的是,以下建議的版本自 .NET Core 3.0 起不起作用。 您仍然可以返回IQueryable ,但不能DbSet其轉換為DbSet

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

更糟糕的是,從 EF Core 3.0 開始,新的FromSqlRawFromSqlInterpolated方法(替換FromSql )只能在查詢根上指定,即直接在DbSet<>而不是在IQueryable 嘗試在其他任何地方指定它們將導致編譯錯誤。

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

為了避免錯誤“System.Reflection.AmbiguousMatchException: 'Ambiguous match found.'”我使用了以下版本:

    public static IQueryable Set(this DbContext context, Type T)
    {
        MethodInfo method = typeof(DbContext).GetMethods()
            .Where(p => p.Name == "Set" && p.ContainsGenericParameters).FirstOrDefault();

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

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

在 EF Core 中,我們只有Set<TEntity>()方法,它有一個約束,即TEntity必須是 class 的類型,因此這可以防止您意外傳遞一個 EF 無法找到任何現有 DbSet 的接口類型。

因此,如果可能的話,您始終可以將泛型類型參數限制為與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>()
}

除了 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;
            }
        }
    }
}

此類向DbSet實例添加了擴展方法AsEnumerable

當你想使用它時,例如Count或過濾一行:

var someDbSet = this.Set(typeof(SomeEntity));
var count = (from a in someDbSet.AsEnumerable() select a).Count();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM