LINQ to Entities/LINQ to SQL:在查询理解过程中从服务器(可查询)切换到客户端(可枚举)? [英] LINQ to Entities / LINQ to SQL: switching from server (queryable) to client (enumerable) in the middle of a query comprehension?
问题描述
在许多情况下,我想在服务器端进行一些过滤(有时是投影),然后切换到客户端以执行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<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;
}
}
In many cases, I want to do some filtering (and sometimes projection) on the server side and then switch to client-side for operations that the LINQ provider doesn't natively support.
The naive approach (which is basically what I do now) is to just break it up into multiple queries, similar to:
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;
However, there are many times where this is more code/trouble than it's really worth. I'd really like to do a 'switch to client side' in the middle. I've tried various methods of using a query continuation, but after doing a 'select t into foo' at the end of the first query, foo is still an individual item, not the collection, so I can't AsEnumerable() it.
My goal is to be able write something more like:
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;
Okay, firstly you absolutely should not use the code here. It was written by trained stunt-hamsters who have been trained not to throw up when dealing with this code of this nature.
You should absolutely pick one of the options you know about:
- Use a "temporary" variable (if you can statically type that variable as
IEnumerable<T>
then you don't need the call toAsEnumerable
- that won't work if you've got an anonymous type as the element type of course) - Use brackets for a call to
AsEnumerable
- Use the "fluent" or "dot notation" syntax to make the
AsEnumerable
call fit in.
However, you can do a bit of magic, using the way that query expressions are translated. You just need to make one of the standard query operators with a representation in query expressions have a different translation. The simplest option here is probably "Where". Just write your own extension method taking an IQueryable<T>
and a Func<T, SomeType>
where SomeType
isn't bool
, and you're away. Here's an example, first of the hack itself and then a sample use of it...
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;
}
}
这篇关于LINQ to Entities/LINQ to SQL:在查询理解过程中从服务器(可查询)切换到客户端(可枚举)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!