C# 一个实体对象不能被多个IEntityChangeTracker实例引用
我的问题如下: 我有两个具有多对多关系的表:用户、用户角色和角色。当我编辑一个用户时,我试图实现为该用户设置角色的可能性。我将ASP.NET MVC与EF一起使用,并在视图中使用复选框设置新角色。在UserController中,我使用UserService,在其中我向已经存在的用户角色添加新角色。该服务正在使用存储库。我有一个通用存储库,它与工作单元一起工作。这些值从视图映射到模型。当我试图同时更新数据库中的角色和用户时,出现以下错误: 中发生“System.InvalidOperationException”类型的异常 DAL.dll,但未在用户代码中处理C# 一个实体对象不能被多个IEntityChangeTracker实例引用,c#,asp.net-mvc,entity-framework,dbcontext,invalidoperationexception,C#,Asp.net Mvc,Entity Framework,Dbcontext,Invalidoperationexception,我的问题如下: 我有两个具有多对多关系的表:用户、用户角色和角色。当我编辑一个用户时,我试图实现为该用户设置角色的可能性。我将ASP.NET MVC与EF一起使用,并在视图中使用复选框设置新角色。在UserController中,我使用UserService,在其中我向已经存在的用户角色添加新角色。该服务正在使用存储库。我有一个通用存储库,它与工作单元一起工作。这些值从视图映射到模型。当我试图同时更新数据库中的角色和用户时,出现以下错误: 中发生“System.InvalidOperationE
其他信息:实体对象不能由引用 IEntityChangeTracker的多个实例
//用户服务代码
public class UserService:BaseService<User>
{
public UserService() : base()
{
}
public UserService(UnitOfWork unitOfWork) : base(unitOfWork)
{
}
public List<SelectItem> GetSelectedRoles(List<Role> roles)
{
roles = roles ?? new List<Role>();
return new RoleService()
.GetAll()
.Select(r => new SelectItem
{
Text = r.Name,
Value = r.Id.ToString(),
Selected = roles.Any(ar => ar.Id == r.Id)
})
.ToList();
}
public List<Role> GetUpdatedUserRoles(List<Role> roles, string[] selectedRoles)
{
selectedRoles = selectedRoles ?? new string[0];
roles = roles ?? new List<Role>();
return roles = new RoleService(unitOfWork)
.GetAll()
.Where(r => selectedRoles.Any(sr => r.Id == Convert.ToInt32(sr)))
.ToList();
}
}
公共类UserService:BaseService
{
public UserService():base()
{
}
公共用户服务(UnitOfWork UnitOfWork):基础(UnitOfWork)
{
}
公共列表GetSelectedRoles(列表角色)
{
角色=角色??新列表();
返回新角色服务()
.GetAll()
.选择(r=>new SelectItem
{
Text=r.名称,
Value=r.Id.ToString(),
Selected=roles.Any(ar=>ar.Id==r.Id)
})
.ToList();
}
公共列表GetUpdateUserRoles(列表角色,字符串[]selectedRoles)
{
selectedRoles=selectedRoles??新字符串[0];
角色=角色??新列表();
返回角色=新角色服务(unitOfWork)
.GetAll()
.Where(r=>selectedRoles.Any(sr=>r.Id==Convert.ToInt32(sr)))
.ToList();
}
}
//UserService继承BaseService,它具有以下构造函数
public class Base`enter code here`Service<T> where T: BaseEntity, new()
{
private BaseRepository<T> Repository;
protected UnitOfWork unitOfWork;
public BaseService()
{
this.Repository = new BaseRepository<T>();
}
public BaseService(UnitOfWork unitOfWork)
{
this.unitOfWork = unitOfWork;
this.Repository = new BaseRepository<T>(this.unitOfWork);
}
public T GetById(int id)
{
return this.Repository.GetById(id);
}
public class Base`enter code here`Service where T:BaseEntity,new()
{
专用数据库;
受保护的工作单元工作单元;
公共服务()
{
this.Repository=newbaserepository();
}
公共基础服务(UnitOfWork UnitOfWork)
{
this.unitOfWork=unitOfWork;
this.Repository=新的BaseRepository(this.unitOfWork);
}
公共T GetById(int-id)
{
返回this.Repository.GetById(id);
}
}
即使在这里,网上也有很多关于这个错误的参考资料,但是在阅读了所有这些评论和建议之后,我还没有找到解决方案或者我出现这个错误的原因。你的
基本存储库中有一个方法更新
,它接收一个实体,并以某种方式将其附加到一个DbContext
,它是在这个基本存储库的构造函数中创建的
您还有一个GetById
方法,可以调用该方法来获取该用户,添加角色,然后传递到Update
方法
问题是您有两个服务类:UserService和RoleService,它们都继承了BaseService
那么你称之为:
return new RoleService()
.GetAll()
.Select(r => new SelectItem
{
Text = r.Name,
Value = r.Id.ToString(),
Selected = roles.Any(ar => ar.Id == r.Id)
})
.ToList();
或者这个:
return roles = new RoleService(unitOfWork)
.GetAll()
.Where(r => selectedRoles.Any(sr => r.Id == Convert.ToInt32(sr)))
.ToList();
在这两种情况下,RoleService
不会使用来自BaseRepository
的Update()
方法中使用的相同的DbContext
实例
我可以证实这一点,因为您的RoleService
使用DbContext
获取角色有两种方式:
由于它继承了BaseService,BaseService
始终创建BaseRepository
的新实例,这将创建DbContext
的新实例
由于您使用UserService
通过id获取用户,并使用RoleService
获取角色,因此这两个服务类使用的不是相同的DbContext
,每个服务类都有自己的BaseRepository
和自己的DbContext
TL;博士
您无法从一个DbContext
获取角色,并将其添加到从另一个DbContext
查询的用户中。这是行不通的:
var userService = new UserService();
var user = userService.GetById(1);
var roles = userService.GetSelectedRoles(null);
user.AddRoles(roles);
userService.Update(user);
您可以通过始终使用工作单元中的DbContext
来修复代码:
// Never use this, unless you are using only one entity (e.g user)
// and not using two or more related (e.g users and roles)
// public BaseRepository()
// {
// db = new BookATableContext();
// dbSet = db.Set<T>();
// }
public BaseRepository(UnitOfWork unitOfWork)
{
// Don't create a new DbContext instance, use a unique shared instance
// from UnitOfWork
// db = new BookATableContext();
dbSet = db.Set<T>();
this.unitOfWork = unitOfWork;
this.db = unitOfWork.db;
}
因此,所有继承BaseService的类都必须具有UnitOfWork的构造函数,您不能使用无参数构造函数…还要解决以下问题:
public List<SelectItem> GetSelectedRoles(List<Role> roles)
{
roles = roles ?? new List<Role>();
return new RoleService(this.unitOfWork) // always pass unitOfWork
.GetAll()
.Select(r => new SelectItem
{
Text = r.Name,
Value = r.Id.ToString(),
Selected = roles.Any(ar => ar.Id == r.Id)
})
.ToList();
}
在BaseRepository
中有一个方法Update
,该方法接收一个实体,并以某种方式将其附加到一个DbContext
,该方法是在该基本存储库的构造函数中创建的
您还有一个GetById
方法,可以调用该方法来获取该用户,添加角色,然后传递到Update
方法
问题是您有两个服务类:UserService和RoleService,它们都继承了BaseService
那么你称之为:
return new RoleService()
.GetAll()
.Select(r => new SelectItem
{
Text = r.Name,
Value = r.Id.ToString(),
Selected = roles.Any(ar => ar.Id == r.Id)
})
.ToList();
或者这个:
return roles = new RoleService(unitOfWork)
.GetAll()
.Where(r => selectedRoles.Any(sr => r.Id == Convert.ToInt32(sr)))
.ToList();
在这两种情况下,RoleService
不会使用来自BaseRepository
的Update()
方法中使用的相同的DbContext
实例
我可以证实这一点,因为您的RoleService
使用DbContext
获取角色有两种方式:
由于它继承了BaseService,BaseService
始终创建BaseRepository
的新实例,这将创建DbContext
的新实例
由于您使用UserService
通过id获取用户,并使用RoleService
获取角色,因此这两个服务类使用的不是相同的DbContext
,每个服务类都有自己的BaseRepository
和自己的DbContext
TL;博士
您无法从一个DbContext
获取角色,并将其添加到从另一个DbContext
查询的用户中。这是行不通的:
var userService = new UserService();
var user = userService.GetById(1);
var roles = userService.GetSelectedRoles(null);
user.AddRoles(roles);
userService.Update(user);
您可以通过始终使用工作单元中的DbContext
来修复代码:
// Never use this, unless you are using only one entity (e.g user)
// and not using two or more related (e.g users and roles)
// public BaseRepository()
// {
// db = new BookATableContext();
// dbSet = db.Set<T>();
// }
public BaseRepository(UnitOfWork unitOfWork)
{
// Don't create a new DbContext instance, use a unique shared instance
// from UnitOfWork
// db = new BookATableContext();
dbSet = db.Set<T>();
this.unitOfWork = unitOfWork;
this.db = unitOfWork.db;
}
所以所有继承BaseServic的类