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

发布于 2024-10-04 20:38:14 字数 2322 浏览 1 评论 0原文

我刚刚从 Linq 2 SQL 切换到实体框架,我在 EF 中看到一些奇怪的行为,我希望有人可以提供帮助。我尝试用谷歌搜索,但找不到其他人也有同样的问题。我模拟了一个场景来解释这种情况。

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

        // 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。

我意识到,如果 Companies 和 CompanyUsers 之间存在关联,那么我可以简单地执行此操作,并且效果很好:

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

...但我的示例只是一个更复杂场景的简化版本,其中我在实体。

所以我很困惑为什么实体框架抛出 NotSupportedException。当我直接使用 EF 上下文时,查询工作得很好,但如果我使用从另一个方法返回的 IQueryable,则不支持它。这在 Linq 2 SQL 中工作得很好,但在实体框架中似乎不起作用。

任何见解将不胜感激。

提前致谢。

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. 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. 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)...

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 (e.g. in my Services layer)

        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.

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. 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. This worked perfectly fine with Linq 2 SQL, but it doesn't seem to work in Entity Framework.

Any insight would be greatly appreciated.

Thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

夏雨凉 2024-10-11 20:38:16

我怀疑发生的情况是,EF 在第一个 select 的 lambda 中看到了 repository.GetCompanyUsers() 的表达式,并且不知道如何处理它,因为 < code>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(); 

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. I think that if you pass in the IQueryable directly instead of an expression that returns it, it should work.

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(); 
审判长 2024-10-11 20:38:16

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

如果您使用像 CompanyUsers 这样的属性而不是函数 GetCompanyUsers(),它应该可以工作。

很奇怪吧?

因此,

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

您可能会执行

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

“就参数化查询而言”(显然,您不能使用属性来执行此操作),请参阅此处回答的我的问题: 实体框架查询中的自定义函数有时可以正确翻译,有时则不能

您还可以有 wheres并且也在属性中选择;他们会翻译得很好。例如,如果我有一个带有 Articles 表的博客,其中包含一些不在线的文章:

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

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

This is one of those strange quirks with Linq to 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.

If instead of a function GetCompanyUsers() you use a property like CompanyUsers, it should work.

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文