非空对象上的EF4 linq NullReferenceException

时间:2010-11-12 07:01:41

标签: c# .net linq entity-framework-4

我首先使用ef4代码和通用存储库。我的存储库有一个如下所示的select方法:

public IEnumerable<T> Select(Func<T, bool> predicate)
{
    return objectSet.Where(predicate);
}

我用以下代码调用它

pushQueueRepository.Select(x => x.User.ID == user.ID && x.PageID == pageID);

* note - pushQueueRepository已正确实例化。

当我运行它时,我得到一个NullReferenceException。抛出异常后,当我在调试中查看它时,它显示错误是x.User.ID == user.ID.当我鼠标悬停在x.User上时,它为空。但是当我展开x时,我们在x.User(非null)中有一个具有id的用户对象。

FYI x是一个PushQueue对象,定义如下:

public class PushQueue : IEntity
{
    ...

    [Required]
    public User User { get; set; }

    ... 
}

这似乎不对,我错过了什么吗?

感谢。

3 个答案:

答案 0 :(得分:2)

获取异常的原因是因为您正在内存中加载所有的PushQueues,然后尝试应用谓词:x => x.User.ID == user.ID,因为延迟加载是 NOT < / em>由您的代码启用,x.User将不会延迟加载,因此抛出异常。您没有将用户导航属性标记为虚拟,因此它不会选择加入EF延迟加载。在VS中以调试模式展开它时,您显式地加载它,但在运行时它并不是延迟加载的,这就是为什么在展开它时会看到它被填充的原因。

要解决此问题,您需要更改Select方法的签名,因为这是主要问题:您正在传递Func<T, bool>,而需要Expression<Func<T, bool>>。基本上您希望谓词在数据存储中而不是在内存中执行,因此您需要将代码更改为:

public IEnumerable<T> Select(Expression<Func<T, bool>> predicate)
{
    return objectSet.Where(predicate);
}

当然,你可以保持现在的select方法并启用延迟加载,这样,NullReferenceException就会消失但是会在运行时导致可怕的性能,因为EF会尝试延迟加载每个单独的用户PushQueue对象,然后应用您的谓词:

public virtual User User { get; set; }     

答案 1 :(得分:0)

通过展开x很可能会导致评估其他属性,然后填充x.User

当然,如果这是一个EF存储库,我希望实际的查询无论如何都要在数据库中执行,所以在查询失败的情况下你看到的有点不寻常。您是否尝试过查看SQL中正在执行的查询?

答案 2 :(得分:0)

我认为你需要使用.Include:

pushQueueRepository.Include("User").Select(x => x.User.ID == user.ID && x.PageID == pageID)

.Include强制EF立即加载User对象,否则会被懒惰加载,因此无法在User.ID上进行比较。

在.Include中有一个字符串不是很优雅,遗憾的是没有编译时检查。你可以这样做:

pushQueueRepository.Include(typeof(User).Name)....

也不是很优雅,但至少它由编译器检查; - )