在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;}
}
我也试过
.AsQueryable()
如何处理Linq to Objects
中可能0行的左连接?
答案 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
。