繁体   English   中英

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;
    }
}

当然,如果你使用的是普通的方法语法,那就没问题了:

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??

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM