我有一个非常简单的数据库,其中包含以下两个Customers和States。
State_Id
是引用 States
表主键的外键。
CREATE TABLE [dbo].[Customers](
[Id] [int] NOT NULL,
[FirstName] [varchar](50) NOT NULL,
[LastName] [varchar](50) NOT NULL,
[State_Id] [int] NULL,
CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED
)
ALTER TABLE [dbo].[Customers] WITH CHECK ADD CONSTRAINT [FK_Customers_States] FOREIGN KEY([State_Id])
REFERENCES [dbo].[States] ([Id])
GO
ALTER TABLE [dbo].[Customers] CHECK CONSTRAINT [FK_Customers_States]
GO
CREATE TABLE [dbo].[States](
[Id] [int] NOT NULL,
[Name] [varchar](50) NOT NULL,
CONSTRAINT [PK_States] PRIMARY KEY CLUSTERED
对应的 EF 模型类如下所示。
public class Customer
{
[Required]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual State State { get; set; }//Navigation Property
public int State_Id { get; set; }
}
public class State
{
public int Id { get; set; }
public string Name { get; set; }
//Navigation property
public virtual ICollection<Customer>? Customers { get; } = new List<Customer>();
}
OnModelCreating 方法看起来像这样
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>()
.HasKey(c => c.Id);
modelBuilder.Entity<State>()
.HasKey(c => c.Id);
modelBuilder.Entity<Customer>()
.HasOne(c => c.State)
.WithMany(s => s.Customers)
.HasForeignKey(p => p.State_Id);
modelBuilder.Entity<State>()
.HasMany(s => s.Customers);
}
现在我想使用名为 Customers 的 States 导航属性获取属于某个州的所有客户。
下面是代码。
public List<Customer> Get(string stateName)
{
var result = _appDbContext.States.Where(s => s.Name == stateName).FirstOrDefault();
//result.Customers.Count is always 1 even if city has more than 1 customers
//Alternatively below query returns correct result.
var correct = _appDbContext.Customers.Wehere ( c => c.State_Id == 2 ) ;
return customers;
}
我错过了什么吗?
如果没有启用预加载或延迟加载,EF 在涉及相关实体时的行为是 DbContext 将填充它当时正在跟踪的任何相关实体。这可能会导致特定情况下的奇怪行为。
在干净的上下文中,这样的陈述:
var result = _appDbContext.States.Where(s => s.Name == stateName).FirstOrDefault();
...总是会带着空的客户集合回来。您可能找回单个或多个客户的唯一原因是 DbContext 是否已经在跟踪与该状态关联的任何客户。当您请求状态时,EF 将扫描跟踪缓存以查找相关数据以自动关联。这可能是一个不完整的图片,一旦您添加
AsNoTracking()
来加快查询速度,这种行为就会停止导致查询中断的指控。
使用 EF 处理读取而不依赖于急切或延迟加载的最佳方法是读取 /w 投影,然后在执行更新时获取单个实体和显式关系 /w 急切加载。 (即使用
Select
)当您养成使用投影的习惯时,如果您忘记急切加载关系,则可以避免跟踪状态问题,并且您还可以构建比养成急切习惯的替代习惯更有效的查询加载所有内容“以防万一”。