[英]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 開始,新的FromSqlRaw
和FromSqlInterpolated
方法(替換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.