我在下面粘贴了我的整个测试应用程序。它相当紧凑,所以我希望这不是问题。您应该能够简单地将其剪切并粘贴到控制台应用程序中并运行它。
我需要能够过滤任何一个或多个 Person 对象的属性,并且直到运行时我才知道是哪一个。我知道这已经到处讨论了,我已经研究过并且也在使用PredicateBuilder和Dynamic Linq Library等工具,但是围绕它们的讨论往往更多地集中在排序和排序上,并且每个人都在努力解决他们的问题遇到可空类型时自己的问题。所以我想我会尝试至少构建一个可以解决这些特定场景的补充过滤器。
在下面的示例中,我试图过滤掉在某个日期之后出生的家庭成员。关键是要过滤的对象上的 DateOfBirth 字段是 DateTime 属性。
我得到的最新错误是
在类型“System.String”和“System.Nullable`1[System.DateTime]”之间没有定义强制运算符。
这是问题所在。我尝试了几种不同的铸造和转换方法,但都失败了。最终,这将应用于 EF 数据库,该数据库也拒绝使用 DateTime.Parse(--) 等转换方法。
任何帮助将不胜感激!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
List<Person> people = new List<Person>();
people.Add(new Person { FirstName = "Bob", LastName = "Smith", DateOfBirth = DateTime.Parse("1969/01/21"), Weight=207 });
people.Add(new Person { FirstName = "Lisa", LastName = "Smith", DateOfBirth = DateTime.Parse("1974/05/09") });
people.Add(new Person { FirstName = "Jane", LastName = "Smith", DateOfBirth = DateTime.Parse("1999/05/09") });
people.Add(new Person { FirstName = "Lori", LastName = "Jones", DateOfBirth = DateTime.Parse("2002/10/21") });
people.Add(new Person { FirstName = "Patty", LastName = "Smith", DateOfBirth = DateTime.Parse("2012/03/11") });
people.Add(new Person { FirstName = "George", LastName = "Smith", DateOfBirth = DateTime.Parse("2013/06/18"), Weight=6 });
String filterField = "DateOfBirth";
String filterOper = "<=";
String filterValue = "2000/01/01";
var oldFamily = ApplyFilter<Person>(filterField, filterOper, filterValue);
var query = from p in people.AsQueryable().Where(oldFamily)
select p;
Console.ReadLine();
}
public static Expression<Func<T, bool>> ApplyFilter<T>(String filterField, String filterOper, String filterValue)
{
//
// Get the property that we are attempting to filter on. If it does not exist then throw an exception
System.Reflection.PropertyInfo prop = typeof(T).GetProperty(filterField);
if (prop == null)
throw new MissingMemberException(String.Format("{0} is not a member of {1}", filterField, typeof(T).ToString()));
Expression convertExpression = Expression.Convert(Expression.Constant(filterValue), prop.PropertyType);
ParameterExpression parameter = Expression.Parameter(prop.PropertyType, filterField);
ParameterExpression[] parameters = new ParameterExpression[] { parameter };
BinaryExpression body = Expression.LessThanOrEqual(parameter, convertExpression);
Expression<Func<T, bool>> predicate = Expression.Lambda<Func<T, bool>>(body, parameters);
return predicate;
}
}
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime? DateOfBirth { get; set; }
string Nickname { get; set; }
public int? Weight { get; set; }
public Person() { }
public Person(string fName, string lName)
{
FirstName = fName;
LastName = lName;
}
}
}
更新:2013/02/01
然后我的想法是将 Nullabe 类型转换为它的 Non-Nullable 类型版本。因此,在这种情况下,我们希望将 <Nullable>DateTime 转换为简单的 DateTime 类型。我在调用 Expression.Convert 调用之前添加了以下代码块,以确定和捕获 Nullable 值的类型。
//
//
Type propType = prop.PropertyType;
//
// If the property is nullable we need to create the expression using a NON-Nullable version of the type.
// We will get this by parsing the type from the FullName of the type
if (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
String typeName = prop.PropertyType.FullName;
Int32 startIdx = typeName.IndexOf("[[") + 2;
Int32 endIdx = typeName.IndexOf(",", startIdx);
String type = typeName.Substring(startIdx, (endIdx-startIdx));
propType = Type.GetType(type);
}
Expression convertExpression = Expression.Convert(Expression.Constant(filterValue), propType);
这实际上可以从 DateTime 中删除 Nullable-ness,但会导致以下强制错误。我仍然对此感到困惑,因为我认为“Expression.Convert”方法的目的就是为了做到这一点。
在类型“System.String”和“System.DateTime”之间没有定义强制运算符。
继续前进,我将值显式解析为 DateTime 并将其插入混合...
DateTime dt = DateTime.Parse(filterValue);
Expression convertExpression = Expression.Convert(Expression.Constant(dt), propType);
...这导致了一个异常,超出了我对表达式、Lambda 及其相关同类知识的任何了解...
“System.DateTime”类型的 ParameterExpression 不能用于“ConsoleApplication1.Person”类型的委托参数
我不确定还有什么可以尝试的。