Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/275.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 一个实体对象不能被多个IEntityChangeTracker实例引用_C#_Asp.net Mvc_Entity Framework_Dbcontext_Invalidoperationexception - Fatal编程技术网

C# 一个实体对象不能被多个IEntityChangeTracker实例引用

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

我的问题如下: 我有两个具有多对多关系的表:用户、用户角色和角色。当我编辑一个用户时,我试图实现为该用户设置角色的可能性。我将ASP.NET MVC与EF一起使用,并在视图中使用复选框设置新角色。在UserController中,我使用UserService,在其中我向已经存在的用户角色添加新角色。该服务正在使用存储库。我有一个通用存储库,它与工作单元一起工作。这些值从视图映射到模型。当我试图同时更新数据库中的角色和用户时,出现以下错误:

中发生“System.InvalidOperationException”类型的异常 DAL.dll,但未在用户代码中处理
其他信息:实体对象不能由引用 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的类