我的问题如下: 我有两个具有多对多关系的表:Users,UserRoles和Roles。我在编辑用户时正在尝试,以实现为用户设置角色的可能性。我在EF中使用ASP.NET MVC,在View中我使用复选框来设置新角色。在UserController中,我正在使用UserService,在其中我将新角色添加到已存在的用户的角色中。该服务正在使用存储库。我有一个通用存储库,它与工作单元一起工作。值是从视图到模型的映射。当我在尝试更新数据库中的角色和用户的同时,我收到以下错误:
发生了'System.InvalidOperationException'类型的异常 DAL.dll但未在用户代码中处理
附加信息:无法引用实体对象 IEntityChangeTracker的多个实例。
public class BaseRepository<T> where T: BaseEntity, new()
{
private DbContext db;
private DbSet<T> dbSet;
private UnitOfWork unitOfWork;
// constructors, where I make an instance of Unit of work
public BaseRepository()
{
db = new BookATableContext();
dbSet = db.Set<T>();
}
public BaseRepository(UnitOfWork unitOfWork)
{
db = new BookATableContext();
dbSet = db.Set<T>();
this.unitOfWork = unitOfWork;
}
//Get User by id
public virtual T GetById(int id)
{
return dbSet.Find(id);
}
// Update method
protected virtual void Update(T entity)
{
try
{
entity.UpdatedAt = DateTime.Now;
db.Entry(entity).State = EntityState.Modified;
db.SaveChanges();
}
catch (Exception e)
{
throw e;
}
}
}
// my implementation of Unit of work
public class UnitOfWork : IDisposable
{
private BookATableContext context;
private DbContextTransaction transaction;
public UnitOfWork()
{
this.context = new BookATableContext();
this.transaction = this.context.Database.BeginTransaction();
}
public void Commit()
{
if (transaction != null)
{
transaction.Commit();
transaction = null;
}
}
public void RollBack()
{
if (transaction != null)
{
transaction.Rollback();
transaction = null;
}
}
public void Dispose()
{
if (transaction != null)
{
transaction.Dispose();
transaction = null;
}
}
}
// UserService代码
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 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);
}
}
网上有很多关于这个错误的引用,即使是在SO上,但在阅读了所有这些意见和建议之后,我还没有找到解决方案或我收到此错误的原因。
答案 0 :(得分:0)
您的Update
中有一个方法BaseRepository
,它接收一个实体,并以某种方式将其附加到在此基本存储库的构造函数内创建的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
都不会使用DbContext
中Update()
方法中使用的同一BaseRepository
个实例。
我可以肯定这一点,因为RoleService
使用DbContext
来扮演角色有两种方式:
由于它继承了BaseService,BaseService
始终会创建BaseRepository
的新实例,从而创建DbContext
的新实例。
由于您使用UserService
按ID获取用户,并使用RoleService
获取角色,因此这两个服务类不使用相同的DbContext
,每个服务类都有使用自己的BaseRepository
拥有DbContext
。
您无法从一个DbContext
获取角色,并将其添加到从另一个DbContext
查询的用户。这不起作用:
var userService = new UserService();
var user = userService.GetById(1);
var roles = userService.GetSelectedRoles(null);
user.AddRoles(roles);
userService.Update(user);
您可以始终使用DbContext
:
UnitOfWork
来修复代码
// 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;
}
并且只要您需要创建UserService
或RoleService
,请使用相同的UnitOfWork
实例以确保没有2个DbContext实例。此外,您可能需要设置db
公开的UnitOfWork
属性(或创建公共获取者)。
因此,您还需要修复此方法:
public class Base`enter code here`Service<T> where T: BaseEntity, new()
{
private BaseRepository<T> Repository;
protected UnitOfWork unitOfWork;
// Must not use this constructor, we always need the unitofWork to share DbContext
// public BaseService()
// {
// this.Repository = new BaseRepository<T>();
// }
public BaseService(UnitOfWork unitOfWork)
{
this.unitOfWork = unitOfWork;
this.Repository = new BaseRepository<T>(this.unitOfWork);
}
}
因此,继承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();
}
Controller中的示例方法:
public ActionResult AddRoleToUser()
{
var unitOfWork = new UnitOfWork();
var userService = new UserService(unitOfWork); // This unitOfWork will be passed to RoleService and BaseRepository as well...
var user = userService.GetById(1);
var roles = userService.GetSelectedRoles(null);
user.AddRoles(roles);
userService.Update(user);
}