当使用Func<;TModel,TResult>;而不是内联选择器

本文关键字:选择器 gt Func lt TModel TResult | 更新日期: 2023-09-27 18:20:47

我有一个带有两个表的Linq2SQL上下文,一个表用于Users,一个用于Posts。最初我有这样的疑问:

var results = db.Users.Select(m => new UserModel
        {
            Id = m.Id,
            DisplayName = m.DisplayName,
            PostCount = m.Posts.Count(),
        }).ToList();

这很有效,没有n+1。然后,我决定编写第二个函数,以不同的方式过滤用户,并认为我会很聪明,将select移到Func<User, UserModel>中,然后从两个查询中调用它。这个查询特别更改为如下所示:

Func<User, UserModel> UserModelSelector =
        m => new UserModel
        {
            Id = m.Id,
            DisplayName = m.DisplayName,
            PostCount = m.Posts.Count(),
        };
var results = db.Users.Select(UserModelSelector).ToList();

只是现在,这个查询通过单独加载每个User的post计数来产生n+1。我尝试过将其公开/私有/内部、只读、静态的每一种组合,在所有情况下都会出现n+1。

有人能解释一下这里发生了什么吗

当使用Func<;TModel,TResult>;而不是内联选择器

您已经将选择器定义为lambda,因此LINQ别无选择,只能获取用户,然后依次对每个用户执行选择器,结果是n+1。

您可以通过将其定义为表达式树来避免这种情况:

Expression<Func<User, UserModel>> UserModelSelector = 
    m => new UserModel 
    { 
        Id = m.Id, 
        DisplayName = m.DisplayName, 
        PostCount = m.Posts.Count(), 
    }; 

这允许LINQ将整个投影转换为SQL,并将其作为单个查询执行。当然,在表达式树中,您只能使用可以转换为SQL的构造,但在这种情况下,一切都很好,正如我们从您最初的方法中看到和知道的那样。