问题
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;
回答1:
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;
}
}
回答2:
Of course, if you were using the normal method syntax, this would be no problem:
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);
If you insist on using the query syntax, you won’t get around using some parentheses, but otherwise, you can certainly still do the same:
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;
Reusing the variable name t
does not cause any problems; I tested it.
回答3:
What do you mean by server/client side?
I guess you mean you get some collection from the server and then execute additional filtering that's is not available in the LINQ-to-entity. Just try this:
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();
回答4:
You want to use the more abstract syntax to gain finer control over server vs local execution? Sorry - that's not do-able.
Think about the problem of scope within the query comprehension.
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??
来源:https://stackoverflow.com/questions/4824367/linq-to-entities-linq-to-sql-switching-from-server-queryable-to-client-enu