テストアプリ全体を以下に貼り付けました。かなりコンパクトなので問題ないことを期待しています。切り取ってコンソールアプリに貼り付けて実行できるはずです。
1つ以上のPersonオブジェクトのプロパティをフィルタリングできる必要がありますが、実行時までどのプロパティかわかりません。これはあちこちで議論されていることを知っています。PredicateBuilderやDynamicLinqLibraryなどのツールも調べて使用していますが、それらの議論は並べ替えと順序付けに重点を置く傾向があり、それぞれが苦労しています。 Nullable型に直面したときの独自の問題。それで、私はこれらの特定のシナリオに対処できる少なくとも補足フィルターを構築しようと思った。
以下の例では、特定の日付以降に生まれた家族を除外しようとしています。キックは、フィルタリングされるオブジェクトの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);
...これは、式、ラムダ、およびそれらに関連する同類についての私が持っている知識を上回る例外をもたらしました...
タイプ「System.DateTime」のParameterExpressionは、タイプ「ConsoleApplication1.Person」のデリゲートパラメータには使用できません。
何を試すべきかわかりません。