您如何知道一个集合将充当 IEnumerable 还是 IQueryable?

How do you know if a collection will act as IEnumerable or IQueryable?

我有这行代码:

var attachments = EntityRepository<Attachment>().Entities
.Where(at => at.EntityType == EntityType.EmailTemplate)
.ToDictionary(at => at.Extension, at => at);

EntityRepository<Attachment>().EntitiesSystem.Data.Entity.Infrastructure.DbQuery<TResult> 类型,它实现了 IQueryable<TResult>IEnumerable<TResult>

如果它作为 IEnumerable<T>(即从数据库中检索所有行,然后在 C# 中进行过滤)或作为 IQueryable<T>(转换C# 谓词到 SQL 查询并仅检索那些行)。

我想你可能对IEnumerable有点误解。它只是说 class 支持迭代。它不会直接影响任何数据的获取方式。

此外,IQueryable 实现了 IEnumerable,因此所有 IQueryable 实例也是 IEnumerable。这是有道理的,因为您可以迭代结果。

在您的示例中,缺少 IQueryable 意味着 "retrieve all rows from DB, then do the filtering in C#"。

LINQ 中有 2 个不同的扩展 - IEnumerable and for IQueryable

当你写 EntityRepository<Attachment>().Entities .Where(at => at.EntityType == EntityType.EmailTemplate) 时,编译器检查 Entities 的类型,当它声明 'more specific' IQueryable 时,编译器选择 Queryable.Where() 方法,表达式被翻译成IQueryProvider 到 SQL。当您编写 .ToDictionary(at => at.Extension, at => at) 时,编译器找不到 Queryable.ToDictionary(),因此它回退到 Enumerable.ToDictionary() 并在内存中过滤项目。

扩展方法调用规则定义在C# language spec:

  • The set of candidate methods is reduced to contain only methods from the most derived types: For each method C.F in the set, where C is the type in which the method F is declared, all methods declared in a base type of C are removed from the set. Furthermore, if C is a class type other than object, all methods declared in an interface type are removed from the set. (This latter rule only has affect when the method group was the result of a member lookup on a type parameter having an effective base class other than object and a non-empty effective interface set.)
    public interface IInterfaceA { }
    public interface IInterfaceB : IInterfaceA { }

    public static class MyExtensions {
        public static void Print(this IInterfaceA a) => Console.WriteLine("A");
        public static void Print(this IInterfaceB b) => Console.WriteLine("B");
    }

    public class AB: IInterfaceA, IInterfaceB { }
    public class BA: IInterfaceB, IInterfaceA { }

    public partial class Program
    {
        static void Main(string[] args)
        {
            new AB().Print(); // B
            new BA().Print(); // B
        }
    }