LINQ to Entities / LINQ to SQL:在查询理解过程中从服务器(可查询)切换到客户端(可枚举)?

在许多情况下,我想在服务器端进行一些过滤(有时是投影),然后切换到客户端,以获取LINQ提供程序本身不支持的操作。

天真的方法(基本上就是我现在所做的)就是把它分解成多个查询,类似于:

var fromServer = from t in context.Table where t.Col1 = 123 where t.Col2 = "blah" select t; var clientSide = from t in fromServer.AsEnumerable() where t.Col3.Split('/').Last() == "whatever" select t.Col4; 

然而,有很多次这是更多的代码/麻烦,而不是真正的价值。 我真的很想在中间做一个“切换到客户端”。 我已经尝试了各种使用查询延续的方法,但是在第一个查询结束时执行’select t into foo’之后,foo仍然是一个单独的项目,而不是集合,所以我不能AsEnumerable()它。

我的目标是能够写出更像:

 var results = from t in context.Table where t.Col1 = 123 where t.Col2 = "blah" // Magic happens here to switch to the client side where t.Col3.Split('/').Last() == "whatever" select t.Col4; 

好的,首先你绝对不应该在这里使用代码 。 它是由经过训练的特技仓鼠编写的,他们在处理这种性质的代码时受过训练而不会呕吐。

绝对应该选择一个你知道的选项:

  • 使用“临时”变量(如果您可以静态地将该变量键入为IEnumerable那么您不需要调用AsEnumerable – 如果您有一个匿名类型作为课程的元素类型,那将无效)
  • 使用括号调用AsEnumerable
  • 使用“流畅”或“点符号”语法使AsEnumerable调用适合。

但是,您可以使用查询表达式的翻译方式来做一些魔术。 您只需要使一个标准查询运算符与查询表达式中的表示具有不同的转换。 这里最简单的选择可能就是“Where”。 只需编写自己的扩展方法,使用IQueryableFunc ,其中SomeType不是bool ,你就离开了。 这是一个例子,首先是hack本身然后使用它的样本……

 using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; public static class QueryHacks { public static readonly HackToken TransferToClient = HackToken.Instance; public static IEnumerable Where( this IQueryable source, Func ignored) { // Just like AsEnumerable... we're just changing the compile-time // type, effectively. return source; } // This class only really exists to make sure we don't *accidentally* use // the hack above. public class HackToken { internal static readonly HackToken Instance = new HackToken(); private HackToken() {} } } public class Test { static void Main() { // Pretend this is really a db context or whatever IQueryable source = new string[0].AsQueryable(); var query = from x in source where x.StartsWith("Foo") // Queryable.Where where QueryHacks.TransferToClient where x.GetHashCode() == 5 // Enumerable.Where select x.Length; } } 

当然,如果您使用的是普通方法语法,那么这不是问题:

 var results = context.Table .Where(t => t.Col1 == 123) .Where(t => t.Col2 == "blah") .AsEnumerable() .Where(t => t.Col3.Split('/').Last() == "whatever") .Select(t => t.Col4); 

如果你坚持使用查询语法,你就不会使用一些括号,但除此之外,你当然可以做同样的事情:

 var results = from t in ( from t in context.Table where t.Col1 == 123 where t.Col2 == "blah" select t ).AsEnumerable() where t.Col3.Split('/').Last() == "whatever" select t.Col4; 

重用变量名t不会引起任何问题; 我测试了它。

服务器/客户端是什么意思?

我想你的意思是你从服务器获得一些集合,然后执行LINQ-to-entity中没有的额外过滤。 试试这个:

 var items = context.Table.Where(t => t.Col1 = 123 && t.Col2 = "blah").ToList() .Where(t => t.Col3.Split('/').Last() == "whatever") .Select(t => t.Col4).ToList(); 

您想使用更抽象的语法来更好地控制服务器与本地执行吗? 对不起 – 这不可行。

考虑查询理解中的范围问题。

 from c in context.Customers from o in c.Orders from d in o.Details asLocal where //c, o and d are all in scope, so they all had to be hydrated locally??