在许多情况下,我想在服务器端进行一些过滤(有时是投影),然后切换到客户端进行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;
答案 0 :(得分:20)
好的,首先你绝对不应该使用这里的代码。它是由受过训练的特技仓鼠编写的,他们在处理这种性质的代码时已经接受过训练而不会呕吐。
你绝对选择你知道的一个选项:
IEnumerable<T>
,那么您不需要调用AsEnumerable
- 如果您有匿名的话,这将无效键入作为课程的元素类型)AsEnumerable
AsEnumerable
调用适合。但是,可以使用查询表达式的翻译方式做一些魔术。您只需要使一个标准查询运算符与查询表达式中的表示具有不同的转换。这里最简单的选择可能就是“Where”。只需编写您自己的扩展方法,即IQueryable<T>
和Func<T, SomeType>
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<T> Where<T>(
this IQueryable<T> source,
Func<T, HackToken> 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<string> 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;
}
}
答案 1 :(得分:8)
当然,如果您使用的是常规方法语法,那就没问题了:
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
不会导致任何问题;我测试了它。
答案 2 :(得分:0)
服务器/客户端是什么意思?
我猜你的意思是你从服务器获得一些集合,然后执行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();
答案 3 :(得分:-1)
您想使用更抽象的语法来更好地控制服务器与本地执行吗?对不起 - 这不可行。
考虑查询理解中的范围问题。
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??