IQueryableのWhereLike拡張機能を作成しようとしていますが、実行時にプロパティのタイプを知ることができません。
これが私のコードです:
public static IQueryable WhereLike(this IQueryable source, string propertyName, string pattern)
{
if (source == null) throw new ArgumentNullException("source");
if (propertyName == null) throw new ArgumentNullException("propertyName");
var a = Expression.Parameter(typeof(object), "a");
var prop = Expression.Property(a, propertyName);
return source.Provider.CreateQuery(
Expression.Call(
typeof(SqlMethods), "Like",
null,
prop, Expression.Constant(pattern)));
}
例外が発生します:インスタンスプロパティ 'foo'がタイプ 'System.Object'に対して定義されていません
コンパイル時にターゲットタイプを知らなくてもプロパティ設定を処理する方法を知っていますか?
汎用のIQueryable<T>
バリアントを使用できる場合、 CreateQuery
が不要になり、 IQueryable<T>
ソースに対して直接実行できるため、これははるかに簡単な問題になります。
public static IQueryable<T> WhereLike<T>(this IQueryable<T> source, string propertyName,
string pattern)
{
if (source == null) throw new ArgumentNullException("source");
if (propertyName == null) throw new ArgumentNullException("propertyName");
var a = Expression.Parameter(typeof(T), "a");
var prop = Expression.PropertyOrField(a, propertyName);
var expr = Expression.Call(
typeof(SqlMethods), "Like",
null,
prop, Expression.Constant(pattern));
var lambda = Expression.Lambda<Func<T, bool>>(expr, a);
return source.Where(lambda);
}
2つの重要な点に注意してください。
プロパティのみを取得する代わりに、PropertyOrFieldを使用すると、フィールドを公開している可能性があるLinq-2-SQL用に生成されたコードを適切にサポートできます。
さらに、 IQueryable<T>
ソースに対して実行しているため、 "Like" MethodCallExpression
結果からラムダ式を作成する必要があります。
非ジェネリックバリアントが必要な場合でも、同じことを実行できますが、Like MethodCallExpression
をWhere MethodCallExpression
でラップして、適切に構造化する必要があります。
public static IQueryable WhereLike(this IQueryable source, string propertyName,
string pattern)
{
if (source == null) throw new ArgumentNullException("source");
if (propertyName == null) throw new ArgumentNullException("propertyName");
var a = Expression.Parameter(source.GetType().GetGenericArguments().First(), "a");
var prop = Expression.PropertyOrField(a, propertyName);
var expr = Expression.Call(
typeof(SqlMethods), "Like",
null,
prop, Expression.Constant(pattern));
MethodCallExpression whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new Type[] { source.ElementType },
source.Expression,
Expression.Lambda(expr, a));
return source.Provider.CreateQuery(whereCallExpression);
}
どちらのバリアントもワイルドカードで呼び出すことができます。
var data = source.WhereLike("ColumnName", "%o%");