c# EF DbContext - 从字符串动态生成对象
c# EF DbContext - generate object dynamically from string
我想创建一个工厂 class 到 return DbContext
对象,其中 table 名称将作为字符串传递。数据库中有 100 多个 table,每个都有不同的 structure/schema。
想法是将 tableName
作为 string
在将 return 对象放入 EF 的方法中传递,我们可以 select/update 这些记录。我在某篇文章中找到了这段代码,但对如何使用它有些困惑:
public ObjectContext Context(EntityObject entity)
{
var relationshipManager = ((IEntityWithRelationships)entity).RelationshipManager;
var wrappedOwnerProperty = relationshipManager.GetType().GetProperty("WrappedOwner", BindingFlags.Instance | BindingFlags.NonPublic);
var wrappedOwner = wrappedOwnerProperty.GetValue(relationshipManager);
var contextProperty = wrappedOwner.GetType().GetProperty("Context");
return (ObjectContext)contextProperty.GetValue(wrappedOwner);
}
我不确定这是否是我需要的。另外,我应该在 EntityObject entity
中传递什么以及我应该在何处传递 tableName
?
如果有任何其他方法可以实现同样的事情,请告诉我。
一个简单的方法是使用 DbContext.Set() 方法,并根据字符串获取类型。
using (var db = new MyDbContext)
{
string tableName = "ApplicationUser";
var type = Assembly.GetExecutingAssembly()
.GetTypes()
.FirstOrDefault(t => t.Name == tableName);
if(type != null)
DbSet catContext = context.Set(type);
}
但是,这有一个缺点,那就是这是一个非通用 DbSet(即它是 DbSet
而不是 DbSet<T>
)。
获取通用 DbSet(允许 linq 功能)的一种方法是执行如下操作:
using (var db = new IdentityDbContext())
{
string tableName = "ApplicationUser";
var type = Assembly.GetExecutingAssembly()
.GetTypes().FirstOrDefault(t => t.Name == tableName);
var method = db.GetType().GetMethods()
.First(x => x.IsGenericMethod && x.Name == "Set");
MethodInfo generic = method.MakeGenericMethod(type);
var set = generic.Invoke(db, null);
}
当然 set 在这里将是一个对象,您必须以某种方式转换它,这仍然是问题的一部分。
归根结底,如果你不打算使用静态编译类型,你将不得不继续处理反射,特别是当你有泛型类型要处理时(即 DbSet<T>
,等等)。您必须在某个时候将它们强制转换为静态类型才能调用这些方法,或者继续执行 MethodInfo.Invoke。
另一种选择是使用动态,但您不能将动态与 C# 扩展方法一起使用(不强制转换为具体类型),因此您又回到了没有 Linq 支持的同一条船上。
通过反射使用 Linq 是一个巨大的痛苦。
老实说,如果你有 100 个 类,我会硬着头皮写硬编码类型,或者像 CodeSmith 一样使用代码生成器为你做。
我想创建一个工厂 class 到 return DbContext
对象,其中 table 名称将作为字符串传递。数据库中有 100 多个 table,每个都有不同的 structure/schema。
想法是将 tableName
作为 string
在将 return 对象放入 EF 的方法中传递,我们可以 select/update 这些记录。我在某篇文章中找到了这段代码,但对如何使用它有些困惑:
public ObjectContext Context(EntityObject entity)
{
var relationshipManager = ((IEntityWithRelationships)entity).RelationshipManager;
var wrappedOwnerProperty = relationshipManager.GetType().GetProperty("WrappedOwner", BindingFlags.Instance | BindingFlags.NonPublic);
var wrappedOwner = wrappedOwnerProperty.GetValue(relationshipManager);
var contextProperty = wrappedOwner.GetType().GetProperty("Context");
return (ObjectContext)contextProperty.GetValue(wrappedOwner);
}
我不确定这是否是我需要的。另外,我应该在 EntityObject entity
中传递什么以及我应该在何处传递 tableName
?
如果有任何其他方法可以实现同样的事情,请告诉我。
一个简单的方法是使用 DbContext.Set() 方法,并根据字符串获取类型。
using (var db = new MyDbContext)
{
string tableName = "ApplicationUser";
var type = Assembly.GetExecutingAssembly()
.GetTypes()
.FirstOrDefault(t => t.Name == tableName);
if(type != null)
DbSet catContext = context.Set(type);
}
但是,这有一个缺点,那就是这是一个非通用 DbSet(即它是 DbSet
而不是 DbSet<T>
)。
获取通用 DbSet(允许 linq 功能)的一种方法是执行如下操作:
using (var db = new IdentityDbContext())
{
string tableName = "ApplicationUser";
var type = Assembly.GetExecutingAssembly()
.GetTypes().FirstOrDefault(t => t.Name == tableName);
var method = db.GetType().GetMethods()
.First(x => x.IsGenericMethod && x.Name == "Set");
MethodInfo generic = method.MakeGenericMethod(type);
var set = generic.Invoke(db, null);
}
当然 set 在这里将是一个对象,您必须以某种方式转换它,这仍然是问题的一部分。
归根结底,如果你不打算使用静态编译类型,你将不得不继续处理反射,特别是当你有泛型类型要处理时(即 DbSet<T>
,等等)。您必须在某个时候将它们强制转换为静态类型才能调用这些方法,或者继续执行 MethodInfo.Invoke。
另一种选择是使用动态,但您不能将动态与 C# 扩展方法一起使用(不强制转换为具体类型),因此您又回到了没有 Linq 支持的同一条船上。
通过反射使用 Linq 是一个巨大的痛苦。
老实说,如果你有 100 个 类,我会硬着头皮写硬编码类型,或者像 CodeSmith 一样使用代码生成器为你做。