【问题标题】:How to get Expression for Nullable values ( fields ) without converting from Expression.Convert in C#?如何在不从 C# 中的 Expression.Convert 转换的情况下获取可为 Nullable 值(字段)的表达式?
【发布时间】:2015-02-12 09:29:26
【问题描述】:

我在动态查询表达式的形成过程中处理 Nullable 类型的扫描。这些表达式将从任何 SQL 表中获取过滤后的数据(使用 EF 与 Code First 类交互)。

我有正常的对象(例如,在多个属性上运行的寄售以及可为空的属性)。

在遇到一些 Nullable 类型之前,我的表达式形成很顺利。在这些可空值上,我得到了

没有为“System.Nullable`1[System.Single]”和“System.Single”类型定义二元运算符 NotEqual。

为了消除此异常,我使用了所有关于在不同线程上发布的转换的方法。

Invoking lambda expressions in Expression trees

Trying to filter on a Nullable type using Expression Trees

这些都是生成带有添加单词“Convert”(即 Convert(someValue) )的表达式,结果我总是有表达式

t=>(t.Consignment.Id = 45000 && t.Consignment.someProperty>=45 Or t.Consignment.Weight! = Convert(5000)).

当然,我需要上面的整个表达式,而不需要“转换”。因为这个“转换”不会相应地从表中获取数据。

任何帮助将不胜感激!还应该做什么?我已经知道转换,但这会使整个表达式无用,因为由于不必要的“转换”,它不会投影记录

已添加

   Expression NotEqual<T>(Expression PropertyType, ConstantExpression a_Constant, ParameterExpression parameter)
   {
    if(IsNullableType(Property.Type) &&!IsNullableType(a_Constant.Type))
    {
      var converted = a_Constant.Type != Property.Type ?  (Expression)Expression.Convert(a_Constant, Property.Type): (Expression)a_Constant;

     // here above statement returns (Convert(50000)) and all I want (50000), but i tried all combinitions from Expression in order to form this constant as expression, it always throws exception what I mentioned originally.

     var body = Expression.MakeBinary(ExpressionType.NotEqual, PropertyType,  converted);

    //MakeBinary statement returns {(t.Weight != Convert(5000000))} but I    need {(t.Weight != 5000000)}

     var expr = Expression.Lambda<Func<T, bool>>(body, parameter);
     return expr;
    }
  }

代码:

public class Consignment
{
    public float? Weight { get; set; }
}    

public static class GenericQueryExpressionBuilder
{        
    private static Expression NotEqual<T>(Expression memberExpression, ConstantExpression a_Constant, ParameterExpression parameter)
    {
        ConstantExpression constantExpression = null;

        if (IsNullableType(memberExpression.Type) && !IsNullableType(a_Constant.Type))
        {                
             //var converted = a_Constant.Type != memberExpression.Type ? (Expression)Expression.Convert(a_Constant, memberExpression.Type) : (Expression)a_Constant;

           Expression constantExp =  Expression.Property(a_Constant,typeof(T),"Weight");

        **// above statement throws exception I commented.**

            var body = Expression.MakeBinary(ExpressionType.NotEqual, memberExpression, converted);

            //here I want "t=>(t.Weight!=5000.0) INSTEAD of t=>(t.Weight!=Convert(5000.0))"

            var expr = Expression.Lambda<Func<T, bool>>(body, parameter);
            return expr;
        }

        else if (!IsNullableType(memberExpression.Type) && IsNullableType(a_Constant.Type))
            memberExpression = Expression.Convert(memberExpression, a_Constant.Type);

        return Expression.NotEqual(memberExpression, constantExpression);
    }

    static bool IsNullableType(Type t)
    {
        return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>);
    }        

    private static Expression GetExpression<T>(ParameterExpression param, string a_strPropertyName, string Operator, object Value)
    {            
        MemberExpression member = Expression.Property(param, a_strPropertyName);
        ConstantExpression constant = Expression.Constant(Value);

        try
        {               
           return GenericQueryExpressionBuilder.NotEqual<T>(member, constant, param);             

        }
        catch (InvalidOperationException)
        {
            return null;
        }

        return null;
    }

    public static Expression<Func<T, bool>> GetExpression<T>(Consignment consignment)
    {
        Expression expression = null;

        var parameter = Expression.Parameter(typeof(T), "t");

        string PropertyName = "Weight";
        string Operation = "NotEqual";
        object Value = consignment.Weight;

        expression = GenericQueryExpressionBuilder.GetExpression<T>(parameter, PropertyName, Operation, Value);
        return Expression.Lambda<Func<T, bool>>(expression, parameter);
    }
}
class Program
{
    static void Main(string[] args)
    {


        Consignment consignment = new Consignment();
        consignment.Weight = 50000.0f;

        var deleg = GenericQueryExpressionBuilder.GetExpression<Consignment>(consignment).Compile();            

    }
   }

【问题讨论】:

  • 如果值为空,你想发生什么? “不会相应地从表中获取数据”是什么确切?生成的 SQL 是什么样的,您需要它是什么样的?
  • Jon Skeet :当然,如果值为空,那么它会被简单地跳过。我有一个用户界面,用户可以在其中手动输入过滤器。然后将这些过滤器转换为表达式。然后将这些表达式应用于庞大的数据集以获取过滤记录。因此,就我而言,根本没有过滤器可以包含可为空的值。但是通过代码映射的数据表首先对可空字段进行操作
  • 我没有看到任何“当然”的东西......肯定还有其他同样有效的选择。但听起来你想要 "(t.Consignment.Weight.HasValue && t.Consignment.Weight.Value != 5000)` 或类似的东西。换句话说,你可以使用 HasValueValue 属性来指定准确你想要什么。
  • 对! ,这就是我想要的。但是如何构建可以与其他表达式进一步连接的表达式。 If(IsNullable(constantExpression)) { Expression.(property,constantExpression.Value) }... 但这会产生相同的措辞,我发布了问题。
  • 恐怕我现在没有关注你。如果您能提供一个简短但完整的程序来演示问题,那么对您的帮助会容易得多。

标签: c# lambda expression-trees linq-expressions


【解决方案1】:

这是一个简短但完整的示例,展示了如何构建 c =&gt; c.Weight.HasValue &amp;&amp; c.Weight.Value != 5000f 表达式树。我从问题中删除了很多不相关的代码:

using System;
using System.Linq.Expressions;

public class Consignment
{
    public float? Weight { get; set; }
}    

public class Test
{        
    private static Expression NotEqual(Expression memberExpression,
                                       ConstantExpression constantToCompare)
    {
        // Other cases removed, for simplicity. This answer only demonstrates
        // how to handle c => c.Weight != 5000f.
        var hasValueExpression = Expression.Property(memberExpression, "HasValue");
        var valueExpression = Expression.Property(memberExpression, "Value");
        var notEqual = Expression.NotEqual(valueExpression, constantToCompare);
        return Expression.AndAlso(hasValueExpression, notEqual);
    }

    static void Main(string[] args)
    {
        Consignment consignment = new Consignment();
        consignment.Weight = 50000.0f;

        var parameter = Expression.Parameter(typeof(Consignment), "c");
        var weight = Expression.Property(parameter, "Weight");
        var constant = Expression.Constant(5000f, typeof(float));
        var weightNotEqualExpression = NotEqual(weight, constant);
        var lambda = Expression.Lambda<Func<Consignment, bool>>
            (weightNotEqualExpression, parameter);
        Console.WriteLine(lambda);
    }
}

【讨论】:

  • 感谢 Jon Skeet:它按我的预期工作!我必须进行一些硬编码才能将确切的类型告知 Expression.Constant(ExpressionValie,tyepof(....))。但在我必须应用所有内置类型检查(float、string、int ....)之前。感谢您的时间和所做的一切!
  • @Usman:如果你有一个值(可能是装箱的),你可以打电话给GetType()。或者在常量类型中使其泛型,并使用typeof(T)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-04
  • 2020-10-06
  • 2012-01-10
相关资源
最近更新 更多