【问题标题】:Casting generic type to build a valid Linq expression转换泛型类型以构建有效的 Linq 表达式
【发布时间】:2019-09-02 17:02:51
【问题描述】:

我在尝试泛型转换或构建可以转换为所需泛型类型T 的 LINQ 表达式时遇到了困难。

这是失败的例子:

public override void PreprocessFilters<T>(ISpecification<T> specification)
{
    if (typeof(ICompanyLimitedEntity).IsAssignableFrom(typeof(T)))
    {
        var companyId = 101; // not relevant, retrieving company ID here

        // and now I need to call the method AddCriteria on the specification
        // as it were ISpecification<ICompanyLimitedEntity>
        // which it should be because at this point I know 
        // that T is ICompanyLimitedEntity
        var cle = specification as ISpecification<ICompanyLimitedEntity>;
        //                      ^-- but of course this conversion won't work, cle is null

        // what should I do now?

        // I need to call it like this:
        cle.AddCriteria(e => e.CompanyId == null ||
            e.CompanyId == 0 || e.CompanyId == companyId);
        // BTW, AddCriteria receives Expression<Func<T, bool>> as an argument

        // tried nasty casts inside of the expression - this doesn't throw NullReference
        // but it doesn't translate to SQL either - gets evaluated locally in memory, which is bad
        specification.AddCriteria(e => ((ICompanyLimitedEntity)e).CompanyId == null ||
            ((ICompanyLimitedEntity)e).CompanyId == 0 || 
            ((ICompanyLimitedEntity)e).CompanyId == companyId);

        // this one also doesn't work

        Expression<Func<ICompanyLimitedEntity, bool>> lex = e => e.CompanyId == null ||
                    e.CompanyId == 0 || e.CompanyId == companyId;

        // it's null again -----------v
        specification.AndCriteria(lex as Expression<Func<T, bool>>);
    }
}

有什么方法可以转换 T 或构建一个 Linq 表达式,将 T 视为 ICompanyLimitedEntity 并在 SQL 服务器上执行查询?

【问题讨论】:

  • 你试过public override void PrerocessFilters&lt;T&gt;(ISpecification&lt;T&gt; specification) where T : ICompanyLimitedEntity吗?这也使得IsAssignableFrom 检查变得多余。
  • @Funk 这将是最好的解决方案,如果 T 始终是 ICompanyLimitedEntity,但不能保证是。

标签: c# linq generics casting


【解决方案1】:

以下是构建所需表达式的方法。

首先你用接口类型参数创建编译时表达式

Expression<Func<ICompanyLimitedEntity, bool>> exprI = e => e.CompanyId == null ||
            e.CompanyId == 0 || e.CompanyId == companyId;

然后用 T 类型的新参数替换主体内的参数,并将修改后的表达式用作新 lambda 表达式的主体:

var parameterT = Expression.Parameter(typeof(T), "e");
var bodyT = exprI.Body.ReplaceParameter(exprI.Parameters[0], parameterT);
var exprT = Expression.Lambda<Func<T, bool>>(bodyT, parameterT);

其中ReplaceParameter 是典型的基于ExpressionVisitor 的帮助器,用于将参数替换为另一个表达式:

public static partial class ExpressionUtils
{
    public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
        => new ParameterReplacer { Source = source, Target = target }.Visit(expression);

    class ParameterReplacer : ExpressionVisitor
    {
        public ParameterExpression Source;
        public Expression Target;
        protected override Expression VisitParameter(ParameterExpression node)
            => node == Source ? Target : node;
    }
}

请注意,您仍然需要来自 Why Linq "where" expression after Select gets evaluated locally when created through a generic method? 的成员访问表达式修复程序

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-10-02
    • 1970-01-01
    • 2016-04-27
    • 2012-01-02
    • 1970-01-01
    • 2022-12-13
    • 2012-12-22
    相关资源
    最近更新 更多