EF:"包括"导航属性,使用"选择"创建包装器对象时投影

时间:2016-01-21 13:56:00

标签: c# entity-framework linq-to-sql entity-framework-6

我在Include的查询中包含了导航属性,因此以后不会加载延迟。但是,当我创建一个带有Select投影的匿名包装对象时,它不起作用。

让我展示一下简化的例子。 实体

public class UserEntity {
    public string Name {get;set;}
    public virtual ICollection<UserEntity> Friends { get; set; }
}

查询

var entry = _dbCtx
    .Users
    .Include(x => x.Friends)
    // Select here is simplified, but it shows the wrapping
    .Select(user => new {
        User = user
    })
    .First();

// Here we have additional lazy loaded DB call
var friends = entry.User.Friends.Select(x => x.Name).ToList();

我也从生成的SQL中看到,不包括导航属性:

SELECT 
    [Limit1].[Name] AS [Name], 
    FROM ( SELECT TOP (1) 
        [Extent1].[Name] AS [Name]
        FROM [dbo].[Users] AS [Extent1]
    )  AS [Limit1]

在这种情况下是否可以Include导航属性Friends,以便User能够在不延迟加载的情况下获取数据?

我期待这也能奏效:

var entry = _dbCtx
    .Users
    .Select(user => new {
        User = user
    })
    .Include(x => x.User.Friends)
    .First();

但是得到例外:

  

InvalidOperationException:查询的结果类型既不是EntityType,也不是具有实体元素类型的CollectionType。只能为具有这些结果类型之一的查询指定包含路径。

我遇到了一些变通方法,但它们有些棘手:

  1. Select

    中为我们的匿名对象添加了附加属性
    var entry = _dbCtx
        .Users
        .Select(user => new {
            User = user,
            UsersFriends = user.Friends
        })
        .First();
    
    // manually copy the navigation property
    entry.User.Friends = user.UsersFriends;
    
    // Now we don't have any addition queries
    var friends = entry.User.Friends.Select(x => x.Name).ToList();
    
  2. 将用户映射到数据库级别的匿名对象,然后将属性映射到C#中的UserEntity

    var entry = _dbCtx
        .Users
        .Select(user => new {
            User = new {
                Name = user.Name,
                Friends = user.Friends
            }
        })
        .Take(1)
        // Fetch the DB
        .ToList()
        .Select(x => new {
            User = new UserEntity {
                Name = x.Name,
                Friends = x.Friends
            }
        })
        .First();
    
    // Now we don't have any addition queries
    var friends = entry.User.Friends.Select(x => x.Name).ToList();
    
  3. 现在,LEFT OUTER JOIN有一个Friends,但两种解决方法都不太好:

    1)附加属性和副本不是一种干净的方式。

    2)我的UserEntity还有更多其他属性。另外,每次添加新属性时,我们都应该在这里修改选择器。

    是否有某种方法可以实现导航属性,包括第一个样本?

    感谢您阅读,我希望有人知道这一点。

    修改

    我将扩展实体和查询以显示真实的用例。

    实体

    public class UserEntity {
        public string Name {get;set;}
        public int Score {get;set;}
        public virtual ICollection<UserEntity> Friends { get; set; }
    }
    

    查询

    var entry = _dbCtx
        .Users
        .Include(x => x.Friends)
        .Select(user => new {
            User = user,
            Position = _dbCtx.Users.Count(y => y.Score > user.Score)
        })
        .First();
    

1 个答案:

答案 0 :(得分:0)

不是_why_的答案,但想要更好的代码格式化......

我真的很惊讶它的工作方式。也许EF检测到您没有在投影中直接使用Friends属性,因此忽略了它。如果您封装了EF查询的 对象

,该怎么办?
var entry = _dbCtx
    .Users
    .Include(x => x.Friends)
    .Take(1);  // replicate "First" inside the EF query to reduce traffic
    .AsEnumerable()  // shift to linq-to-objects
    // Select here is simplified, but it shows the wrapping
    .Select(user => new {
        User = user
    })
    .First()