如何在 C# 中使用 Linq 方法语法检索具有相同外键 ID 的多个列值?
How to retrieve multiple column values with same foreign key id using Linq method Syntax in C#?
这个有效:
var query = (from user in _context.Users
join role in _context.UserRoles on user.UserID equals role.UserId
where user.Username == username
select role.Role).ToArray();
如何在方法语法中执行相同的操作?
//here role.Role has multiple values
var query2 = _context.Users.Join(_context.UserRoles, u=>u.UserID,ur=>ur.UserId,
(u,ur)=> new { ur.Role }).ToArray();
以上代码抛出错误:
Cannot implicitly convert type<anonymous // type string Role>[] to 'string[]'
最好使用更接近 SQL 的 LINQ 查询语法,您可以轻松修改查询。不管怎样,这是你的翻译:
var query2 = _context.Users
.Where(u => u.Username == username)
.Join(_context.UserRoles, u => u.UserID, ur => ur.UserId, (u,ur) => ur.Role)
.ToArray();
所以你有 Users
和 UserRoles
。 Users 和 UserRoles 之间存在 one-to-many 关系:每个 User 都有零个或多个 UserRoles;每个UseRole恰好属于一个User,即外键UserId所指的User。
您还有一个用户名,并且您希望所有用户都具有此名称,每个用户都有其用户角色。
注意:您没有说 UserName 是唯一的,所以在 Where
之后您仍然有几个用户名为“Will Smith”的用户。
简答
string userName = "Will Smith";
var result = dbContext.Users
.Where(user => user.UserName == userName)
.Join(dbContext.UserRoles,
user => user.Id, // from each User take the Id
userRole => userRole.UserId, // from each UserRole take the foreign key
(user, userRole) => userRole);
或者反过来:从 UserRoles 开始,只保留那些具有用户名的用户角色:
dbContext.UserRoles.Where (userRole =>
dbContext.Users
.Where(user => user.Id == userRole.UserId // get the User of this Role
&& user.UserName == userName) // check the name of this User
.Any() );
还有改进的余地
如果用户 [10] 有 20 个 UserRoles,那么用户 [10] 的每个 UserRole 都有一个值为 10 的外键。您将传输此值 20 次。
如果有多个“Will Smith”,您将得到一个大序列,所有“Will Smiths”的所有 UserRoles 随机混合。
您的解决方案将导致:
UserId UserRole
10 Administator,
10 User
25 Backup
10 Backup
18 User
25 User
将用户分组不是更有效,所以你有这样的东西:
UserId UserRoles
10 { Administator, User, Backup }
18 { User }
25 { User, Backup }
22 <no UserRoles yet>
注意:结果略有不同:您还得到了还没有角色的用户。
只要你有零个或多个子项的项目,比如学校有他们的学生,客户有他们的订单,或者用户有他们的用户角色,请考虑使用 Queryable.GroupJoin.[=20 的重载之一=]
大多数时候我使用带有参数 resultSelector 的重载。这样您就可以准确指定您想要的属性以及格式:
var usersWithTheirUserRoles = dbContext.Users
// keep only the users with a specific UserName
.Where(user => user.UserName == userName)
// fetch some properties of the remaining users and their UserRoles
.GroupJoin(dbContext.UserRoles,
user => user.Id, // from each User take the Id
userRole => userRole.UserId, // from each UserRole take the foreign key
// parameter resultSelector: take each user, with its zero or more userRoles
// to make one new:
(user, userRolesOfThisUser) => new
{
// Select only the user parameters that you plan to use:
Id = user.Id,
Address = user.Address,
...
// select the zero or more user roles of this user
UserRoles = userRolesOfThisUser.Select(userRole => new
{
// Select only the properties that you plan to use
Id = userRole.Id,
Description = userRole.Description,
...
// not needed, you've already got the value:
// UserId = userRole.UserId,
})
.ToList(),
});
优点:
- 您还会获得还没有 UserRoles 的用户(在您的原始要求中不是问题)
- 效率:用户的每个 属性 只发送一次。内部联接或左外部联接会一遍又一遍地发送用户的相同属性
- 效率:您只传输真正需要的属性
- 您可以偏离原始表格。如果您想省略 UserRoles,或计算一些属性,例如
UserRoleCount
,您可以这样做。这使得将来更改数据库表更容易,而无需更改此查询
这个有效:
var query = (from user in _context.Users
join role in _context.UserRoles on user.UserID equals role.UserId
where user.Username == username
select role.Role).ToArray();
如何在方法语法中执行相同的操作?
//here role.Role has multiple values
var query2 = _context.Users.Join(_context.UserRoles, u=>u.UserID,ur=>ur.UserId,
(u,ur)=> new { ur.Role }).ToArray();
以上代码抛出错误:
Cannot implicitly convert type<anonymous // type string Role>[] to 'string[]'
最好使用更接近 SQL 的 LINQ 查询语法,您可以轻松修改查询。不管怎样,这是你的翻译:
var query2 = _context.Users
.Where(u => u.Username == username)
.Join(_context.UserRoles, u => u.UserID, ur => ur.UserId, (u,ur) => ur.Role)
.ToArray();
所以你有 Users
和 UserRoles
。 Users 和 UserRoles 之间存在 one-to-many 关系:每个 User 都有零个或多个 UserRoles;每个UseRole恰好属于一个User,即外键UserId所指的User。
您还有一个用户名,并且您希望所有用户都具有此名称,每个用户都有其用户角色。
注意:您没有说 UserName 是唯一的,所以在 Where
之后您仍然有几个用户名为“Will Smith”的用户。
简答
string userName = "Will Smith";
var result = dbContext.Users
.Where(user => user.UserName == userName)
.Join(dbContext.UserRoles,
user => user.Id, // from each User take the Id
userRole => userRole.UserId, // from each UserRole take the foreign key
(user, userRole) => userRole);
或者反过来:从 UserRoles 开始,只保留那些具有用户名的用户角色:
dbContext.UserRoles.Where (userRole =>
dbContext.Users
.Where(user => user.Id == userRole.UserId // get the User of this Role
&& user.UserName == userName) // check the name of this User
.Any() );
还有改进的余地
如果用户 [10] 有 20 个 UserRoles,那么用户 [10] 的每个 UserRole 都有一个值为 10 的外键。您将传输此值 20 次。
如果有多个“Will Smith”,您将得到一个大序列,所有“Will Smiths”的所有 UserRoles 随机混合。
您的解决方案将导致:
UserId UserRole
10 Administator,
10 User
25 Backup
10 Backup
18 User
25 User
将用户分组不是更有效,所以你有这样的东西:
UserId UserRoles
10 { Administator, User, Backup }
18 { User }
25 { User, Backup }
22 <no UserRoles yet>
注意:结果略有不同:您还得到了还没有角色的用户。
只要你有零个或多个子项的项目,比如学校有他们的学生,客户有他们的订单,或者用户有他们的用户角色,请考虑使用 Queryable.GroupJoin.[=20 的重载之一=]
大多数时候我使用带有参数 resultSelector 的重载。这样您就可以准确指定您想要的属性以及格式:
var usersWithTheirUserRoles = dbContext.Users
// keep only the users with a specific UserName
.Where(user => user.UserName == userName)
// fetch some properties of the remaining users and their UserRoles
.GroupJoin(dbContext.UserRoles,
user => user.Id, // from each User take the Id
userRole => userRole.UserId, // from each UserRole take the foreign key
// parameter resultSelector: take each user, with its zero or more userRoles
// to make one new:
(user, userRolesOfThisUser) => new
{
// Select only the user parameters that you plan to use:
Id = user.Id,
Address = user.Address,
...
// select the zero or more user roles of this user
UserRoles = userRolesOfThisUser.Select(userRole => new
{
// Select only the properties that you plan to use
Id = userRole.Id,
Description = userRole.Description,
...
// not needed, you've already got the value:
// UserId = userRole.UserId,
})
.ToList(),
});
优点:
- 您还会获得还没有 UserRoles 的用户(在您的原始要求中不是问题)
- 效率:用户的每个 属性 只发送一次。内部联接或左外部联接会一遍又一遍地发送用户的相同属性
- 效率:您只传输真正需要的属性
- 您可以偏离原始表格。如果您想省略 UserRoles,或计算一些属性,例如
UserRoleCount
,您可以这样做。这使得将来更改数据库表更容易,而无需更改此查询