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



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

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

理想情况下,我想做的是编写一个方法,该方法采用字符串参数(表名,例如"table_grade1")并返回EF类,这样我就可以在实现Repository Pattern时使用它来创建数据存储库。所以理想情况下是这样的:

var EFClass = GetMyEF("table_grade1")

然后我可以在从UnitOfWork类-创建Repository时使用此对象

MyRepository repo = UnitOfWork.GetRepo<EFClass>();

这将返回带有DbSet的存储库,实体框架将称为TableGrade1,并将使用代码-返回

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

我已经使用方法从字符串中返回类Type,但我不能使用这些类Type来返回repo。我也创建了类的实例,但不能使用这些对象来创建repo。

我不知道我想做的事情是否可能,但我希望是这样,否则我可能不得不在UnitOfWork类中写一个switch语句来返回正确的repo(大约有150个案例)。如果可能的话,我会尽量避免这种情况,因为我希望这对数据库中表名的未来更改开放,写switch语句意味着我有两个项目每次数据库发生更改时都需要更新。

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

因此,数据库有大约150个表,每个表都有相同的列,但价格是针对不同的资产的,每个表有published_date、pricing_date、price、relative_month列。列出的表格与以下表格类似table_grade1,table_grade2,table_grade3,table_grade4,等等。

我过去只使用出色的Npgsql-nuget包来访问表,因此可以有一个字符串列表,这些字符串可以在运行时与当时数据库中的表名一起读取(因为如果数据库中开始跟踪另一个资产类,则可能会添加表),并且表名可以作为参数化查询插入SQL语句中,以获得对表的当前列表。

我的主程序中有

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

UnitOfWork

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是具有CRUD操作的Type T(EntityFramework创建的从DB表派生的类)的标准存储库。

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方法,因为它只接受表名并将字符串转换为EF DBSet名称(删除下划线,加上资本化删除railing's'等)。

我最终要做的是启用某种方式,为DB表名取一个字符串(例如"table_gradeX"),并使用它从UnitOfWork中的EntityFramework返回一个包含该表DBSet的存储库。我不确定一路上是否需要helper方法(将字符串转换为类的类型或实例或其他什么),但这是我的最终目标。

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

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的返回。

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

该方法会进行一些检查,以确保传入的字符串的格式与我想要的目标表相匹配,我会检索这些目标表名的列表,并检查字符串是否在列表上。然后,我运行一个SQL命令从数据库中返回数据,并将此数据映射到一个我称之为ForwardDbSet的泛型类类型的列表中(此处名称中的dbset不是实际的dbset类型,它们是我尝试转换为真正的dbset型型时的遗留问题,这只是我创建的一个类,返回的是数据库中表的属性)。我将此列表存储在变量_dbSet中(同样不是实际的dbSet类型),然后将其重新转换为可使用的。由于此数据是只读的,我不需要跟踪它,因为一旦读取数据,我将不会执行任何创建、更新或删除功能。

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

最新更新