c# - ef core 多次附加同一个实体

标签 c# entity-framework-core

我确定之前有人问过这个问题,我不知道要搜索什么,所以它可能是重复的。
我有将新实体添加到数据库的代码。这个实体引用了另一个实体( Role ),我通过服务得到它。服务创建 dbContext 的另一个实例,所以我必须在获取它后将角色附加到上下文。问题是,当我尝试附加两个相同的角色时,出现以下异常:

'Role' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.'



我该怎么做?代码如下:
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();
}

最佳答案

我会与 Attach 分享针对这种情况和类似情况的潜在方法 - 规则非常简单,您永远不应该将具有相同 Id 的 Entity 附加两次。好的一点是,有一种简单的方法可以检查它是否已附加,如果已附加,您可以使用该实体,因此最好的方法是 总是 附加前检查本地实体 任何 实体。
对于您的情况代替

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,您就不需要为了将 TenantRole 附加到上下文而进行 DB 调用。

鉴于代码工作正常,但是一旦有人有很多这样的地方,它就会变得很重。对此的潜在解决方案是为您的上下文创建扩展方法:
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);
...

为了让它工作,你应该添加接口(interface) ISomeInterfaceThatAllYourDBModelsImplements 与此类似(代替 Guid,您可以使用任何其他您喜欢的类型):
public interface ISomeInterfaceThatAllYourDBModelsImplements
{
    public Guid Id { get; set; }
}

并更新 租户角色
public class TenantRole: ISomeInterfaceThatAllYourDBModelsImplements
{
    [Key]
    public Guid Id { get; set; }
...

我希望这可以帮助某人。

关于c# - ef core 多次附加同一个实体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54440265/

相关文章:

c# - 在 web.config 中指定相对文件位置以供标准 C# 类库使用

c# - Entity Framework Core 和 SQL Server 2016 时态表

c# - 让多个请求等待第一个完成(缓存)

c# - Selenium-如何迭代网页以查找所有必要的元素并执行单击操作

win-universal-app - 如何使用 ef core 1 在 UWP 中运行迁移

entity-framework-core - EfCore OwnsOne 种子可为空对象失败

c# - Entity Framework Core 急切加载然后包含在集合中

visual-studio-2017 - 包 X 与 netstandard2.0 不兼容

c# - 与 <li> 一起使用的 FindControl() 等效项

c# - 从 C# WPF 中的另一个类访问控件的属性