我确定这是之前被问过的,我不知道要搜索什么,所以它很可能是重复的。
我有将新实体添加到数据库的代码。该实体引用了另一个实体(Role
),我可以通过服务获得它。服务会创建dbContext
的另一个实例,因此在获取上下文后,我必须将角色附加到上下文。问题是,当我尝试附加两个相同的角色时,出现此异常:
'Role'无法被跟踪,因为已经为{'Id'}设置了相同键值的另一个实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。考虑使用'DbContextOptionsBuilder.EnableSensitiveDataLogging'查看冲突的键值。'
我应该怎么做?下面的代码:
using (var context = new TenantContext(schemaName, connectionString))
{
ApprovalTemplates templates = new ApprovalTemplates();
ApprovalTemplate template = new ApprovalTemplate();
template.Approvers = new List<StageTemplate>();
foreach (var stage in request.Stages)
{
var temp = new StageTemplate();
temp.Order = stage.Order;
temp.Name = stage.Name;
var role = roleService.GetById(stage.RoleId, schemaName);//here I get the role
temp.AvailableActions = new List<ApprovalActionTemplate>();
foreach (var actionId in stage.Actions)
temp.AvailableActions.Add(context.ApprovalActions.First(a => a.Id == actionId));
//when I try to add already attached role, exception is thrown
context.TenantRoles.Attach(role);
temp.Role = role;
template.Approvers.Add(temp);
}
templates.PRApprovalTemplate = template;
context.ApprovalTemplates.Add(templates);
context.SaveChanges();
}
答案 0 :(得分:0)
我将通过Attach(共享)为这种情况和类似情况共享潜在的方法-规则非常简单,永远不要将具有相同ID的Entity附加两次。很好的一点是,有一种简单的方法可以检查它是否已附加以及是否已附加,您可以只使用该实体,因此最好的方法是在附加任何之前先始终检查本地实体。 strong>实体。 您的情况代替
var role = roleService.GetById(stage.RoleId, schemaName);//here I get the role
可能是:
var localRole = context.Set<TenantRole>().Local.FirstOrDefault(entry => entry.Id.Equals(stage.RoleId));
if (localRole == null)
{
localRole = new TenantRole
{
Id = stage.RoleId,
};
Context.TenantRoles.Attach(localRole);
}
...
temp.Role = localRole;
因为您知道RoleId,所以无需进行DB调用即可仅将TenantRole附加到上下文。
给出的代码可以正常工作,但是一旦有人拥有很多这样的地方,它就会变得繁重。可能的解决方案是为您的上下文创建扩展方法:
public static class RepositoryExtensions
{
public static T LocalContextEntitiesFinder<T>(this TenantContext context, Guid id) where T : class, ISomeInterfaceThatAllYourDBModelsImplements, new()
{
var localObj = context.Set<T>().Local.FirstOrDefault(entry => entry.Id.Equals(id));
if (localObj != null)
{
return localObj;
}
localObj = new T
{
Id = id
};
context.Set<T>().Attach(localObj);
return localObj;
}
}
因此,您将能够将代码重新编写为:
...
temp.Role = context.LocalContextEntitiesFinder<TenantRole>(id: stage.RoleId);
...
要使其正常工作,您应该添加与此类似的接口 ISomeInterfaceThatAllYourDBModelsImplements (代替Guid,您可以使用任何其他喜欢的类型):
public interface ISomeInterfaceThatAllYourDBModelsImplements
{
public Guid Id { get; set; }
}
并更新 TenantRole
public class TenantRole: ISomeInterfaceThatAllYourDBModelsImplements
{
[Key]
public Guid Id { get; set; }
...
我希望这可以对某人有所帮助。