我首先使用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; }
...
}
这似乎不对,我错过了什么吗?
感谢。
答案 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)....
也不是很优雅,但至少它由编译器检查; - )