您必须了解 LINQ 使用的延迟执行。
假设db.Custs 是一个实现IQueryable<Cust> 或类似的对象。
如果您在第一个示例中检查a 的类型,您会看到它是IQueryable<Cust>。第二个示例中的一个是Cust。
如果您检查 LINQ 方法,您会注意到它们有两组:返回 IEnumerable<TResult>(或 IQueryable<TResult>)的一组和不返回的一组。
Select, Join, GroupBy 是第一组的示例。他们不执行查询。它们不代表获取的数据,它们代表选择数据和枚举数据的潜力。
只要您只连接该组中的函数。不查询您的数据来源。这个来源通常是一个数据库,但它可以是任何可枚举的、CSV 文件阅读器、JSON 文件、来自互联网的信息。在 LINQ 术语中,我们说这些方法使用延迟执行。您可以在该方法的每个描述中找到该术语。
第二组方法的示例是ToList, FirstOrDefault, Count, Any:返回不是IQueryable<...>。执行其中一个方法将开始枚举作为输入源的 IEnumerable/IQueryable:将获取第一个元素,如果有,并且如果方法需要,将获取序列的其他元素。
回到你的问题
您的第一个页面加载将创建查询。创造了从数据库中获取数据的潜力。唉,你忘了执行查询。如果您调试您的程序,并在断点处停止所有线程,并想检查 a 的值,您想查看对象 a 的属性。唉,你的调试器愿意向你展示简单属性的结果,但是联系数据库对它来说太难了。
因此,如果您真的想在调试时查看数据库查询的结果,则必须通过添加第二组的方法来执行查询。大多数情况下,人们会临时添加ToList()。
在您的第二个示例中,您使用了第二组中的方法:执行查询,并将结果放入本地内存中:调试器可以访问数据,因为它只需要检查一些内存。
所以调试解决方案很简单:在检查之前添加ToList。
改进
现在,您知道第一个示例中的变量a 表达了获取数据的可能性,而不是获取的数据本身。您显然简化了问题的代码,您想对数据做一些事情。
- 我很确定 SSEntities 实现了
IDisposable。您应该在不再需要它时立即将其丢弃。
- 确保在处置 SSEntities 之前获取数据(执行查询)。
通常人们使用以下结构来确保对象在所有情况下都被正确处理:
using (SSEntities db = new SSEntities())
{
var myQuery = db.Custs.Select(cust => new {...});
// execute the query:
var fetchedData = myQuery.ToList();
}
注意以下陷阱:
IQueryable<Cust> GetCustomers()
{
using (SSEntities db = new SSEntities())
{
return db.Custs.Select(cust => new {...});
}
}
查询未执行,但您已处置 SSEntities。你的编译器不会抱怨,你会在运行时得到一个异常。
正确的解决方案是让你的调用者创建和处理与数据库的连接,你只提供选择:
static IQueryable<Cust> GetCustomersByPostCode(this IQueryable<Cust> customers,
PostCode postCode)
{
return customers.Where(customer => customer.PostCode == postCode)
.Select(customer => new {...});
}
用法:
protected void Page_Load(object sender, EventArgs e)
{
List<Customer> customers = null;
using (SSEntities db = new SSEntities())
{
PostCode postCode = ...
customers = db.Customers.GetCustomersByPostCode(postCode).ToList();
}
// the database is disposed, cannot be used anymore
// customers already fetched, can still be used:
ProcessFetchedCustomers(customers);
}