如何处理Linq对象中的一系列左连接?

时间:2012-02-27 15:40:44

标签: linq-to-objects

在linq中对像这样的数据库实体的真实查询工作得很好。在尝试使用linq对对象进行单元测试时,我得到一个空引用异常。

我在linqPad中重现了它。

 void Main()
{
    var _news= new [] {new {ID=0, ExpiryDate=(DateTime?)null }, new {ID=1,ExpiryDate=(DateTime?)DateTime.UtcNow.AddDays(-1)}};
    var _newsRegionSource = new RegionSource[]{};
    var _entitledRegions=new Region[] {};

    var validNews = from n in _news.Where(n=>n.ExpiryDate==null || n.ExpiryDate>DateTime.UtcNow)
        select n;
    var q = from n in validNews

            join r in _newsRegionSource
            on n.ID equals r.ID into rLeft
            from rn in rLeft.DefaultIfEmpty()

            join erL in _entitledRegions 
            // adding .DefaultIfEmpty() here instead moves the exception to erL.ID
            //NullReferenceException underlining rn at rn.RegionID
            on rn.RegionID equals erL.ID into erLeft
            from er in erLeft.DefaultIfEmpty()

            where rn==null | (rn.ID==n.ID && er!=null)
            select new {News=n,RegionID=(rn==null? (byte?)null: rn.RegionID)};
            var materialized=q.ToArray();
        materialized.Dump();

}

public class Region
{
    public byte ID {get;set;}
}
public class RegionSource
{
    public int ID{get;set;}
    public byte RegionID{get;set;}
}

我也试过

  • 确保所有源数组中至少有一个项目(但是对于此特定查询/案例,它们仍然会产生0行),
  • 将数组包装在.AsQueryable()

如何处理Linq to Objects中可能0行的左连接?

1 个答案:

答案 0 :(得分:0)

实体行为与LINQ对于左外连接的对象略有不同,正如您在此处发现的那样。你仍然可以通过一个非常轻微的调整使它工作。诀窍是提供一个连接表达式,避免解除引用null

我的第一次尝试,我有连接表达式:

        on (rn == null : Int32.MinValue : rn.RegionID) equals erL.ID into erLeft

Int32.MinValue看起来不太令人满意,因为它既是一个任意值,也是因为它可能是RegionID的有效值,具体取决于您的用法。另一个选择是使用一个可以为空的int,我发现它更令人满意,因为它提供相同的结果而不会劫持Int32.MinValue的情况。

        on (rn == null ? (Int32?) null : rn.RegionID) equals erL.ID into erLeft

现在将下一个条件中的OR(|)更改为短路(||),如下所示:

        where rn==null || (rn.ID==n.ID && er!=null)

现在,您应该按预期收到单个新闻,RegionID结果,而不是NullReferenceException