[英]Entity Framework: read data from generic table with unknown type column
I am new to Entity Framework, and in my project I have to perform a generic query to retrieve the maximum value of a db column.我是 Entity Framework 的新手,在我的项目中,我必须执行通用查询来检索 db 列的最大值。 Let's say that this is my main method:
假设这是我的主要方法:
long GetMaxValue(string tableName, string columnName)
{
// code here
}
and the SQL equivalent is literally和 SQL 等价的字面意思是
SELECT MAX(<columnName>) FROM <tableName>
I have created a GenericDbEntity
class which contains only a NumericColumn
property representing this column, and in the implementation of OnModelCreating
inside my DbContext
I have this:我创建了一个
GenericDbEntity
类,它只包含一个表示该列的NumericColumn
属性,并且在我的OnModelCreating
内的DbContext
的实现中,我有这个:
modelBuilder.Entity<GenericDbEntity>().ToTable(tableName).HasNoKey();
modelBuilder.Entity<GenericDbEntity>().Property(e => e.NumericColumn)
.HasColumnName(columnName)
.HasConversion(p => (?)p, (? p) => Convert.ToInt64(p));
The question marks inside HasConversion
are because I don't actually know which is the db type of that column (I only know it's numeric), but I want to convert it to long
, since that's the data type of GenericDbEntity.NumericColumn
. HasConversion
中的问号是因为我实际上不知道该列的 db 类型(我只知道它是数字),但我想将其转换为long
,因为那是GenericDbEntity.NumericColumn
的数据类型。 I tried to replace the question marks with object
, but it says:我试图用
object
替换问号,但它说:
The property 'GenericDbEntity.NumericColumn' is of type 'object' which is not supported by the current database provider.
属性“GenericDbEntity.NumericColumn”属于“对象”类型,当前数据库提供程序不支持。 Either change the property CLR type, or ignore the property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
更改属性 CLR 类型,或使用“[NotMapped]”属性或使用“OnModelCreating”中的“EntityTypeBuilder.Ignore”忽略该属性。
Is there an easy way to achieve this?有没有简单的方法来实现这一目标?
PS: I tried also to use dynamic
, both in HasConversion
and as data type of NumericColumn
, but it didn't help. PS:我也尝试在
HasConversion
和NumericColumn
的数据类型中使用dynamic
,但这没有帮助。
Edit: The called compiled query is this one:编辑:调用的编译查询是这个:
private static readonly Func<DatabaseContext, long> getMaxValue =
EF.CompileQuery((DatabaseContext context) =>
context.GenericEntity
.Max(e => e.NumericColumn));
Added solution which generates LINQ query for particular table and column.添加了为特定表和列生成 LINQ 查询的解决方案。 For making it working you have to REMOVE
HasConversion
because properties with converters cannot be used in LINQ queries.为了使其正常工作,您必须删除
HasConversion
,因为不能在 LINQ 查询中使用带有转换器的属性。
For example for call例如调用
var maxValue = context.GetMaxValue("Persons", "id");
Will be generated and executed the following query:将生成并执行以下查询:
var maxValue = (long)context.Set<Person>().Max(e => e.Id);
And implementation和实施
public static class QueryableExtensions
{
public static long GetMaxValue(this DbContext ctx, string tableName, string columnName)
{
var model = ctx.Model;
var entityType = model.GetEntityTypes().FirstOrDefault(et => tableName.Equals(et.GetTableName(), StringComparison.InvariantCultureIgnoreCase));
if (entityType == null)
throw new InvalidOperationException($"Entity for table '{tableName}' not found.");
// GetColumnName() can be obsolete, it depends on EF Core version.
var prop = entityType.GetProperties().FirstOrDefault(p => columnName.Equals(p.GetColumnName(), StringComparison.InvariantCultureIgnoreCase));
if (prop == null)
throw new InvalidOperationException($"Property for column '{tableName}'.'{columnName}' not found.");
var entityParam = Expression.Parameter(entityType.ClrType, "e");
var ctxParam = Expression.Parameter(typeof(DbContext), "ctx");
// ctx.Set<entityType>()
var setQuery = Expression.Call(ctxParam, nameof(DbContext.Set), new[] {entityType.ClrType});
Expression propExpression;
if (prop.PropertyInfo == null)
{
// 'prop' is Shadow property, so call via EF.Property(e, "name")
propExpression = Expression.Call(typeof(EF), nameof(EF.Property), new[] { prop.ClrType },
entityParam, Expression.Constant(prop.Name));
}
else
{
propExpression = Expression.MakeMemberAccess(entityParam, prop.PropertyInfo);
}
// e => e.Prop
var propLambda = Expression.Lambda(propExpression, entityParam);
// ctx.Set<entityType>().Max(e => e.Prop)
Expression maxCall = Expression.Call(typeof(Queryable), nameof(Queryable.Max),
new[] { entityType.ClrType, prop.ClrType },
setQuery, propLambda);
// cast if needed
if (maxCall.Type != typeof(long))
maxCall = Expression.Convert(maxCall, typeof(long));
// ctx => ctx.Set<entityType>().Max(e => e.Prop)
var maxLambda = Expression.Lambda<Func<DbContext, long>>(maxCall, ctxParam);
// probably we can cache such compiled retrieving function
var func = maxLambda.Compile();
return func(ctx);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.