我有一种方法,我尝试返回所有匹配性别的默认客户地址。我希望能够通过将System.Func方法传递给where子句来逐位构建过滤查询。
var emailAddresses = new List<string>();
// get all customers.
IQueryable<Customer> customersQ = base.GetAllQueryable(appContext).Where(o => o.Deleted == false);
// for each customer filter, filter the query.
var genders = new List<string>() { "C" };
Func<Customer, bool> customerGender = (o => genders.Contains(o.Addresses.FirstOrDefault(a => a.IsDefaultAddress).Gender));
customersQ = customersQ.Where(customerGender).AsQueryable();
emailAddresses = (from c in customersQ
select c.Email).Distinct().ToList();
return emailAddresses;
但是这个方法为每个地址(8000)调用数据库,这非常慢。
但是,如果我更换两行
Func<Customer, bool> customerGender = (o => genders.Contains(o.Addresses.FirstOrDefault(a => a.IsDefaultAddress).Gender));
customersQ = customersQ.Where(customerGender).AsQueryable();
一行
customersQ = customersQ.Where(o => genders.Contains(o.Addresses.FirstOrDefault(a => a.IsDefaultAddress).Gender)).AsQueryable();
然后查询只对数据库进行一次调用,速度非常快。
我的问题是为什么这会有所作为?如何只使用一次调用数据库就可以使第一个方法工作?
答案 0 :(得分:10)
使用表达而不是Func:
Expression<Func<Customer, bool>> customerGender = (o =>
genders.Contains(o.Addresses.FirstOrDefault(a => a.IsDefaultAddress).Gender));
customersQ = customersQ.Where(customerGender).AsQueryable();
当您使用简单的Func
委托时,会调用Where
的{{1}}扩展名。因此,所有数据都进入内存,枚举它并为每个实体执行lambda。你有很多数据库调用。
另一方面,当您使用表达式时,将调用Where
{{1}}的扩展名,并将表达式转换为SQL查询。这就是为什么你在第二种情况下有单一查询的原因(如果你使用就地lambda它被转换成表达式)。