您的代码存在一些问题。例如,你的FindAllBy
不执行sqlWHERE
查询,而是加载数据库中的所有条目,然后根据您的数据在内存中进行过滤criteria
。要理解为什么会这样,请看以下内容:
int a = 5;
long b = 5;
现在,这里发生的事情很明显,但仍然非常重要。编译器读取以下代码并生成两个变量。一个整数和一个长整型,其值都设置为数字 5。但是,这两个数字的值是不同的,即使它们(在源代码中)设置为相同的值。一种是 32 位,另一种是 64 位。
现在,我们来看看下面的代码:
Func<int, string> a = num => num.ToString();
Expr<Func<int, string>> b = num => num.ToString();
这里(或多或少)发生了同样的事情。在第一种情况下,C# 编译器认为您需要一个谓词(aFunc<int, string>
谓词),而第二个值是Expr<Func<int, string>>
即使值写成相同。然而,与第一个示例相反,这里的最终结果有很大不同。
谓词被编译为编译器生成的类上的方法。它的编译方式与任何其他代码一样,并且只允许您删除一堆样板文件。另一方面,表达式是实际编写的代码在内存中的表示。例如,在这种情况下,表达式可能类似于Call(int.ToString, $1)
。这可以由其他代码读取并转换为 SQL 等,然后用于查询数据库。
现在,回到你的问题。 EntityFramework 为您提供帮助IQueryable<T>
实例,进而继承IEnumerable<T>
。每当您枚举可枚举对象时,它都会查询数据库。
所有接受委托的扩展方法都定义在IEnumerable
从而查询你的数据库before运行谓词。这就是为什么您需要确保选择正确的方法重载。
编辑(回答评论)]
为了更清楚地说明这一点,我将举几个例子。比如说我们有一个User
包含的类FirstName
, LastName
and Age
,并且数据库集合被简单地称为db
.
Expr<Func<User, bool>> olderThan10 = u => u.Age > 10;
Func<User, bool> youngerThan90 = u => u.Age < 90;
var users = db.Where(olderThan10).Where(youngerThan90);
这将导致 SQL 找到所有年龄超过 10 岁的用户,然后它会在内存中过滤掉所有年龄超过或等于 90 岁的用户。
所以通过一个Func
并不一定意味着它查询整个数据库。这只是意味着它此时停止构建查询并执行它。
至于下一个问题,Expression<Func<T,bool>>
不是一个普遍的答案。它的意思是“一个接受 T 并返回 bool 的表达式”。在某些情况下,比如.Include
这引发了整个问题,你不想返回一个布尔值。您想要返回您想要包含的任何内容。例如,如果我们回到我们的用户示例,并修改Father
用户类上引用另一个用户的属性,我们希望将其包含在常规代码中
db.Include(u => u.Father);
现在。这里,u
是一个User,返回值u.Father
也是一个用户,所以在这种情况下u => u.Father
is Expr<Func<User, User>>
or Expr<Func<User, object>>
(不知道实体框架是否.Include
接受通用值或简单地object
s).
So your FindAll
函数应该看起来像这样:
public ICollection<TData> FindAll<TInclude>(Expr<Func<TData, TInclude>> include) {
using (var ctx = new TContext()) {
return ctx.T.Include(include).ToList();
}
}
不过,说实话,这是看起来很奇怪的代码,而且鉴于您(例如)已经命名了模型,您可能正在对模型做一些其他奇怪的事情T
and TContext
。我的猜测是您需要阅读一些有关 C# 中泛型如何工作的内容。