2013-10-12 29 views
1

我已经看过很多SO帖子,但还没有找到1回答我的问题。我是LINQ的新手,所以我觉得这很简单,我只是没有得到它。简单的LINQ连接返回IQueryable对象集合

我有2个简单的实体:地址和状态,定义如下:

public class Address 
{ 
    [Key] 
    public int Id { get; set; } 
    public string CityName { get; set; } 
    public int? StateId { get; set; } 
    [ForeignKey("StateId")] 
    public virtual State State { get; set; } 
} 


public class State 
{ 
    [Key] 
    public int Id { get; set; } 
    public string StateName { get; set; } 
} 

所有我想要做的是返回地址的IQueryable的,但我想做一个SQL左相当于外部连接并返回地址中的StateName。

这是我迄今为止所返回的以“I”开头的城市的地址。这很好。我只需要加入状态表并获取StateName。

[HttpGet] 
public IQueryable<Address> Addresses() 
{ 
    var query = from a in _contextProvider.Context.Addresses 
    where a.CityName.StartsWith("I") 
    select a; 
    return query; 
} 

回答

1

您的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方法返回IQueryable,因此在您真正枚举查询之前,查询将不会执行。所以执行的SQL查询很大程度上取决于您如何使用由Addresses返回的查询。

如果先执行Addresses查询和访问国后LINQ到实体将不包括它们:

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来急切加载相关状态(即使您以后不访问它们)的解决方案也可以。而你最初使用连接的想法也是可以的。但既然你想让你的方法返回一个IQueryable<Address>你必须在呼叫站点上进行(如上所述)。

根据我的经验,启用延迟加载通常会使事情变得更容易。如果您不知道到底发生了什么,可能会导致数据库发生大量不必要的往返操作。但是您仍然可以使用Include来优化您的查询。为了更好地理解LINQ查询如何转换为SQL语句,我推荐使用SQL Profiler(或者如果使用SQL Server Express,则为免费工具,如Express Profiler)。

另一方面,使用显式加载也是一种痛苦。被迫包含所有相关表格可能导致huge datasets。如果由于某些原因,您不能包含所有相关表格,则必须明确检查实体是否已加载:context.Entry(address).Reference(a => a.State).IsLoaded。如果可为空的属性为null,则不知道它是否为数据库中的NULL或尚未加载。

+0

这给了我一个转换错误,(不能隐式转换类型'System.Linq.IQueryable '为'System.Linq.IQueryable '),我明白为什么。我需要所有地址字段,而不仅仅是州名。我必须明确列出它们吗?然后我会不得不施放它?谢谢 – mwill

+0

@mwill:我将返回类型更改为'IQueryable '。如果你想获得所有的地址字段,那么你的代码是完全正确的。 JOIN已通过State属性的ForeignKey属性完成。 – pescolino

+0

是的,我想要所有的地址字段。在我最初的代码中,状态是NULL,所以我想我必须亲自编写连接。也许有一个我在某处丢失的设置?谢谢 – mwill