简体   繁体   English

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

[英]Entity Framework and Repository Pattern (problem with IQueryable)

I just switched from Linq 2 SQL to Entity Framework, and I'm seeing some strange behaviors in EF that I'm hoping someone can help with. 我刚刚从Linq 2 SQL切换到Entity Framework,我在EF中看到了一些奇怪的行为,我希望有人可以提供帮助。 I tried Googling around, but I wasn't able to find other people with this same problem. 我试过谷歌搜索,但我无法找到同样问题的其他人。 I've mocked up a scenario to explain the situation. 我嘲笑了一个场景来解释这种情况。

If I work directly with an EF context, I'm able to do a select within a select. 如果我直接使用EF上下文,我可以在select中进行选择。 For example, this executes perfectly fine: 例如,这执行得非常好:

        // 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();

However, if I use a repository pattern where the repository is returning IQueryable (or even ObjectSet or ObjectQuery), I get a NotSupportedException (LINQ to Entities does not recognize the method 'System.Linq.IQueryable`1)... 但是,如果我使用存储库返回IQueryable(甚至是ObjectSet或ObjectQuery)的存储库模式,我会得到一个NotSupportedException(LINQ to Entities无法识别方法'System.Linq.IQueryable`1)...

Here is an example of my repository: 这是我的存储库的一个例子:

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;
    }
}

// I'm using the repository inside another class (eg in my Services layer) //我在另一个类中使用存储库(例如在我的Services层中)

        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();

The above code throws a NotSupportedException. 上面的代码抛出NotSupportedException。

I realize that if there's an association between Companies and CompanyUsers, then I can simply do this and it will work fine: 我意识到,如果公司和公司用户之间存在关联,那么我可以简单地做到这一点并且它可以正常工作:

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

...but my example is just a simplified version of a more complicated scenario where I don't have an association between the entities. ...但我的例子只是一个更复杂的场景的简化版本,我没有实体之间的关联。

So I'm very confused why Entity Framework is throwing the NotSupportedException. 所以我很困惑为什么Entity Framework会抛出NotSupportedException。 How is it that the query works perfectly fine when I'm working with the EF context directly, but it's not supported if I'm working with IQueryable returned from another method. 当我直接使用EF上下文时,查询是如何工作完全正常的,但如果我正在使用从另一个方法返回的IQueryable,则不支持它。 This worked perfectly fine with Linq 2 SQL, but it doesn't seem to work in Entity Framework. 这与Linq 2 SQL完美配合,但它似乎在Entity Framework中不起作用。

Any insight would be greatly appreciated. 任何见解将不胜感激。

Thanks in advance. 提前致谢。

I suspect that what's happening is that EF sees the expression for repository.GetCompanyUsers() inside the lambda for the first select and doesn't know what to do with it because repository isn't an EF context. 我怀疑正在发生的事情是,EF在第一个select的lambda中看到了repository.GetCompanyUsers()的表达式,并且不知道如何处理它因为repository不是EF上下文。 I think that if you pass in the IQueryable directly instead of an expression that returns it, it should work. 我认为如果直接传递IQueryable而不是返回它的表达式,它应该可以工作。

How about if you do this: 如果你这样做怎么样:

    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(); 

This is one of those strange quirks with Linq to SQL/EF. 这是Linq到SQL / EF的奇怪怪癖之一。 Apparently they implemented a way to translate from a getter property to SQL, but not a way to translate from a getter function to SQL. 显然,他们实现了一种从getter 属性转换为SQL的方法,但不是从getter 函数转换为SQL的方法。

If instead of a function GetCompanyUsers() you use a property like CompanyUsers , it should work. 相反,如果一个函数的GetCompanyUsers()你使用像一个属性CompanyUsers ,它应该工作。

Weird eh? 怪啊呃?

So instead of 而不是

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

You might do 你可能会这样做

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

As far as parameterized queries go (which you can't do with a property, obviously), see my question answered here: Custom function in Entity Framework query sometimes translates properly, sometimes doesn't 至于参数化查询去(显然你不能用属性做),请参阅我在这里回答的问题: 实体框架查询中的自定义函数有时会正确翻译,有时不会

You can also have wheres and selects in the property too; 您也可以wheres ,并selects在酒店过; they'll translate fine. 他们会翻译得很好。 For instance, if I had a blog with an Articles table that contains some articles that aren't online: 例如,如果我的博客上有一篇文章表,其中包含一些不在线的文章:

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

That'll also reduce the number of parameterized queries that you need. 这也将减少您需要的参数化查询的数量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM