【发布时间】:2016-10-26 12:37:09
【问题描述】:
我遇到了一个问题,我想了解它为什么会发生。一些帮助将不胜感激。
我正在使用实体框架核心,这是我在代码中的查询:
var user = await _db.User
.Where(x => x.Email == username & x.Suspended == null)
.Select(x => new
{
x.UserId,
x.Password,
x.Salt,
Role = x.Role.Name
})
.SingleOrDefaultAsync();
此查询不会选择用户,但不会引发任何错误。如果我从 Where 子句中删除“x.Suspended”,它将返回用户。
“Suspended”是通过用户 ID 与用户表相关的表。用户 ID 在两个表中都是 PK,所以关系是 1 对 1。
我认为这个查询不会返回用户,因为 x.Suspended 为空。有人可以帮助我了解为什么会发生这种情况以及我可以做些什么来让它发挥作用。
更新: 好的,我使用了 SQL Server Profiler,这是通过实体框架转换的 sql:
exec sp_executesql N'SELECT [x.Suspended].[UserId], [x.Suspended].[RCD], [x.Suspended].[Reason], [x].[UserId], [x].[Password], [x].[Salt], [x.Role].[Name]
FROM [User] AS [x]
INNER JOIN [Role] AS [x.Role] ON [x].[RoleId] = [x.Role].[RoleId]
INNER JOIN [Suspended] AS [x.Suspended] ON [x].[UserId] = [x.Suspended].[UserId]
WHERE [x].[Email] = @__username_0',N'@__username_0 varchar(80)',@__username_0='some-username'
这里的问题是所有的一对一关系都被转换为 INNER JOIN。我该如何处理这个问题?提前致谢!
更新 2: 我开始认为这是 EF Core 中的一个错误。例如当我有这个时:
var user = await _db.User
.Include(x => x.Role)
.Include(x => x.Suspended)
.Where(x => x.Email == username)
.SingleOrDefaultAsync();
在分析器下,sql输出是这样的:
exec sp_executesql N'SELECT TOP(2) [x].[UserId], [x].[Email], [x].[FirstName], [x].[LastName], [x].[Password], [x].[Phone], [x].[RCD], [x].[RoleId], [x].[Salt], [s].[UserId], [s].[RCD], [s].[Reason], [r].[RoleId], [r].[Name]
FROM [User] AS [x]
LEFT JOIN [Suspended] AS [s] ON [s].[UserId] = [x].[UserId]
INNER JOIN [Role] AS [r] ON [x].[RoleId] = [r].[RoleId]
WHERE [x].[Email] = @__username_0',N'@__username_0 varchar(80)',@__username_0='user-email'
显然,“暂停”表是左连接而不是内连接。但是当我有这个时:
var user1 = await _db.User
.Include(x => x.Role)
.Include(x => x.Suspended)
.Where(x => x.Email == username & x.Suspended == null)
.SingleOrDefaultAsync();
sql profiler下的sql是:
SELECT [x].[UserId], [x].[Email], [x].[FirstName], [x].[LastName], [x].[Password], [x].[Phone], [x].[RCD], [x].[RoleId], [x].[Salt], [x.Suspended].[UserId], [x.Suspended].[RCD], [x.Suspended].[Reason], [s].[UserId], [s].[RCD], [s].[Reason], [r].[RoleId], [r].[Name]
FROM [User] AS [x]
INNER JOIN [Suspended] AS [x.Suspended] ON [x].[UserId] = [x.Suspended].[UserId]
LEFT JOIN [Suspended] AS [s] ON [s].[UserId] = [x].[UserId]
INNER JOIN [Role] AS [r] ON [x].[RoleId] = [r].[RoleId]
真是一团糟:-D 我们到底是如何将“暂停”表加入两次,INNER 和 LEFT? WHERE 子句在哪里?
我想要的只是在 LINQ 中工作:
SELECT U.[UserId], U.[Password], U.[Salt], R.[Name] AS [Role]
FROM [ag].[dbo].[User] AS U
INNER JOIN [ag].[dbo].[Role] AS R ON U.[RoleId] = R.[RoleId]
LEFT JOIN [ag].[dbo].[Suspended] AS S ON U.[UserId] = S.[UserId]
WHERE U.[Email] = 'john.smith@gmail.com' AND S.[UserId] IS NULL
【问题讨论】:
-
但是
INNER JOIN将完全满足您的需求 - 删除带有nullSuspended的行。唯一让我感到惊讶的是Role表上的INNER JOIN,我相信这是由Select子句中的x.Role.Name引起的。试试Role = x.Role == null ? null : x.Role.Name,但我不确定这是否会有所帮助。 -
用户和挂起的关系是1..0-1而不是1:1
-
谢谢罗伯特,这里的想法是,如果用户被暂停,那么记录应该在“暂停”表中,并附上原因和日期。此查询用于身份验证,在用户通过身份验证并提供令牌(jwt)之前,我需要确保用户没有被暂停。 INNER join 在这里根本没有帮助。我确实理解他们为什么要这样设计。
-
角色是一个自己喜欢的表 暂停与用户的一对一关系。唯一的区别是用户记录有 RoleId 列。似乎 EF 将所有一对一关系转换为内部连接。
-
是的,因为所有 1:1 关系本质上都是内连接。
标签: c# linq async-await entity-framework-core