实体框架和存储库模式(IQueryable的问题)

时间:2010-11-27 06:43:55

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

我刚刚从Linq 2 SQL切换到Entity Framework,我在EF中看到了一些奇怪的行为,我希望有人可以提供帮助。我试过谷歌搜索,但我无法找到同样问题的其他人。我嘲笑了一个场景来解释这种情况。

如果我直接使用EF上下文,我可以在select中进行选择。例如,这执行得非常好:

        // this is an Entity Framework context that inherits from ObjectContext
        var dc = new MyContext();

        var companies1 = (from c in dc.Companies
                          select new {
                              Company = c,
                              UserCount = (from u in dc.CompanyUsers
                                           where u.CompanyId == c.Id
                                           select u).Count()
                          }).ToList();

但是,如果我使用存储库返回IQueryable(甚至是ObjectSet或ObjectQuery)的存储库模式,我会得到一个NotSupportedException(LINQ to Entities无法识别方法'System.Linq.IQueryable`1)...

以下是我的存储库的示例:

public class Repository {
    private MyContext _dc;

    public Repository() {
        _dc = new MyContext();
    }

    public IQueryable<Company> GetCompanies() {
        return _dc.Companies;
    }

    public IQueryable<CompanyUser> GetCompanyUsers() {
        return _dc.CompanyUsers;
    }
}

//我正在另一个类中使用存储库(例如在我的服务层中)

        var repository = new Repository();

        var companies2 = (from c in repository.GetCompanies()
                          select new {
                              Company = c,
                              UserCount = (from u in repository.GetCompanyUsers()
                                           where u.CompanyId == c.Id
                                           select u).Count()
                          }).ToList();

上面的代码抛出NotSupportedException。

我意识到如果公司和公司用户之间存在关联,那么我可以简单地做到这一点并且它会正常工作:

        var companies3 = (from c in repository.GetCompanies()
                          select new {
                              Company = c,
                              UserCount = (from u in c.CompanyUsers
                                           select u).Count()
                          }).ToList();

...但我的例子只是一个更复杂的场景的简化版本,我没有实体之间的关联。

所以我很困惑为什么Entity Framework会抛出NotSupportedException。当我直接使用EF上下文时,查询是如何工作完全正常的,但如果我正在使用从另一个方法返回的IQueryable,则不支持它。这与Linq 2 SQL完美配合,但它似乎在Entity Framework中不起作用。

非常感谢任何见解。

提前致谢。

2 个答案:

答案 0 :(得分:7)

我怀疑发生的事情是,EF在第一个repository.GetCompanyUsers()的lambda中看到select的表达式,并且不知道如何处理它,因为repository不是EF上下文。我认为,如果直接传递IQueryable而不是返回它的表达式,它应该可以工作。

如果你这样做怎么样:

    var companyUsers = repository.GetCompanyUsers();
    var companies2 = (from c in repository.GetCompanies() 
                      select new { 
                          Company = c, 
                          UserCount = (from u in companyUsers 
                                       where u.CompanyId == c.Id 
                                       select u).Count() 
                      }).ToList(); 

答案 1 :(得分:3)

这是Linq to SQL / EF的一个奇怪的怪癖。显然,他们实现了一种从getter 属性转换为SQL的方法,但不是从getter 函数转换为SQL的方法。

如果您使用GetCompanyUsers()这样的属性代替函数CompanyUsers,它应该有用。

很奇怪呃?

所以而不是

public IQueryable<CompanyUser> GetCompanyUsers() {
    return _dc.CompanyUsers;
}

你可以做

public IQueryable<CompanyUser> CompanyUsers {
    get { return _dc.CompanyUsers; }
}

就参数化查询而言(显然,您无法对属性进行查询),请参阅我的问题:Custom function in Entity Framework query sometimes translates properly, sometimes doesn't

您也可以在酒店中拥有wheresselects;他们会翻译得很好。例如,如果我的博客上有一篇文章表,其中包含一些不在线的文章:

public IQueryable<Article> LiveArticles {
    get { return _dc.Articles.Where(a => !a.IsDraft); }
}

这也会减少您需要的参数化查询的数量。