DbContext派生类异常:将DbSet对象转入ViewModel列表时数据重复
嘿,这个问题我之前踩过好几次坑,大概率和EF Core的上下文生命周期或者测试数据初始化逻辑有关,咱们一步步来排查解决:
1. 先排查测试数据初始化(TempDB类)的逻辑
这是最常见的原因!如果你的TempDB是在DbContext的构造函数、OnModelCreating或者每次上下文实例化时执行添加数据的操作,那每创建一次DbContext,就会重复添加一遍测试数据,自然会出现重复对象。
解决办法:
添加测试数据前先判断数据是否已存在,避免重复插入:
// 在TempDB或者DbContext的初始化逻辑里 using(var context = new YourDbContext()) { if (!context.Users.Any()) // 检查用户表是否已有数据 { // 手动添加你的User测试数据 context.Users.AddRange(new List<User> { new User { Id = 1, Name = "User1" }, new User { Id = 2, Name = "User2" } // 其他用户数据 }); } if (!context.Blogs.Any()) // 检查博客表是否已有数据 { // 添加Blog测试数据 context.Blogs.AddRange(new List<Blog> { new Blog { Id = 1, Title = "Blog1", UserId = 1 }, new Blog { Id = 2, Title = "Blog2", UserId = 2 } // 其他博客数据 }); } context.SaveChanges(); }
或者更规范的方式是用EF Core的种子数据初始化,这种方式只会在数据库创建/迁移时执行一次,不会重复添加:
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 配置User的种子数据 modelBuilder.Entity<User>().HasData( new User { Id = 1, Name = "User1" }, new User { Id = 2, Name = "User2" } ); // 配置Blog的种子数据 modelBuilder.Entity<Blog>().HasData( new Blog { Id = 1, Title = "Blog1", UserId = 1 }, new Blog { Id = 2, Title = "Blog2", UserId = 2 } ); }
2. 检查DbContext的生命周期配置
如果你的应用(比如ASP.NET Core)里DbContext被错误地注册成了Singleton(单例),那整个应用生命周期里只有一个上下文实例,重复执行数据操作时,EF的实体跟踪机制会把重复添加的实体当成新对象,最终导致数据库里出现重复数据。
正确的生命周期配置(ASP.NET Core):
把DbContext注册为Scoped(每个请求一个实例),这是EF Core推荐的方式:
// 在Startup.cs或者Program.cs里 services.AddDbContext<YourDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
如果是手动创建DbContext实例,记得每次使用完就调用Dispose(),不要重复使用同一个实例执行多次添加操作。
3. 优化ViewModel赋值的逻辑
如果你直接把DbSet的实体引用赋值给ViewModel的List,EF的实体跟踪可能会带来一些意外问题(比如重复附加实体)。建议在获取数据时使用AsNoTracking(),让EF不跟踪这些实体,既避免问题又提升性能:
// 在LoginConfirmed动作里 var userList = _context.Users.AsNoTracking().ToList(); viewModel.UserList = userList; var blogList = _context.Blogs.AsNoTracking().ToList(); viewModel.BlogList = blogList;
4. 确认数据库本身的数据
有时候看起来是DbSet里的重复,其实是数据库里已经存在重复数据了。可以直接打开数据库表(比如用SQL Server Management Studio),检查测试数据是不是真的被插入了多次。如果是,那还是回到第一步的初始化逻辑问题。
内容的提问来源于stack exchange,提问作者WhatAmIDoing

