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

如果还有其他方法可以实现同样的事情,请告诉我。

c# EF DbContext - 从字符串动态生成对象

执行此操作的一种简单方法是使用 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 一样使用代码生成器为你做这件事。