简单的 LINQ 连接以返回 IQueryable 对象集合

Posted

技术标签:

【中文标题】简单的 LINQ 连接以返回 IQueryable 对象集合【英文标题】:Simple LINQ join to return IQueryable collection of objects 【发布时间】:2013-10-20 04:57:01 【问题描述】:

我查看了许多 SO 帖子,但没有找到 1 个回答我的问题。我是 LINQ 新手,所以我认为这很简单,我只是不明白。

我有 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-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 急切加载相关状态(即使您以后不访问它们)的解决方案是可以的。而且您最初使用联接的想法也可以。但是,由于您希望您的方法返回 IQueryable&lt;Address&gt;,因此您必须在调用站点上执行此操作(如上所述)。

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

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

【讨论】:

这给了我一个转换错误,(不能将类型'System.Linq.IQueryable'隐式转换为'System.Linq.IQueryable'),我明白为什么。我想要所有的地址字段,而不仅仅是州名。我必须明确列出它们吗?然后我必须投它吗?谢谢 @mwill:我将返回类型更改为IQueryable&lt;string&gt;。如果您想获取所有地址字段,那么您的代码就可以了。 JOIN 已经通过 State 属性的 ForeignKey 属性完成。 是的,我想要所有的地址字段。在我的原始代码中,State 为 NULL,所以我认为我必须自己编写连接。也许我在某处缺少一个设置?谢谢 对我来说这看起来完全没问题。确保数据库在两个表之间都有关系,当然还要确保有相关的状态记录(即 StateId 未设置为 NULL)。 我找到了一种解决方法,或者它实际上可能是解决方案。如果我使用 ".Include("State")" 那么我会得到地址实体中返回的状态数据。

以上是关于简单的 LINQ 连接以返回 IQueryable 对象集合的主要内容,如果未能解决你的问题,请参考以下文章

如何将两个连接表中的 IQueryable LINQ 结果返回到 List<string> 中?

更改存储库以支持 IQueryable(LINQ to SQL 查询)

使用 Distinct() 过滤 Linq 中的重复记录

NHibernate 3 LINQ:如何过滤 IQueryable 以仅选择 T 类及其子类的对象?

将 IQueryable 与 Linq 一起使用

MVC / LINQ /合并Iqueryable对象[关闭]