如何为空导航过滤 IQueryable 属性
How to filter IQueryable for null navigation property
假设我有 2 个简单模型(如下),其中 'User' 包含导航 属性 到 'UserDetail'(一对一关系):
数据库
CREATE TABLE [dbo].[UserDetail]
(
[UserId] [int] NOT NULL PRIMARY KEY,
[Name] varchar(100) NULL
)
CREATE TABLE [dbo].[User]
(
[UserId] [int] NOT NULL PRIMARY KEY
)
C#.NET
[Table("UserDetail"), Schema="dbo"]
public class UserDetail
{
[Key, Required, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int UserId { get; set; }
public string Name { get; set; }
// Additional Properties Not Listed
}
[Table("User"), Schema="dbo"]
public class User
{
[Key, Required]
public int UserId { get; set; }
// Additional Properties Not Listed
[ForeignKey("UserId")]
public virtual UserDetail UserDetail { get; set; }
}
现在,.NET 应用程序正在使用存储库体系结构,因此我最初为 'User' table(默认 Entity Framework 行为;延迟加载):
IQueryable<User> users = DataRepository.UserRepository.List(); // 'List()' returns an IQueryable
我在这里的最终目标是简单地添加一个过滤器,它只会拉取 'User'(s) 没有关联的 'UserDetail' 记录(即导航 属性 将是空):
users = users.Where(m => m.UserDetail == null); // Also tried (m => m.UserDetail.Equals(null))
var testing = users.ToList();
但是在检查 'testing' 变量时,查询总是 returns 为空(在数据库中,有 'User' 条关联和不关联 'UserDetails' 的记录)。根据一些初步研究,这似乎可能是由于延迟加载造成的。为了验证,我尝试了以下示例:
users = users.Where(m => m.UserDetail.Name == "UserName");
var testing = users.ToList();
令我惊讶的是,这按照最初的预期工作; 'testing' 变量包含 'User' 条记录,这些记录有一条关联的 'UserDetail' 条记录,其中 'Name' 属性 等于 'UserName'。现在看来延迟加载不是问题,而是 IQueryable 比较 NULL 导航属性的潜在问题(在 SQL 查询转换期间?)。
在最终测试中,决定尝试使用 Include 方法查看是否有所不同,但它产生的结果与第一个测试相同(即为空):
users = users.Include(m => m.UserDetail).Where(m => m.UserDetail == null);
var testing = users.ToList();
所以我现在不知所措,显然我理解得不够好,无法产生我正在寻找的结果。会喜欢任何知道我在这个问题上被绊倒的人的任何 advice/help。
我最好的猜测是 IQueryable 查询将导航属性转换为 'JOIN'(s) 而不是 'LEFT JOIN'(s),因此不能用于比较 empty/null 相关记录(记录根本不存在)。
重述问题:
- 我如何在 .NET/Entity 框架中编写将在数据库上执行的查询,以仅提取没有关联 'UserDetail' 记录的 'User'(s)?
- 为什么 IQueryable 没有提取我在第一次测试中期望的记录?
- 澄清我对 IQueryable 功能的错误陈述的任何一般要点
提前感谢任何help/advice!
最终,问题归结为 Entity Framework 的关系配置(数据注释)不正确。正在尝试配置 one-to-(一个或 none)关系,Entity Framework 将其解释为 one-to-一个(两者都是必需的)。因此,IQueryable/Include 函数正在执行 JOIN 而不是所需的 LEFT JOIN。
我参考了两篇文章,使我产生了这种想法 (this and this)
所需的代码更改是针对 C#.NET 模型:
[Table("UserDetail"), Schema="dbo"]
public class UserDetail
{
// Other articles solution was to remove the 'Required' data annotation, but I found
// it not to be necessary (especially if it's used for validation)
[Key, Required, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int UserId { get; set; }
public string Name { get; set; }
// Additional Properties Not Listed
// Required so that 'UserId' can be considered 'nullable';
// not just a primary key (non-nullable)
[ForeignKey("UserId")]
// Needed to determine 'principle end' between 'User' and 'UserDetail' relationship
[Required]
public virtual UserDetail UserDetail { get; set; }
}
[Table("User"), Schema="dbo"]
public class User
{
[Key, Required]
public int UserId { get; set; }
// Additional Properties Not Listed
//[ForeignKey("UserId")] // Optional: not needed in my case but won't break anything
public virtual UserDetail UserDetail { get; set; }
}
现在 IQueryable 查询的工作方式为 expected/intended:
IQueryable<User> users = DataRepository.UserRepository.List().Where(m => m.UserDetail == null);
// Now, it only displays 'User' records that don't have an associated 'UserDetail' record
// FYI, no 'Include()' method required
var testing = users.ToList();
假设我有 2 个简单模型(如下),其中 'User' 包含导航 属性 到 'UserDetail'(一对一关系):
数据库
CREATE TABLE [dbo].[UserDetail]
(
[UserId] [int] NOT NULL PRIMARY KEY,
[Name] varchar(100) NULL
)
CREATE TABLE [dbo].[User]
(
[UserId] [int] NOT NULL PRIMARY KEY
)
C#.NET
[Table("UserDetail"), Schema="dbo"]
public class UserDetail
{
[Key, Required, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int UserId { get; set; }
public string Name { get; set; }
// Additional Properties Not Listed
}
[Table("User"), Schema="dbo"]
public class User
{
[Key, Required]
public int UserId { get; set; }
// Additional Properties Not Listed
[ForeignKey("UserId")]
public virtual UserDetail UserDetail { get; set; }
}
现在,.NET 应用程序正在使用存储库体系结构,因此我最初为 'User' table(默认 Entity Framework 行为;延迟加载):
IQueryable<User> users = DataRepository.UserRepository.List(); // 'List()' returns an IQueryable
我在这里的最终目标是简单地添加一个过滤器,它只会拉取 'User'(s) 没有关联的 'UserDetail' 记录(即导航 属性 将是空):
users = users.Where(m => m.UserDetail == null); // Also tried (m => m.UserDetail.Equals(null))
var testing = users.ToList();
但是在检查 'testing' 变量时,查询总是 returns 为空(在数据库中,有 'User' 条关联和不关联 'UserDetails' 的记录)。根据一些初步研究,这似乎可能是由于延迟加载造成的。为了验证,我尝试了以下示例:
users = users.Where(m => m.UserDetail.Name == "UserName");
var testing = users.ToList();
令我惊讶的是,这按照最初的预期工作; 'testing' 变量包含 'User' 条记录,这些记录有一条关联的 'UserDetail' 条记录,其中 'Name' 属性 等于 'UserName'。现在看来延迟加载不是问题,而是 IQueryable 比较 NULL 导航属性的潜在问题(在 SQL 查询转换期间?)。
在最终测试中,决定尝试使用 Include 方法查看是否有所不同,但它产生的结果与第一个测试相同(即为空):
users = users.Include(m => m.UserDetail).Where(m => m.UserDetail == null);
var testing = users.ToList();
所以我现在不知所措,显然我理解得不够好,无法产生我正在寻找的结果。会喜欢任何知道我在这个问题上被绊倒的人的任何 advice/help。
我最好的猜测是 IQueryable 查询将导航属性转换为 'JOIN'(s) 而不是 'LEFT JOIN'(s),因此不能用于比较 empty/null 相关记录(记录根本不存在)。
重述问题:
- 我如何在 .NET/Entity 框架中编写将在数据库上执行的查询,以仅提取没有关联 'UserDetail' 记录的 'User'(s)?
- 为什么 IQueryable 没有提取我在第一次测试中期望的记录?
- 澄清我对 IQueryable 功能的错误陈述的任何一般要点
提前感谢任何help/advice!
最终,问题归结为 Entity Framework 的关系配置(数据注释)不正确。正在尝试配置 one-to-(一个或 none)关系,Entity Framework 将其解释为 one-to-一个(两者都是必需的)。因此,IQueryable/Include 函数正在执行 JOIN 而不是所需的 LEFT JOIN。
我参考了两篇文章,使我产生了这种想法 (this and this)
所需的代码更改是针对 C#.NET 模型:
[Table("UserDetail"), Schema="dbo"]
public class UserDetail
{
// Other articles solution was to remove the 'Required' data annotation, but I found
// it not to be necessary (especially if it's used for validation)
[Key, Required, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int UserId { get; set; }
public string Name { get; set; }
// Additional Properties Not Listed
// Required so that 'UserId' can be considered 'nullable';
// not just a primary key (non-nullable)
[ForeignKey("UserId")]
// Needed to determine 'principle end' between 'User' and 'UserDetail' relationship
[Required]
public virtual UserDetail UserDetail { get; set; }
}
[Table("User"), Schema="dbo"]
public class User
{
[Key, Required]
public int UserId { get; set; }
// Additional Properties Not Listed
//[ForeignKey("UserId")] // Optional: not needed in my case but won't break anything
public virtual UserDetail UserDetail { get; set; }
}
现在 IQueryable 查询的工作方式为 expected/intended:
IQueryable<User> users = DataRepository.UserRepository.List().Where(m => m.UserDetail == null);
// Now, it only displays 'User' records that don't have an associated 'UserDetail' record
// FYI, no 'Include()' method required
var testing = users.ToList();