c# EF DbContext - 从字符串动态生成对象
本文关键字:动态 对象 字符串 EF DbContext | 更新日期: 2024-10-30 17:05:58
我想创建一个工厂类来返回DbContext
对象,其中表名将作为字符串传递。数据库中有 100 多个表,每个表都有不同的结构/模式。
这个想法是在方法中string
传递tableName
,该方法将在 EF 中返回对象,我们可以选择/更新这些记录。我在某篇文章中找到了这段代码,但对如何使用它有些困惑:
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 一样使用代码生成器为你做这件事。