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

Jam*_*ing 10 c# linq linq-to-objects 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;
Run Code Online (Sandbox Code Playgroud)

然而,有很多次这是更多的代码/麻烦,而不是真正的价值.我真的很想在中间做一个"切换到客户端".我已经尝试了各种使用查询延续的方法,但是在第一个查询结束时执行'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;
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 20

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

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

  • 使用"临时"变量(如果您可以静态键入该变量,IEnumerable<T>那么您不需要调用AsEnumerable- 如果您有一个匿名类型作为课程的元素类型,那将无效)
  • 使用括号进行呼叫 AsEnumerable
  • 使用"流利"或"点符号"语法使AsEnumerable呼叫适合.

但是,您可以使用查询表达式的翻译方式来做一些魔术.您只需要使一个标准查询运算符与查询表达式中的表示具有不同的转换.这里最简单的选择可能就是"Where".只要写你自己的扩展方法服用IQueryable<T>Func<T, SomeType>地方SomeTypebool和你离开.这是一个例子,首先是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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 警告将被忽略,这段代码我会在野外看到.谢谢你提前告诉我. (4认同)
  • 如果你要说它很糟糕,那么你真的应该解释*为什么*这很糟糕,否则人们会用它.老实说,如果你选择一个合适的命名惯例并且你对使用情况保持一致,我并不是百分之百确定自己是那么糟糕,并且会喜欢被选中的人开悟. (2认同)
  • 看到这个排名远远超过更好的答案而感到难过,不要仅仅因为它们“有趣”就让他们获得超级排名,或者因为他们是乔恩·斯凯特的答案而更不可能对它进行排名,这会破坏网站的整体。 (2认同)

Tim*_*mwi 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);
Run Code Online (Sandbox Code Playgroud)

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

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;
Run Code Online (Sandbox Code Playgroud)

重用变量名称t不会导致任何问题; 我测试了它.