返回要在 EntityFrameworkCore 中使用的 Class 类型
Returning a Class Type to be used in EntityFrameworkCore
我在数据库中有很多 table,我正在使用 Entity Framework Core
将我的应用程序连接到它们。我有大约 150 个 table,它们都具有完全相同的结构(每个都具有相同的列)。每个 table 通过 table 的命名方式来标识它包含的数据,例如table_grade1
、table_grade2
、table_grade3
等
现在我知道我可以创建一个带有额外列(grade1
、grade2
、grade3
等)的单个 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。
我在数据库中有很多 table,我正在使用 Entity Framework Core
将我的应用程序连接到它们。我有大约 150 个 table,它们都具有完全相同的结构(每个都具有相同的列)。每个 table 通过 table 的命名方式来标识它包含的数据,例如table_grade1
、table_grade2
、table_grade3
等
现在我知道我可以创建一个带有额外列(grade1
、grade2
、grade3
等)的单个 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 并将使用代码 -
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。