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