6

私は基本的にクエリを作成しようとしていますが、MicrosoftがEntityFrameworkとLINQでこれを非常に困難にした理由がわかりません。さまざまなパラメータSTRINGSがあります。したがって、変数が表示された場合は、それがどこかから渡された文字列であると想定します。

             users = this.entities.tableUsers
                .Where(searchfield+" LIKE %@0%", search)
                .OrderBy(x => x.GetType().GetProperty(order_by).GetValue(x, null).ToString())
                .Skip(Convert.ToInt32(limit_begin))
                .Take(Convert.ToInt32(limit_end))
                .ToList();

私の質問は、LINQの「Where()」関数の中に何を入れるかです。

文字列"searchfield"のフィールドで、値.contains()"search"を検索したいと思います。

VisualStudioでこれを簡単に実行できない理由がわかりません。

私もこれを試しましたが、運がありません:

.Where(x => x.GetType().GetProperty(searchfield).GetValue(x, null).ToList().Contains(search))

注:新しいライブラリをインストールしたくありません。これは、現代語では信じられないほど簡単でシンプルなはずです。クエリがすべての行を返すかどうかは気にせず、.Contains()を使用して後で検索します。

4

4 に答える 4

9

これは些細なことではありませんが、できると思います。以下はテストされていません。コードはここから借用しています。

次のようなヘルパーメソッドを作成します

public static Expression<Func<T, bool>> GetContainsExpression<T>(string propertyName, string containsValue)
{
    var parameterExp = Expression.Parameter(typeof(T), "type");
    var propertyExp = Expression.Property(parameterExp, propertyName);
    MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
    var someValue = Expression.Constant(propertyValue, typeof(string));
    var containsMethodExp = Expression.Call(propertyExp, method, someValue);

    return Expression.Lambda<Func<T, bool>>(containsMethodExp, parameterExp);
}

public static Expression<Func<T, TKey>> GetPropertyExpression<T, TKey>(string propertyName)
{
    var parameterExp = Expression.Parameter(typeof(T), "type");
    var exp = Expression.Property(parameterExp, propertyName);
    return Expression.Lambda<Func<T, TKey>>(exp, parameterExp);
}

次のように使用します

users = this.entities.tableUsers
                     .Where(GetContainsExpression<User>(searchfield, search))
                     .OrderBy(GetPropertyExpression<User, string>(searchfield))
                     ...

アップデート

別の方法として、よりクリーンな構文を提供する拡張メソッドを作成することもできます。静的クラスのどこかに次のメソッドを作成します。

    public static IQueryable<T> WhereStringContains<T>(this IQueryable<T> query, string propertyName, string contains)
    {
        var parameter = Expression.Parameter(typeof(T), "type");
        var propertyExpression = Expression.Property(parameter, propertyName);
        MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
        var someValue = Expression.Constant(contains, typeof(string));
        var containsExpression = Expression.Call(propertyExpression, method, someValue);

        return query.Where(Expression.Lambda<Func<T, bool>>(containsExpression, parameter));
    }

    public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> query, string propertyName)
    {
        var propertyType = typeof(T).GetProperty(propertyName).PropertyType;
        var parameter = Expression.Parameter(typeof(T), "type");
        var propertyExpression = Expression.Property(parameter, propertyName);
        var lambda = Expression.Lambda(propertyExpression, new[] { parameter });

        return typeof(Queryable).GetMethods()
                                .Where(m => m.Name == "OrderBy" && m.GetParameters().Length == 2)
                                .Single()
                                .MakeGenericMethod(new[] { typeof(T), propertyType })
                                .Invoke(null, new object[] { query, lambda }) as IOrderedQueryable<T>;
    }

    public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> query, string propertyName)
    {
        var propertyType = typeof(T).GetProperty(propertyName).PropertyType;
        var parameter = Expression.Parameter(typeof(T), "type");
        var propertyExpression = Expression.Property(parameter, propertyName);
        var lambda = Expression.Lambda(propertyExpression, new[] { parameter });

        return typeof(Queryable).GetMethods()
                                .Where(m => m.Name == "OrderByDescending" && m.GetParameters().Length == 2)
                                .Single()
                                .MakeGenericMethod(new[] { typeof(T), propertyType })
                                .Invoke(null, new object[] { query, lambda }) as IOrderedQueryable<T>;
    }

次に、次のように呼び出すことができます。

var users = this.entities.tableUsers.WhereStringContains(searchField, search)
                                    .OrderBy(searchField);
于 2012-08-16T20:01:13.487 に答える
5

これは現代語にとって信じられないほど簡単でシンプルなはずです

いいえ、それがその言語パラダイムに反する場合は、そうすべきではありません。LINQとEntityFramework(およびその他の適切なORM)は、実行しようとしていること(型指定されておらず、コンパイラーで検証できないクエリ)を回避するために正確に作成されています。つまり、基本的には、四角いペグを丸い穴に押し込むことになります。

引き続きDynamicLINQを確認できます。

于 2012-08-16T19:43:54.330 に答える
2

メソッドに渡す式ツリーを作成する必要がありますWhere。これが私が嘘をついているいくつかのコードの大まかな適応です:

string searchfield, value; // Your inputs
var param = Expression.Parameter(typeof(User), "user");

return Expression.Lambda<Func<T, bool>>(
    Expression.Call(
        Expression.Property(
            param,
            typeof(User).GetProperty(searchfield)),
        typeof(string).GetMethod("Contains"),
        Expression.Constant(value)),
    param);

これにより、へのパラメータとして使用する適切な式が生成されますWhere

編集:参考までに、結果の式は次のようになりますuser => user.Foo.Contains(bar)

編集:ソートするには、次のようなもの(私のDynamicOrderListクラスからリッピング):

private IQueryable<T> OrderQuery<T>(IQueryable<T> query, OrderParameter orderBy)
{
    string orderMethodName = orderBy.Direction == SortDirection.Ascending ? "OrderBy" : "OrderByDescending";
    Type t = typeof(T);

    var param = Expression.Parameter(t, "user");
    var property = t.GetProperty(orderBy.Attribute);

    return query.Provider.CreateQuery<T>(
        Expression.Call(
            typeof(Queryable),
            orderMethodName,
            new Type[] { t, typeof(string) },
            query.Expression,
            Expression.Quote(
                Expression.Lambda(
                    Expression.Property(param, property),
                    param))
        ));
}
于 2012-08-16T19:51:37.527 に答える
0

これについての他の質問に対する私の答え:

EntityFrameworkで動的なlinqの並べ替えと検索の順序ステートメントを作成する場合

于 2012-08-16T19:46:46.187 に答える