重构LINQ to Entities查询

时间:2010-09-23 07:01:33

标签: linq entity-framework entity-framework-4

我已经实施了搜索查询。它用“标准化”字符替换特殊字符。我已在不同领域应用此规则。实际上,查询看起来非常难看并且充满了DRY违规。

但是反思这一点,对我来说似乎并不容易。当然,我只是试图将整个Replace-Stuff重构为一个单独的方法,但这导致了一个错误,如

  

LINQ to Entities无法识别   方法'System.String   帮助(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 个答案:

答案 0 :(得分:5)

您确实可以将代码重构为另一个返回表达式树的函数(如Jon所述)。

我为你准备了一个解决方案,虽然它有点冗长。不幸的是,处理表达式非常复杂,我希望你发现这很有用。

注意:要使此解决方案有效,您还需要使用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 :(得分:1)

好吧,LINQ to EF不会像在lambda表达式中那样简单的私有方法调用...但是你可以编写一个方法来构建一个与你已经得到的等价表达式树。使用表达式树并不总是很容易,但它应该可以解决问题。然后,您将调用Where方法,将表达式树作为普通参数传递(即使用lambda表达式)。

我建议你编写一个非常简单的语句,执行2 Replace个操作,并查看C#编译器为你做的事情(通过反射)或使用VS2010中的表达式树可视化器。一旦你知道树的样子,以编程方式构建它应该不会太糟糕。

请注意,您还必须在表达式树中构建“OR”功能(我建议您使用单独的方法),但只需使用多个{{“即可实现”AND“功能。 1}}来电。

答案 2 :(得分:1)

它不会帮助您重构此代码。 LINQ to Entities查询最终被转换为SQL,无论代码看起来多么好,这个SQL都会变得一团糟。您需要根据数据库为您提供的工具重新考虑查询策略。理想情况下,您应该能够编写使用索引的查询。

需要考虑两种策略:归类和架构更改。

您尚未提及您正在使用的数据库,但大多数数据库都提供对WHERE搜索不重音的排序规则。您应该考虑将列上的排序规则更改为其中一个。

关于美元符号等,除非你自己写,否则你可能找不到会忽略它们的整理。因此,另一个选项是在数据库中有一个单独的列,由触发器更新,该触发器包含删除了这些字符的名字和姓氏。相反,对此运行搜索,可以使用这些列的索引。

答案 3 :(得分:-1)

您是否考虑过使用Func&lt;&gt;委托或私人方法调用将发生所有这些过程?这可以提高代码的可读性。