Вопрос по entity-framework, linq, c# – Есть ли простой способ написать пользовательскую функцию в LINQ to Entities?

6

Я пишу простой поисковый запрос для своего приложения Entity Framework. Мне нужно проверить, являются ли несколько полей пустыми, а если нет, вызвать ToLower () для них и сравнить с поисковым запросом. Запрос LINQ выглядит примерно так:

<code>public IQueryable<Store> SearchStores(string q, IQueryable<Store> source)
{
    q = q.ToLower();

    return (
        from s in source
        where (
            (s.Name != null && s.Name.ToLower().Contains(q)) ||
            (s.Description != null && s.Description.ToLower().Contains(q)) ||
            ...
}
</code>

Подобных строк много, поэтому мне хотелось написать вспомогательный метод, чтобы немного его очистить:

<code>public static bool SafeSearch(this string s, string q)
{
    return s == null ? false : s.ToLower().Contains(q);
}
</code>

Это, конечно, не работает, поскольку LINQ для сущностей не понимает, что такое функция SafeSearch:

LINQ to Entities does not recognize the method 'Boolean SafeSearch(System.String, System.String)' method, and this method cannot be translated into a store expression.

Есть ли простой способ написать простую пользовательскую функцию, подобную этой?

Спасибо!

Какой тип сортировки в вашей базе данных? Brannon

Ваш Ответ

2   ответа
1

а также использовать функции для построения их частей. Синтаксис также читабелен, что подходит для «простого» часть вопроса. Это возможно благодаряcombining Linq expressions, Есть несколько статей о том, как это можно сделать, но я думаю, что придумал новый подход. По крайней мере, я не нашел его в Интернете.

Для продолжения вам нужна библиотека из 3 простых функций. Они используютSystem.Linq.Expressions.ExpressionVisitor динамически изменять выражения. Ключевой особенностью является объединение параметров внутри выражения, чтобы 2 параметра с одинаковым именем были сделаны одинаковыми (UnifyParametersByName). Оставшаяся часть заменяет именованный параметр заданным выражением (ReplacePar) и вспомогательный метод (NewExpr). Библиотека доступна с лицензией MIT на github:LinqExprHelper, но вы можете быстро написать что-то самостоятельно.

Сначала вы определяете некоторые методы, которые впоследствии могут быть использованы при создании динамических запросов.

public class Store
{
    ...

    public static Expression<Func<Store, bool>>
        SafeSearchName(string sWhat)
    {
        return LinqExprHelper.NewExpr(
            (Store s) => s.Name != null && s.Name.ToLower().Contains(sWhat)
        );
    }

    public static Expression<Func<Store, bool>>
        SafeSearchDesc(string sWhat)
    {
        return LinqExprHelper.NewExpr(
            (Store s) => s.Description != null && s.Description.ToLower().Contains(sWhat)
        );
    }
}

Затем вы делаете запрос следующим образом:

    // Define a master condition, using named parameters.
    var masterExpr = LinqExprHelper.NewExpr(
        (Store s, bool bSearchName, bool bSearchDesc)
        => (bSearchName && bSearchDesc));

    // Replace stub parameters with some real conditions.
    var combExpr = masterExpr
        .ReplacePar("bSearchName", Store.SafeSearchName("b").Body)
        .ReplacePar("bSearchDesc", Store.SafeSearchDesc("p").Body);
        // Sometimes you may skip a condition using this syntax:
        //.ReplacePar("bSearchDesc", Expression.Constant(true));

    // It's interesting to see how the final expression looks like.
    Console.WriteLine("expr: " + combExpr);

   // Execute the query using combined expression.
   db.Stores
        .Where((Expression<Func<Store, bool>>)combExpr)
        .ToList().ForEach(i => { Console.WriteLine(i.Name + ", " + i.Description); });

Я еще не использовал это в производстве, но некоторые простые тесты пройдены. Я не вижу никаких ограничений в комбинировании запросов таким образом. Если нам нужно больше параметров, мы можем добавить дополнительный уровень объединения. Преимущество этого метода заключается в том, что вы можете использовать встроенные лямбда-выражения, которые приятно читать, вместе с созданием динамических выражений и составлением, что очень удобно.

Является ли это "простым" в конце концов? Если вы считаете,method syntax Линк как простой, то это почти так просто. Он не позволяет вам создавать пользовательские функции Linq, но предоставляет вам сопоставимые возможности.

Error: User Rate Limit Exceeded
2

которые не выполняются до тех пор, пока вы на самом деле не вызовете базу данных, вам потребуется заключить свою функцию в предикат.

private static Func<Country, bool> Predicate(string q)
{
    return x => (
        q.SafeSearch(x.Name) ||
        q.SafeSearch(x.Description)
        );
}

Также обратный метод расширения SafeSearch путем вызова его по запросу, позаботится о случаях, когда x.Name имеет значение null.

public static class SearchExt
{
    public static bool SafeSearch(this string q, string param)
    {
        return param == null ? false : param.ToLower().Contains(q);
    }
}

и тогда вы можете использовать его с методами расширения

return source.Where(Predicate(q));

или используя выражение linq

return from p in source
       where Predicate(q).Invoke(p)
       select p;
Error: User Rate Limit Exceeded ManicBlowfish
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded ManicBlowfish

Похожие вопросы