我有这样一行代码:
var attachments = EntityRepository<Attachment>().Entities
.Where(at => at.EntityType == EntityType.EmailTemplate)
.ToDictionary(at => at.Extension, at => at);
EntityRepository<Attachment>().Entities
属于System.Data.Entity.Infrastructure.DbQuery<TResult>
型,它同时实现IQueryable<TResult>
和IEnumerable<TResult>
。
我如何确定它是充当IEnumerable<T>
(即从数据库中检索所有行,然后在 C# 中进行过滤(,还是充当IQueryable<T>
(将 C# 谓词转换为 SQL 查询并仅检索这些行(。
我想你可能对IEnumerable
有轻微的误解。它只是说该类支持迭代。它不会直接影响任何数据的获取方式。
此外,IQueryable
实现了IEnumerable
,因此所有IQueryable
实例也IEnumerable
。这是有道理的,因为您可以迭代结果。
在您的示例中,缺少IQueryable
意味着"从数据库中检索所有行,然后在 C# 中进行过滤"。
LINQ 中有 2 个不同的扩展 - 用于 IEnumerable 和 IQueryable。
当您编写EntityRepository<Attachment>().Entities
.Where(at => at.EntityType == EntityType.EmailTemplate)
编译器会检查Entities
的类型,并在声明"更具体"时IQueryable
编译器选择Queryable.Where()
方法,IQueryProvider 会将表达式转换为 SQL。编写时.ToDictionary(at => at.Extension, at => at)
编译器找不到Queryable.ToDictionary()
因此它会回退到Enumerable.ToDictionary()
并在内存中筛选项目。
扩展方法调用的规则在 C# 语言规范中定义:
- 候选方法集减少为仅包含派生最多的类型的方法:对于集合中
C.F
的每个方法(其中C
是声明方法F
的类型(,以基类型C
声明的所有方法都将从集合中删除。此外,如果C
是object
以外的类类型,则接口类型中声明的所有方法都将从集合中删除。(仅当方法组是对具有对象以外的有效基类和非空有效接口集的类型参数进行成员查找的结果时,后一条规则才具有影响。
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
}
}