使用InMemory数据库时嵌套对象提交后丢失的原因及解决方法
问题:EF Core InMemory数据库中嵌套对象丢失的原因及解决办法
问题重现代码
实体类定义
public class Account { public int id {get; set;} public string Name {get; set;} public AccountEntry Entry {get; set;} } public class AccountEntry { public int id {get; set;} public DateTime date {get; set;} // 注:原代码中date应为DateTime,C#无原生date类型 public string description {get; set;} public decimal hour {get; set;} }
DbContext配置
protected override void OnConfiguring(DbContextOptionsBuilder options) { options.UseInMemoryDatabase("AccountDB"); }
添加记录方法
public void AddTimesheet(Account account) { _context.Accounts.Add(account); // 注:原代码Accouunts为拼写错误,修正为Accounts _context.SaveChanges(); }
查询记录方法
public IList<Account> GetAllAccounts() { return _context.Accounts.ToList(); }
问题现象
第一次提交数据后,同一次请求中能正常查询到包含AccountEntry的完整记录;但提交新数据后,再次查询时,所有旧记录的Account.Entry都变为null,而Name等实体自身属性仍正常保留。
原因分析
- 导航属性默认不自动加载:EF Core(包括InMemory数据库)默认不会自动加载导航属性(即嵌套的
AccountEntry)。第一次请求中,上下文还在跟踪刚添加的实体,导航属性会被上下文临时保留;但当使用新的上下文实例(比如新请求)查询时,默认只会查询Account表的自身字段,不会自动关联查询AccountEntry。 - 实体关联关系未明确配置:当前实体类没有定义外键字段,EF Core无法自动识别
Account和AccountEntry的关联关系,导致InMemory数据库无法正确维护两者的关联,新增数据后旧关联丢失。
解决办法
方法1:查询时显式加载导航属性
修改查询方法,使用Include方法指定要加载的导航属性,确保查询时同时获取AccountEntry数据:
using Microsoft.EntityFrameworkCore; // 需要引入此命名空间 public IList<Account> GetAllAccounts() { return _context.Accounts.Include(a => a.Entry).ToList(); }
方法2:明确配置实体关联关系
在DbContext的OnModelCreating方法中配置Account和AccountEntry的关联关系,同时补充外键字段,让EF Core能正确识别并维护关联:
第一步:修改实体类,添加外键
public class Account { public int id {get; set;} public string Name {get; set;} public AccountEntry Entry {get; set;} } public class AccountEntry { public int id {get; set;} public int AccountId {get; set;} // 添加外键,关联Account的id public DateTime date {get; set;} public string description {get; set;} public decimal hour {get; set;} public Account Account {get; set;} // 可选:添加反向导航属性 }
第二步:在DbContext中配置关系
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Account>() .HasOne(a => a.Entry) .WithOne(ae => ae.Account) .HasForeignKey<AccountEntry>(ae => ae.AccountId); }
之后结合方法1的Include查询,就能稳定获取包含嵌套对象的完整记录。
内容的提问来源于stack exchange,提问作者DevChu1996
相关产品推荐
相关产品推荐

