【问题标题】:How can I get value from .Include() using .ThenInclude()c如何使用 .ThenInclude()c 从 .Include() 中获取价值
【发布时间】:2019-05-02 12:07:07
【问题描述】:

我的项目中有一些用户过滤器,我想在这里显示每个用户的朋友。 UserFrom - 发送友谊请求的人,UserTo - 接受它的人。所以我需要知道下面代码中的Id才能选择相反的,因为它将是他的朋友。

var users = await _context.User
            .Where(u => userFilter.Gender != null ?
                u.Gender == userFilter.Gender : true)
            .Where(u => (userFilter.Languages != null &&
                         userFilter.Languages.Count() != 0) ?
                userFilter.Languages.Any(fl => u.Languages.Any(
                    ul => ul.LanguageCode == fl &&
                        LevelInRange(ul, userFilter.MinLevel))) : true)
            .Where(u => (userFilter.MaxDistance != null) ?
                LocationHelper.GetDistanceBetween((double)u.Longitude, (double)u.Latitude,
                longtitude, latitude) <= userFilter.MaxDistance : true)
            .Where(u => (userFilter.MaxAge != null) ?
                GetAge(u.Birthdate) <= userFilter.MaxAge : true)
            .Where(u => (userFilter.MinAge != null) ?
                GetAge(u.Birthdate) >= userFilter.MinAge : true)
            .Include(u => u.Languages)
            .ThenInclude(ul => ul.Language)
            .Include(u => u.CreatedEvents)
            .Include(u => u.Friends)
            .ThenInclude(f => f.UserTo) //The problem is here. How can I get u.Id there
            .Include(u => u.Credentials)
            .Include(u => u.Hobbies)
                .ThenInclude(h => h.Hobby)
            .ToListAsync();

【问题讨论】:

    标签: c# entity-framework linq lambda


    【解决方案1】:

    数据库管理系统针对选择数据进行了优化。较慢的部分之一是将所选数据传输到您的流程。因此,明智的做法是仅传输您实际计划使用的数据。

    如果你有一个一对多的关系,比如Schools 和他们的Students,并且School 10 有1000 个Students,那么这个School 中的每个Student 都会有一个外键SchoolId,值为 10。

    因此,如果您获取“学校 [10] 及其学生”,您已经知道学校 [10] 的每个 Student 都会有一个值为 10 的属性 SchoolId。这个值(您已经知道)将被传输 1000 次(如果您还算上学校的主键,则为 1001)。多么浪费处理能力!

    如果您使用实体框架查询数据,请始终使用Select。如果要更新获取的数据(更改、删除),请仅使用 Include

    使用 Select 可以让您以所需的格式仅选择所需的属性。

    回到你的问题

    唉,你忘了给我们上课。所以我们必须猜测它。似乎User 有零个或多个LanguagesCreatedEventsFriendsHobbies 等。其中一些是一对多的关系,可能大多数是多多对多关系:用户知道零种或多种语言。每种语言都由零个或多个用户使用。

    如果你关注了entity framework code first conventions,你可能有类似的类:

    class User
    {
        public int Id {get; set;}
        public string Name {get; set;}
    
        // every User has zero or more Hobbies (many-to-many)
        public virtual ICollection<Hobby> Hobbies {get; set;}
    
        // every Student has created zero or more events (one-to-many)
        public virtual ICollection<CreatedEvent> CreatedEvents {get; set;}
    
        ...
    }
    
    class Hobby
    {
        public int Id {get; set;}
        public string Name {get; set;}
        ...
    
        // every Hobby is practised by zero or more Users (many-to-many)
        public virtual ICollection<User> Users {get; set;}
    }
    
    class CreatedEvent
    {
        public int Id {get; set;}
        public string Name {get; set;}
        public DateTime Date {get; set;}
    
        // every event is created by exactly one User (one-to-many, using foreign key)
        public int UserId {get; set;}
        public virtual User User {get; set;}
    }
    

    等等

    在实体框架中,表的列由非虚拟属性表示。虚拟属性表示表之间的关系(一对多,多对多,...)

    因此,外键是非虚拟的。外键指向的项目是虚拟的。如果两个类有一个指向对方的virtual ICollection&lt;...&gt;,实体框架就知道存在多对多关系;如果两个类之一具有virtual ICollection&lt;...&gt; 而另一个具有virtual ...,则实体框架知道您打算设计一对多关系。

    如果您已正确创建类,尤其是virtual ICollections,则使用Select 的查询相当容易。您很少需要再进行(组)加入。因为您使用虚拟属性,所以实体框架知道需要一个(组)连接。

    var queryUsers = dbContext.User.Where(...).Where(...) ...
        .Select(user => new
        {
            // select only the user properties you really plan to use
            Id = user.Id,
            BirthDay = user.BirthDay,
    
            // Select the data in the format that you want, for example:
            FullName = user.FirstName + user.MiddleName + user.LastName,
    
            // SubCollections:
            Languages = user.Hobbies
               .Where(hobby => ...) // only if you don't want all this user's hobbies
               .Select(hobby => new
               {
                    // again, select only the hobby properties that you plan to use
                    Id = hobby.Id,
                    ...
    
                    // not needed, you already know the value:
                    // I know, it is probably a many-to-many, but let's suppose it is one-to-many
                    // UserId = hobby.UserId,
               })
               .ToList(),
    
               ...
        });
    

    现在您的问题出在属性Friends 中,您可以将其添加到您的选择中,就像您选择了Hobbies 一样

        Friends = user.Friends
            .Where(friend => ...)  // only if you don't want all Friends
            .Select(friend => new
            {
                // select the Friend properties you actually plan to use:
                Id = friend.Id,
                Name = friend.Name,
                ...
            })
            .ToList(),
    
       // continue the select
    

    【讨论】:

      【解决方案2】:

      IIRC,对于使用 .Include() 的孩子,您可以使用 Linq 表达式对孩子进行 Select()。

      return _context.User
       .Include(a => a.Friends.Select(c => c.UserTo));
      

      【讨论】:

        猜你喜欢
        • 2018-09-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-05-02
        • 1970-01-01
        • 2017-05-17
        • 2021-12-18
        • 2017-10-01
        相关资源
        最近更新 更多