您的 Address 类已经有一个表示关系的 State 属性。你为什么不直接使用它?
public IQueryable<string> Addresses()
{
var query = from a in _contextProvider.Context.Addresses
where a.CityName.StartsWith("I")
select a.State.StateName;
return query;
}
更新:
LazyLoadingEnabled
从您的最新评论中,我猜您通过设置为 false关闭了模型的延迟加载。默认情况下启用延迟加载,这可能有点令人困惑,因为旧版本的 EF 甚至不支持延迟加载。
禁用延迟加载“激活”显式加载,不出所料,您需要显式加载相关对象。在您的情况下,您可以使用以下Include
方法(这称为急切加载)来执行此操作:
var query = from a in _contextProvider.Context.Addresses.Include("State")
where a.CityName.StartsWith("I")
select a;
或访问查询中的相关属性(修改最终查询):
var states = (from a in Addresses() // Addresses is your query method
select a.State).ToList();
在第二个版本中,linq-to-entities 自动包含状态,因为您在查询中访问它们。请注意,您的Addresses
方法返回 an IQueryable
,因此在您实际枚举之前不会执行查询。所以执行的 SQL 查询很大程度上取决于你如何使用返回的查询Addresses
。
如果您首先执行Addresses
查询并访问以后的状态,则 linq-to-entities 将不包括它们:
var states = (from a in Addresses().ToList() // <- ToList() executes the query before states are accessed
select a.State).ToList();
延迟加载通常被关闭以避免不必要的数据库往返。如果启用延迟加载然后立即执行Addresses
每次访问相关对象返回的查询实际上会产生一个数据库查询。但同样:如果您在最终查询中包含状态,linq-to-entities 将通过生成 JOIN 自动包含它们(无论您是否使用延迟加载)。
因此,您Include
用于急切加载相关状态的解决方案(即使您以后不访问它们)也可以。而且您最初使用联接的想法也可以。但是由于您希望您的方法返回 anIQueryable<Address>
您必须在调用站点上执行此操作(如上所述)。
根据我的经验,启用延迟加载通常会使事情变得容易得多。如果您不确切知道发生了什么,可能会导致大量不必要的数据库往返。但是您仍然可以使用它Include
来优化您的查询。为了更好地理解 LINQ 查询是如何转换为 SQL 语句的,我建议使用 SQL Profiler(如果您使用 SQL Server Express,则可以使用像Express Profiler这样的免费工具)。
另一方面,使用显式加载也很痛苦。被迫包括所有相关的表可能会导致庞大的数据集。如果由于某些原因您不能包含所有相关表,则必须明确检查是否加载了实体:context.Entry(address).Reference(a => a.State).IsLoaded
。如果可空属性为空,您不知道它在数据库中是空的还是尚未加载。