在许多情况下,我想在服务器端进行一些过滤(有时是投影),然后切换到客户端以执行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
  • 使用“fluent”或“dot notation”语法使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;
        }
    }
    

    09-13 04:37