Рефакторинг LINQ to Entities запрос - PullRequest
2 голосов
/ 23 сентября 2010

Я реализовал поисковый запрос.Он заменяет специальные символы на «нормализованные».Я применил это правило на разных полях.На самом деле, запрос выглядит очень некрасиво и полон DRY-нарушений.

Но переосмыслить это, кажется, нелегко (для меня).Конечно, я только что попытался изменить весь Replace-Stuff в отдельный метод, но это привело к ошибке, такой как

LINQ to Entities не распознает метод 'System.String Help (System.String) ', и этот метод не может быть переведен в магазин выражений ...

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

qry = qry.Where(guest =>
                (guest.FirstName
                    .Replace("ü", "u").Replace("ue", "u").Replace("û", "u").Replace("ù", "u").Replace("ú", "u")
                    .Replace("ä", "a").Replace("ae", "a").Replace("â", "a").Replace("à", "a").Replace("á", "a")
                    .Replace("ë", "e").Replace("ê", "e").Replace("è", "e").Replace("é", "e")
                    .Replace("ö", "o").Replace("oe", "o").Replace("ô", "o").Replace("ò", "o").Replace("ó", "o")
                    .Replace("ï", "i").Replace("ì", "i").Replace("ì", "i").Replace("í", "i")
                    .Replace("ç", "c")
                    .Replace(".", "").Replace("-", "").Replace("_", "").Replace("´", "").Replace("'", "").Replace("\"", "")
                    .Replace("(", "").Replace(")", "").Replace("[", "").Replace("]", "").Replace("{", "").Replace("}", "")
                    .Replace("$", "").Replace("+", "").Replace("*", "").Replace("@", "")
                    .Replace("|", "").Replace("\\", "").Replace("/", "").Replace("<", "").Replace(">", "")
                    .Replace(".", "").Replace(",", "").Replace(";", "").Replace(":", "")
                    .Replace("=", "").Replace("%", "").Replace("^", "").Replace("?", "").Replace("!", "")
                    .StartsWith(firstName) 
                && (guest.LastName
                    .Replace("ü", "u").Replace("ue", "u").Replace("û", "u").Replace("ù", "u").Replace("ú", "u")
                    .Replace("ä", "a").Replace("ae", "a").Replace("â", "a").Replace("à", "a").Replace("á", "a")
                    .Replace("ë", "e").Replace("ê", "e").Replace("è", "e").Replace("é", "e")
                    .Replace("ö", "o").Replace("oe", "o").Replace("ô", "o").Replace("ò", "o").Replace("ó", "o")
                    .Replace("ï", "i").Replace("ì", "i").Replace("ì", "i").Replace("í", "i")
                    .Replace("ç", "c")
                    .Replace(".", "").Replace("-", "").Replace("_", "").Replace("´", "").Replace("'", "").Replace("\"", "")
                    .Replace("(", "").Replace(")", "").Replace("[", "").Replace("]", "").Replace("{", "").Replace("}", "")
                    .Replace("$", "").Replace("+", "").Replace("*", "").Replace("@", "")
                    .Replace("|", "").Replace("\\", "").Replace("/", "").Replace("<", "").Replace(">", "")
                    .Replace(".", "").Replace(",", "").Replace(";", "").Replace(":", "")
                    .Replace("=", "").Replace("%", "").Replace("^", "").Replace("?", "").Replace("!", "")
                    .StartsWith(lastName)
                    ||
                    guest.LastName
                    .Replace("ü", "u").Replace("ue", "u").Replace("û", "u").Replace("ù", "u").Replace("ú", "u")
                    .Replace("ä", "a").Replace("ae", "a").Replace("â", "a").Replace("à", "a").Replace("á", "a")
                    .Replace("ë", "e").Replace("ê", "e").Replace("è", "e").Replace("é", "e")
                    .Replace("ö", "o").Replace("oe", "o").Replace("ô", "o").Replace("ò", "o").Replace("ó", "o")
                    .Replace("ï", "i").Replace("ì", "i").Replace("ì", "i").Replace("í", "i")
                    .Replace("ç", "c")
                    .Replace(".", "").Replace("-", "").Replace("_", "").Replace("´", "").Replace("'", "").Replace("\"", "")
                    .Replace("(", "").Replace(")", "").Replace("[", "").Replace("]", "").Replace("{", "").Replace("}", "")
                    .Replace("$", "").Replace("+", "").Replace("*", "").Replace("@", "")
                    .Replace("|", "").Replace("\\", "").Replace("/", "").Replace("<", "").Replace(">", "")
                    .Replace(".", "").Replace(",", "").Replace(";", "").Replace(":", "")
                    .Replace("=", "").Replace("%", "").Replace("^", "").Replace("?", "").Replace("!", "")
                    .Contains(" " + lastName)
                    ||
                    guest.LastName.Replace(" ", "")
                    .Replace("ü", "u").Replace("ue", "u").Replace("û", "u").Replace("ù", "u").Replace("ú", "u")
                    .Replace("ä", "a").Replace("ae", "a").Replace("â", "a").Replace("à", "a").Replace("á", "a")
                    .Replace("ë", "e").Replace("ê", "e").Replace("è", "e").Replace("é", "e")
                    .Replace("ö", "o").Replace("oe", "o").Replace("ô", "o").Replace("ò", "o").Replace("ó", "o")
                    .Replace("ï", "i").Replace("ì", "i").Replace("ì", "i").Replace("í", "i")
                    .Replace("ç", "c")
                    .Replace(".", "").Replace("-", "").Replace("_", "").Replace("´", "").Replace("'", "").Replace("\"", "")
                    .Replace("(", "").Replace(")", "").Replace("[", "").Replace("]", "").Replace("{", "").Replace("}", "")
                    .Replace("$", "").Replace("+", "").Replace("*", "").Replace("@", "")
                    .Replace("|", "").Replace("\\", "").Replace("/", "").Replace("<", "").Replace(">", "")
                    .Replace(".", "").Replace(",", "").Replace(";", "").Replace(":", "")
                    .Replace("=", "").Replace("%", "").Replace("^", "").Replace("?", "").Replace("!", "")
                    .StartsWith(lastName))
                ) || (
                guest.FirstName
                    .Replace("ü", "u").Replace("ue", "u").Replace("û", "u").Replace("ù", "u").Replace("ú", "u")
                    .Replace("ä", "a").Replace("ae", "a").Replace("â", "a").Replace("à", "a").Replace("á", "a")
                    .Replace("ë", "e").Replace("ê", "e").Replace("è", "e").Replace("é", "e")
                    .Replace("ö", "o").Replace("oe", "o").Replace("ô", "o").Replace("ò", "o").Replace("ó", "o")
                    .Replace("ï", "i").Replace("ì", "i").Replace("ì", "i").Replace("í", "i")
                    .Replace("ç", "c")
                    .StartsWith(lastName)
                && (guest.LastName
                    .Replace("ü", "u").Replace("ue", "u").Replace("û", "u").Replace("ù", "u").Replace("ú", "u")
                    .Replace("ä", "a").Replace("ae", "a").Replace("â", "a").Replace("à", "a").Replace("á", "a")
                    .Replace("ë", "e").Replace("ê", "e").Replace("è", "e").Replace("é", "e")
                    .Replace("ö", "o").Replace("oe", "o").Replace("ô", "o").Replace("ò", "o").Replace("ó", "o")
                    .Replace("ï", "i").Replace("ì", "i").Replace("ì", "i").Replace("í", "i")
                    .Replace("ç", "c")
                    .Replace(".", "").Replace("-", "").Replace("_", "").Replace("´", "").Replace("'", "").Replace("\"", "")
                    .Replace("(", "").Replace(")", "").Replace("[", "").Replace("]", "").Replace("{", "").Replace("}", "")
                    .Replace("$", "").Replace("+", "").Replace("*", "").Replace("@", "")
                    .Replace("|", "").Replace("\\", "").Replace("/", "").Replace("<", "").Replace(">", "")
                    .Replace(".", "").Replace(",", "").Replace(";", "").Replace(":", "")
                    .Replace("=", "").Replace("%", "").Replace("^", "").Replace("?", "").Replace("!", "")
                    .StartsWith(firstName)
                    ||
                    guest.LastName
                    .Replace("ü", "u").Replace("ue", "u").Replace("û", "u").Replace("ù", "u").Replace("ú", "u")
                    .Replace("ä", "a").Replace("ae", "a").Replace("â", "a").Replace("à", "a").Replace("á", "a")
                    .Replace("ë", "e").Replace("ê", "e").Replace("è", "e").Replace("é", "e")
                    .Replace("ö", "o").Replace("oe", "o").Replace("ô", "o").Replace("ò", "o").Replace("ó", "o")
                    .Replace("ï", "i").Replace("ì", "i").Replace("ì", "i").Replace("í", "i")
                    .Replace("ç", "c")
                    .Replace(".", "").Replace("-", "").Replace("_", "").Replace("´", "").Replace("'", "").Replace("\"", "")
                    .Replace("(", "").Replace(")", "").Replace("[", "").Replace("]", "").Replace("{", "").Replace("}", "")
                    .Replace("$", "").Replace("+", "").Replace("*", "").Replace("@", "")
                    .Replace("|", "").Replace("\\", "").Replace("/", "").Replace("<", "").Replace(">", "")
                    .Replace(".", "").Replace(",", "").Replace(";", "").Replace(":", "")
                    .Replace("=", "").Replace("%", "").Replace("^", "").Replace("?", "").Replace("!", "")
                    .Contains(" " + firstName)
                    ||
                    guest.LastName.Replace(" ", "")
                    .Replace("ü", "u").Replace("ue", "u").Replace("û", "u").Replace("ù", "u").Replace("ú", "u")
                    .Replace("ä", "a").Replace("ae", "a").Replace("â", "a").Replace("à", "a").Replace("á", "a")
                    .Replace("ë", "e").Replace("ê", "e").Replace("è", "e").Replace("é", "e")
                    .Replace("ö", "o").Replace("oe", "o").Replace("ô", "o").Replace("ò", "o").Replace("ó", "o")
                    .Replace("ï", "i").Replace("ì", "i").Replace("ì", "i").Replace("í", "i")
                    .Replace("ç", "c")
                    .Replace(".", "").Replace("-", "").Replace("_", "").Replace("´", "").Replace("'", "").Replace("\"", "")
                    .Replace("(", "").Replace(")", "").Replace("[", "").Replace("]", "").Replace("{", "").Replace("}", "")
                    .Replace("$", "").Replace("+", "").Replace("*", "").Replace("@", "")
                    .Replace("|", "").Replace("\\", "").Replace("/", "").Replace("<", "").Replace(">", "")
                    .Replace(".", "").Replace(",", "").Replace(";", "").Replace(":", "")
                    .Replace("=", "").Replace("%", "").Replace("^", "").Replace("?", "").Replace("!", "")
                    .StartsWith(firstName))
                ));

Ответы [ 4 ]

5 голосов
/ 25 сентября 2010

Вы действительно можете преобразовать код в другую функцию, которая возвращает дерево выражений (как сказал Джон).

Я собрал решение для вас, хотя оно немного длинное.К сожалению, работа с выражениями довольно сложна, но я надеюсь, что вы найдете это полезным, хотя.

Примечание: для работы этого решения вам необходимо также использовать LinqKit полезно, когда вы используете LINQ с ORM)

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

Я создалфункция с именем TestCleanString.Вам нужно дать ему селектор для выбора свойства, которое вы хотите проверить, и вам также нужно дать ему предикат для проверки строки.

Например;здесь мы хотим проверить свойство FirstName и проверить, начинается ли оно с firstName.Он выберет свойство FirstName, затем очистит его, используя ваши правила очистки, и затем проверит результат по предикату.

TestCleanString<Guest>(g => g.FirstName, s => s.StartsWith(firstName));

Вот оно в действии:

//make an expression tree to check the first name
var firstnameOk = TestCleanString<Guest>(g => g.FirstName, s => s.StartsWith(firstName));
//make an expression tree to check the last name
var lastnameOk = TestCleanString<Guest>(g => g.LastName, s => s.StartsWith(lastName));
//make your additional filter expressions here ...
//...    

//combine the expression trees together using the "And" and "Or" methods from LinqKit
var filter = firstnameOk.And(lastnameOk);

//pass the filter into the where method
qry = qry.Where(filter);

Я поместилВаш код очистки строки в следующую функцию, которая возвращает дерево выражений.

//returns an expression that will clean the string
private static Expression<Func<string, string>> CleanString()
{
    return s => s.Replace("ü", "u").Replace("ue", "u").Replace("û", "u").Replace("ù", "u").Replace("ú", "u")
                 .Replace("ä", "a").Replace("ae", "a").Replace("â", "a").Replace("à", "a").Replace("á", "a")
                 .Replace("ë", "e").Replace("ê", "e").Replace("è", "e").Replace("é", "e")
                 .Replace("ö", "o").Replace("oe", "o").Replace("ô", "o").Replace("ò", "o").Replace("ó", "o")
                 .Replace("ï", "i").Replace("ì", "i").Replace("ì", "i").Replace("í", "i")
                 .Replace("ç", "c")
                 .Replace(".", "").Replace("-", "").Replace("_", "").Replace("´", "").Replace("'", "").Replace("\"", "")
                 .Replace("(", "").Replace(")", "").Replace("[", "").Replace("]", "").Replace("{", "").Replace("}", "")
                 .Replace("$", "").Replace("+", "").Replace("*", "").Replace("@", "")
                 .Replace("|", "").Replace("\\", "").Replace("/", "").Replace("<", "").Replace(">", "")
                 .Replace(".", "").Replace(",", "").Replace(";", "").Replace(":", "")
                 .Replace("=", "").Replace("%", "").Replace("^", "").Replace("?", "").Replace("!", "");
} 

Наконец, давайте посмотрим на функцию TestCleanString.Он строит дерево выражений, которое выполняет следующие 3 шага;выберите свойство, которое вы хотите проверить, очистите результирующую строку, проверьте строку на соответствие указанному условному выражению.

public static Expression<Func<TElement, bool>> TestCleanString<TElement>(Expression<Func<TElement, string>> stringSelector, Expression<Func<string, bool>> conditionalExpression)
{
    //declare the parameter: e =>
    var param = new[] { Expression.Parameter(typeof(TElement), "e") };
    //pass the parameter into the selector to get the string property
    var invokedStringSelector = Expression.Invoke(stringSelector, param.Cast<Expression>());
    //pass the string property to the clean expression
    var invokedCleanString = Expression.Invoke(CleanString(), invokedStringSelector.Expand());
    //pass the cleaned string to the conditional expression
    var invokedConditionalExpression = Expression.Invoke(conditionalExpression, invokedCleanString.Expand());
    //rebuild the expression tree so the provider can understand it
    return Expression.Lambda<Func<TElement, bool>>(invokedConditionalExpression.Expand(), param);
}

Если вам интересно, он генерирует SQL, который будет выглядеть примерно так (язапускал его против моей собственной модели, поэтому имена разные):

SELECT 
1 AS [C1], 
[Extent1].[EmailRecipientId] AS [EmailRecipientId], 
[Extent1].[Address] AS [Address], 
[Extent1].[SentOn] AS [SentOn], 
[Extent1].[FailedOn] AS [FailedOn], 
[Extent1].[FailReason] AS [FailReason], 
[Extent1].[IsTo] AS [IsTo], 
[Extent1].[IsCC] AS [IsCC], 
[Extent1].[IsBCC] AS [IsBCC], 
[Extent1].[EmailId] AS [EmailId]
FROM  [dbo].[EmailRecipients] AS [Extent1]
INNER JOIN [dbo].[Emails] AS [Extent2] ON [Extent1].[EmailId] = [Extent2].[EmailId]
WHERE ( CAST(LEN(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE([Extent2].[Subject], N'ü', N'u'), N'ue', N'u'), N'û', N'u'), N'ù', N'u'), N'ú', N'u'), N'ä', N'a'), N'ae', N'a'), N'â', N'a'), N'à', N'a'), N'á', N'a'), N'ë', N'e'), N'ê', N'e'), N'è', N'e'), N'é', N'e'), N'ö', N'o'), N'oe', N'o'), N'ô', N'o'), N'ò', N'o'), N'ó', N'o'), N'ï', N'i'), N'ì', N'i'), N'ì', N'i'), N'í', N'i'), N'ç', N'c'), N'.', N''), N'-', N''), N'_', N''), N'´', N''), N'''', N''), N'"', N''), N'(', N''), N')', N''), N'[', N''), N']', N''), N'{', N''), N'}', N''), N'$', N''), N'+', N''), N'*', N''), N'@', N''), N'|', N''), N'\', N''), N'/', N''), N'<', N''), N'>', N''), N'.', N''), N',', N''), N';', N''), N':', N''), N'=', N''), N'%', N''), N'^', N''), N'?', N''), N'!', N'')) AS int)) > 0
1 голос
/ 23 сентября 2010

Ну, LINQ to EF не понравится простой вызов частного метода из лямбда-выражения ... но вы можете написать метод, который строит дерево выражений, эквивалентное тому, что вы уже получили. Работа с деревьями выражений не всегда проста, но она должна помочь. Затем вы вызываете метод Where, передавая дерево выражений в качестве обычного аргумента (т. Е. не с использованием лямбда-выражения).

Я предлагаю вам написать очень простой оператор, выполняющий 2 Replace операций, и либо посмотреть, что делает для вас компилятор C # (через отражение), либо использовать визуализатор дерева выражений в VS2010. Если вы знаете, как выглядит дерево, его программная сборка не должна быть слишком плохой.

Обратите внимание, что вам также необходимо встроить функциональность "ИЛИ" в дерево выражений (что я бы посоветовал сделать с использованием отдельного метода), но функциональность "И" может быть достигнута просто с помощью нескольких Where звонки.

1 голос
/ 23 сентября 2010

Это не окажет вам никакой помощи, чтобы помочь вам реорганизовать этот код. Запросы LINQ to Entities в конечном итоге преобразуются в SQL, и этот SQL будет беспорядочным, независимо от того, насколько хорошо выглядит код. Вам необходимо пересмотреть свою стратегию запросов на основе инструментов, которые предоставляет вам ваша база данных. В идеале вы должны быть в состоянии написать запрос, который использует индекс.

Необходимо рассмотреть две стратегии: параметры сортировки и изменения схемы.

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

Что касается знаков доллара и тому подобного, вы, вероятно, не найдете сопоставления, которое бы просто игнорировало их, если вы не напишите его сами. Таким образом, другой вариант будет иметь отдельный столбец в базе данных, обновленный триггером, который содержит имя и фамилию с удалением этих символов. Вместо этого запустите поиск, и можно использовать индекс по этим столбцам.

0 голосов
/ 23 сентября 2010

Рассматривали ли вы использование Func <> делегата или частного вызова метода, где будет происходить весь этот процесс?Это может улучшить читаемость кода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...