2010-11-12 29 views
3

我首先在通用存储库中使用ef4代码。我的仓库有类似如下的选择方法:EF4 linq非null对象上的NullReferenceException

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

我用下面的代码

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

称之为*注 - pushQueueRepository已正确实例化。

当我运行这个我得到一个NullReferenceException。当我在抛出异常后的调试中看到它时,它显示错误是x.User.ID == user.ID.当我将鼠标移到x.User上时,它是空的。然而,当我展开x我们有一个用户对象在x.User(非空),确实有一个id。

FYI x是PushQueue对象定义为这样:

public class PushQueue : IEntity 
{ 
    ... 

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

    ... 
} 

这似乎并不正确,我失去的东西吗?

谢谢。

回答

2

之所以得到例外的是检查,因为您加载所有的在内存中,然后尝试应用您的谓词PushQueues:x => x.User.ID == user.ID因为延迟加载由代码启用后,将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); 
} 

当然,或者你可以保持选择的方法,因为它是现在让懒惰加载,这样一来,NullReferenceException异常会消失,但它会导致在自EF运行时的可怕表现将试图延迟加载用户的每一个PushQueue对象,然后运用你的断言:

public virtual User User { get; set; }  
+0

这对我来说非常合适。谢谢您的帮助。 – jimox 2010-11-15 06:22:41

+0

好解释!请注意,您不能输入任何谓词的任意函数。它需要由数据存储直接计算。 – 2010-11-16 16:50:06

0

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

当然,如果这是一个EF库,我期望反正数据库要执行的实际查询,让你在查询故障的情况下看到的是有点不寻常呢。你有没有尝试过在SQL中执行什么查询?

+0

当我签入profiler它本质上运行SELECT * FROM [PushQueues]。这是我所期望的。结果是正确的。由于用户不被标记为虚拟不应该立即加载? – jimox 2010-11-12 07:20:09

+0

如果x.User最初没有填充,那么是否有一些我需要做的事情来获得EF来允许我查询外键? – jimox 2010-11-12 07:33:21

0

我认为你需要使用在.include:

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

.INCLUDE力量EF加载用户对象向右走,否则就会延迟加载,从而无法对您的User.ID比较。

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

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

不是很优雅下去,但至少它是由编译器;-)

+0

Torben,感谢您的帮助。虽然我相信这会起作用,但我使用了Morteza的建议,因为它在这种情况下效果更好。干杯。 – jimox 2010-11-15 06:25:07