如果一个操作数是可空类型,则Expression.GreaterThan失败,另一个操作数是非可空的

我正在创建一些动态linq并遇到以下exception的问题:

没有为类型’System.Nullable`1 [System.DateTime]’和’System.DateTime’定义二元运算符GreaterThanOrEqual

我明白了,因为我的字段类型是可以为空的,而且我实际上是在DateTime.Now中传递的。

所以在试图解决这个问题时我已经尝试过了

System.Nullable now; now = DateTime.Now; 

但结果类型是一个不可为空的对象,因此仍然给我上述exception。

有什么建议?!

更新:为了进一步说明, now变量在设置时变为非可空类型,而不是保留为可空的DateTime,因此匹配会引发exception

更新:可以在CodePlex项目中看到实际代码:

http://webquarters.codeplex.com/SourceControl/changeset/view/36529#574700

违规线约为145

 fExp = Expression.GreaterThanOrEqual(fExpLeft, fExpRight); 

这里的问题是,当给出两个不匹配的可为空性的参数时,表达式库会引发exception。 这是一个简单的复制品:

 Expression> ex1 = ()=>DateTime.Now; Expression> ex2 = ()=>DateTime.Now; var ex3 = Expression.GreaterThan(ex1.Body, ex2.Body); 

我不清楚这是不是一个错误; C#的规则要求在这种情况下,非可空操作数转换为可空,并使用提升到可空的比较forms。 但是 ,表达式树库不需要遵循C#的规则,因为表达式树库当然可以用来表示C#表达式,Python表达式,JScript表达式,VB表达式等等; 它不可能遵循所有可能语言的所有规则。

但无论如何,这看起来可能是一个bug,所以我会将它提交给表达式树队,看看他们说了什么。 同时,您可以通过定义自己的辅助方法来轻松解决操作,修复操作数。 快速草图将是:

  static Expression MyGreaterThan(Expression e1, Expression e2) { if (IsNullableType(e1.Type) && !IsNullableType(e2.Type)) e2 = Expression.Convert(e2, e1.Type); else if (!IsNullableType(e1.Type) && IsNullableType(e2.Type)) e1 = Expression.Convert(e1, e2.Type); return Expression.GreaterThan(e1, e2); } static bool IsNullableType(Type t) { return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>); } 

但是,请注意,这并不会检查e1和e2的类型是否仅在可空性方面有所不同; 如果你传入一个可以为空的int和一个不可为空的双重表达式,就会发生坏事。 我把它作为练习来实现更好的逻辑,检查这两个表达式是否只有可空性的类型。

你的比较在哪里,改变这样的比较:

 (nullableDT >= DT) 

 (nullableDT != null && nullableDT.Value >= DT) 

编辑:

根据你的评论,编写一个带有2个对象的函数,在函数内检查它们是否为可空类型,并检查是否为null,然后比较值。 这个函数可能会使用类似于^的代码。

虽然,这开始听起来像你有一个更大的潜在问题。 要么你得到的数据不正确,(你的代码在其他地方返回的数据不应该是,代码中不是问题,而是逻辑上的问题),或者你可以比较这些对象的假设是无效的。 再一次,这是一个逻辑错误。

我想你可能需要退一步这一分钟。 如果您发布更多代码,我们可能会为您提供更多帮助。

我找到了一个在.Net框架内工作的解决方案。 这里有一个方法仍在进行中但它确实有效,并且适用于Linq to Entities:

 public PagedViewModel Filter(Expression> predicate, FilterType filterType = FilterType.Equals) { var name = (predicate.Body as MemberExpression ?? ((UnaryExpression)predicate.Body).Operand as MemberExpression).Member.Name; var value = Expression.Constant(ParamsData[name].To(), typeof (T).GetProperty(name).PropertyType); // If nothing has been set for filter, skip and don't filter data. ViewData[name] = m_QueryInternal.Distinct(predicate.Compile()).ToSelectList(name, name, ParamsData[name]); if (string.IsNullOrWhiteSpace(ParamsData[name])) return this; var nameExpression = Expression.Parameter(typeof(T), name); var propertyExpression = Expression.Property(nameExpression, typeof(T).GetProperty(name)); // Create expression body based on type of filter Expression expression; MethodInfo method; switch(filterType) { case FilterType.Like: method = typeof(string).GetMethod("Contains", new[] { typeof(string) }); expression = Expression.Call(propertyExpression, method, value); break; case FilterType.EndsWith: case FilterType.StartsWith: method = typeof(string).GetMethod(filterType.ToString(), new[] { typeof(string) }); expression = Expression.Call(propertyExpression, method, value); break; case FilterType.GreaterThan: expression = Expression.GreaterThan(propertyExpression, value); break; case FilterType.Equals: expression = Expression.Equal(propertyExpression, value); break; default: throw new ArgumentException("Filter Type could not be determined"); } // Execute the expression against Query. var methodCallExpression = Expression.Call( typeof (Queryable), "Where", new[] { Query.ElementType }, Query.Expression, Expression.Lambda>(expression, new[] { nameExpression })); // Filter the current Query data. Query = Query.Provider.CreateQuery(methodCallExpression); return this; } 

这里发生的是表达式常量值被强制转换为谓词类型。 为清楚起见,此方法称为:

 var paramsData = new NameValueCollection { { "CreatedOn", DateTime.Today.ToString() } }; var model = m_data.ToPagedList(new ViewDataDictionary(), paramsData, 1, 10, null, x => x.LastName) .Filters(Criteria.New(x => x.CreatedOn, FilterType.GreaterThan)) .Setup(); 

此代码基于MVC 3,因此一些ParamsData [“”]是(Request.Params)。

我不确定你的代码究竟是什么,但要获得Nullable的非可空版本,请调用它的.Value成员。

这是有趣的,

我试过这段代码的两种变体:

  System.Nullable now = new System.Nullable(); now = DateTime.Now; 

  System.Nullable now; now = DateTime.Now; 

而且他们都没有错误地工作。

然后我重新阅读你的问题。 实际上答案仍然是“价值”属性。 如果正常则初始化变量,但如果你这样做:

(>> DateTime.Now)在Linq查询中,您将收到错误消息。 它应该是(now.Value> = DateTime.Now)