.net 使用EF Core的泛型类构建表达式树

vlf7wbxs  于 2022-12-30  发布在  .NET
关注(0)|答案(1)|浏览(211)

我有一个TPH架构,它具有以下层次结构:

abstract class Task
{
   public int A { get; set; }
}

abstract class DocumentTask
{
   public virtual Document Doc { get;set; }
   public int B { get; set; }
}

class DocumentTask<TDocument> : DocumentTask
   where TDocument : Document, new()
{
   public override TDocument Doc { get; set; }
}

/* Some classes are inherited from DocumentTask<TDocument> */

class SignTask<TDocument> : DocumentTask<TDocument>
  where TDocument : Document, new()
{
   public int C { get;set; }
}

我需要从指定C的数据库中获取所有的SignTask,但是由于我没有非泛型的SignTask,所以我不能做类似db.Where(t => t is SignTask && ((SignTask)t).C == 5)的事情,这就是为什么我决定直接构建一个表达式树,它只用于获取SignTask〈〉类型的实体:

var taskTypes = typeof(SignTask<>).Assembly
        .GetTypes()
        .Where(t => 
            (t?.BaseType?.IsGenericType ?? false) 
            && t?.BaseType?.GetGenericTypeDefinition() == typeof(SignTask<>)).ToList();

var parameter = Expression.Parameter(typeof(Task), "e");
    var body = taskTypes.Select(type => Expression.TypeIs(parameter, type))
        .Aggregate<Expression>(Expression.OrElse);

var predicate = Expression.Lambda<Func<Task, bool>>(body, parameter);

var tasks = await _dbContext
        .Tasks
        .Where(predicate)
        .ToArrayAsync();

但是我不能通过C属性过滤它,因为我不能为没有指定泛型参数的泛型类型构建Expression.Parameter

ars1skjm

ars1skjm1#

如果DocumentTask派生自Task,则可以执行以下操作:

var parameter = Expression.Parameter(typeof(Task), "e");
var valueExpr = Expression.Constant(c);
var body = taskTypes.Select(type => Expression.AndAlso(Expression.TypeIs(parameter, type),
    Expression.Equal(
      Expression.Property(Expression.Convert(parameter, type), nameof(SignTask<Document>.C)),
      valueExpr)))
  .Aggregate<Expression>(Expression.OrElse);

var predicate = Expression.Lambda<Func<Task, bool>>(body, parameter);

相关问题