返回要在 EntityFrameworkCore 中使用的 Class 类型

Returning a Class Type to be used in EntityFrameworkCore

我在数据库中有很多 table,我正在使用 Entity Framework Core 将我的应用程序连接到它们。我有大约 150 个 table,它们都具有完全相同的结构(每个都具有相同的列)。每个 table 通过 table 的命名方式来标识它包含的数据,例如table_grade1table_grade2table_grade3

现在我知道我可以创建一个带有额外列(grade1grade2grade3 等)的单个 table 来标识内容数据,并且将所有数据放入 table。但是目前有很多其他应用程序访问此数据库,更不用说电子表格查询了。

理想情况下,我想做的是编写一个采用字符串参数(table 名称,例如 "table_grade1")和 returns EF class 的方法因此我可以在实施 Repository Pattern 时使用它来创建数据存储库。 所以理想情况下是这样的:

var EFClass = GetMyEF("table_grade1")

然后我可以在从 UnitOfWork class -

创建我的 Repository 时使用这个对象
MyRepository repo = UnitOfWork.GetRepo<EFClass>();

这将 return 具有 DbSet 的存储库,Entity Framework 将调用 TableGrade1 并将使用代码 -

returned
MyRepository repo = UnitOfWork.GetRepo<TableGrade1>();

我已经使用方法从字符串 return class Type 但我不能使用这些 class Type 到 return 回购协议。我还创建了 class 的实例,但无法使用这些对象创建存储库。

我不知道我想做的事情是否可行,但我希望如此,否则我可能不得不在我的 UnitOfWork class 中写一个 switch 语句到 return 正确的回购协议 - 大约有 150 个案例 - 如果可能的话,我试图避免这种情况,因为我希望将此开放给数据库中 table 名称的未来更改并编写 switch 语句意味着我有每次数据库更改时都需要更新两个项目,而不是一个。

我在这里添加了更详细的信息:

所以数据库有大约 150 tables,每个都有相同的列,但价格是针对不同资产的,每个 table 都有列 published_date,pricing_date,价格,relative_month。列出的表类似于以下内容 table_grade1, table_grade2, table_grade3, table_grade4, 等等..

我过去只使用优秀的 Npgsql nuget 包来访问 tables,因此可以有一个字符串列表,可以在运行时使用数据库中的 table 名称读取时间(因为如果开始在数据库中跟踪另一个资产 class,可能会添加 tables)并且 table 名称可以插入到 SQL 语句中一个参数化查询,用于获取对 tables.

当前列表的动态引用

我的主程序里有

   static void Main()
    {
        var uow = new UnitOfWork();
        var type = Type.GetType("table_grade1"); 
        uow.TheType = type;
        var dynamicRepo = uow.GetForwardRepo(type);

    }

在工作单元中

    private DbContext _dbContext = new MyDBContext();
    public Type TheType { get; set; }

    public MyRepository<T> GetForwardRepo<T>() where T : class
    {
        return new MyRepository<T>(_dbContext);
    } 

    public bool SetTheType(string TableName)
    {
        TheType = GetTypeFromTableName(TableName);
        if (TheType != null) return true;
        return false;
    }

    public Type GetTypeFromTableName(string TableName)
    {
        string derivedTableName = GetEFClassNameFromTableName(TableName);
        
        Type returnType = Type.GetType(derivedTableName);
        return returnType;
    }

MyRepository 是 Type T(EntityFramework 创建的 class 派生自 DB tables)的标准存储库,具有 CRUD 操作。

public class MyRepository<T> : IMyRepository<T> where T : class
{
    DbSet<T> _dbSet;
    private DbContext _dbContext;

    public MyRepository(DbContext dbContext)
    {
        _dbContext = dbContext;
        _dbSet = _dbContext.Set<T>();
    }
}

`

我唯一没有在此处包括的是 GetEFClassNameFromTableName 方法,因为它只采用 table 名称并将字符串转换为 EF DBSet 名称(删除下划线,添加大写删除栏杆's'等

我最终想做的是启用某种方法来获取数据库 table 名称的字符串(例如“table_gradeX”)并将其用于 return 存储库使用 UnitOfWork 中来自 EntityFramework 的那个 table 的 DBSet。我不确定是否需要辅助方法(将字符串转换为 class 的类型或实例或其他)我不确定,但这是我的最终目标。

谢谢你看这个,我很感激。

ps 我知道在 UnitOfWork 中使用 属性 TheType 对这个程序没有任何意义,但是一旦我弄清楚如何从字符串.

如果您需要使用动态类型创建存储库,此代码即可。

Type entityType = Type.GetType("table_grade1"); // Get Entity Type
if (entityType == null) throw new Exception();

Type repoType = typeof(MyRepository<>); //Get Repository Type

Type genericRepository = repoType.MakeGenericType(entityType); // Make <T> to repoType
var dynamicRepo = Activator.CreateInstance(genericRepository, new object[] { context }); // Repository, Params

记住 entityType 需要存在于上下文实体映射 (DbSet)

编辑

如果您需要调用方法,此代码可以提供帮助。

                                                        // Method Name, Type Params
MethodInfo method = genericRepository.GetType().GetMethod("MethodName", new Type[] { });
entities = (IEnumerable<Entity>)method.Invoke(genericRepository, new object[] { });
                                                                // Method Params

在这种情况下,我将 Method.Invoke 的 return 投射到超级列表 Class。

好的,所以我希望能够从数据库中可能的 150 个 table 之一传递一个字符串和 return 数据。所有 table 都具有相同的结构,包括 4 列。理想情况下,这将使用存储库模式来完成。我的第一次尝试是将数据作为 DbSet 来处理,因此尝试 return 使用字符串参数的特定 DbSet 以允许不需要更改代码的新 tables。我不想使用 switch 语句或类似于 return 正确的 Repo。我不得不改变我的方法,因为如果不专门转换为特定的 DbSet,似乎不可能将一个字符串传递给 return 包含其中数据的存储库并能够使用它。相反,我将创建一个通用 Class 列表,我创建时具有与 table 相同的属性。然后,我使用 SQL 检索数据,并使用 Manish Banga 在 donetstudy.com 上发布的解决方案,利用他的 MapToList 方法可以将 returned table 转换为我的通用列表 Class 类型。 我在我的 GenericRepository

中有这个方法
private bool GetForwardDbSet(string tableName)
{
    if (tableName.Substring(0, 2) != "z_") return false;
    Regex regEx = new Regex("z_");
    var tableList = _dbContext.Model.GetRelationalModel().Tables.Select(c => c.Name).Where(c => regEx.IsMatch(c)).ToList();
    if (!tableList.Contains(tableName)) return false;

    using (var command = _dbContext.Database.GetDbConnection().CreateCommand())
    {
        command.CommandText = $"SELECT * FROM {tableName};";
        _dbContext.Database.OpenConnection();
        using (var result = command.ExecuteReader())
        {
            // do something with result
            _dbSet = result.MapToList<ForwardDbSet>();
        }
    }

    if (_dbSet.Count > 0) return true;

    return false;
}

该方法会进行一些检查以确保传入的字符串的格式适合我要定位的 table,我会检索这些目标 table 名称的列表,然后检查字符串是否在列表中。然后我 运行 一个 SQL 命令到 return 数据库中的数据并将此数据映射到一个通用 class 类型的列表我调用 ForwardDbSet,(数据库集在这里的名称不是实际的 DbSet 类型,它们是我尝试转换为真正的 DbSet 类型时的后遗症,这只是我用数据库中 table 的属性创建的 class return编)。我将此列表存储在变量 _dbSet 中(同样不是实际的 DbSet 类型),然后使用 rreturn 对其进行处理。 由于此数据是只读的,因此我不需要跟踪它,因为一旦读取数据,我将不会执行任何创建、更新或删除功能。

我还在存储库中创建了一些辅助函数来检查 _dbSet 对象是否填充了价格并用于处理数据的子集,但这是我的应用程序特有的,与此解决方案无关。 所以这是我的解决方法,感谢所有帮助来到这里的人,尤其是@Joao-Victor-de-Paula-Ramos。