简体   繁体   中英

How can I get value from .Include() using .ThenInclude()c

I have some users filter in my project and I want to show each user's friends here. UserFrom - who send friendship request, UserTo - who accept it. So I need To know the Id in the code below to choose the opposite, beacuse it will be his friend.

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

Database management systems are optimized for selecting data. One of the slower parts is the transport of the selected data to your process. Hence it is wise to transport only the data that you actually plan to use.

If you have a one-to-many relation, like Schools with their Students , and School 10 has 1000 Students , then every Student of this School will have a foreign key SchoolId with a value 10.

So if you fetch "School [10] with its Students", you already know that every Student of school [10] will have a property SchoolId with a value 10. This value (that you already know) will be transported 1000 times (1001 if you also count the school's primary key). What a waste of processing power!

If you query data using entity framework, always use Select . Only use Include if you want to update the fetched data (change, delete)

Using Select enables you to select only the properties that you want, in the format that you want.

Back to your problem

Alas you forgot to give us your classes. So we'll have to guess it. It seems that a User has zero or more Languages , CreatedEvents , Friends , Hobbies , etc. Some of them will be a one-to-many relation, probably most of them will be a many-to-many relation: a user knows zero or more languages. Every language is spoken by zero or more Users.

If you've followed the entity framework code first conventions , you probably have classes similar to:

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

etc.

In entity framework, the columns of your tables are represented by non-virtual properties. The virtual properties represent the relations between the tables (one-to-many, many-to-many, ...)

Hence, a foreign key is non-virtual. The item that the foreign key points to is virtual. If two classes have a virtual ICollection<...> pointing towards each other, entity framework knows that there is a many-to-many relation; if one of the two classes has virtual ICollection<...> while the other has virtual ... then entity framework knows that you intended to design a one-to-many relation.

If you've created your classes properly, especially the virtual ICollections , a query using Select is fairly easy. You seldom have to do a (group-)join anymore. Because you use the virtual properties, entity framework knows that a (group-)join is needed.

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

           ...
    });

Now your problem is in property Friends , you can add it to your Select, just like you selected the 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

IIRC, You can Select() over children with Linq expressions like so for children using .Include().

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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