具有相同ID的多个对象的实体框架跟踪问题

时间:2019-02-21 15:28:21

标签: c# json entity-framework

我正在学习。

我在.netcoreapp 2.1中使用Microsoft.EntityFrameworkCore,关键是要通过API公开静态数据。

当前,我将数据存储在json对象中。让示例保持简单,我正在尝试公开一盘三明治和这些三明治的配料。三明治有各种类型(百吉饼,长面包,短面包等)

首先,我不完全确定使用EF是正确的工具,但是由于稍后我必须管理这些项目(能够点菜),所以我从那里开始。如果有更好的工具供我使用,我现在不禁听到,但是现在我的问题是使用EF公开它。

我正在读取应用程序中经过硬编码的json,并将其用作我的DbContext的起点。我只是在构造函数中反序列化它,然后将其加载到我的上下文对象中,然后通过API将其公开。在todo-list模板项目中就像魅力一样。

这是它的外观,我只是添加了更多的DBSet以满足我的需求

public class EatupContext : DbContext
{
    public DbSet<FoodType> Types { get; set; }
    public DbSet<Ingredient> Ingredients { get; set; }
    public DbSet<FoodItem> Items { get; set; }
}

FoodType是带有ID和名称的int。与Ingredients相同。 物品是三明治,看起来像这样

public class FoodItem
{
    public int Id { get; set; }
    public string Name { get; set; }
    public FoodType Type { get; set; }
    public IEnumerable<Ingredient> Ingredients { get; set; }
}

在我正在读取的json(映射方式与c#对象相似)中,所有对象的所有id均从0开始。因此Types从0到7,从0到105的成分,从0到60的食品。

这会导致实体框架的ID跟踪问题,因为存在多个具有相同ID的对象。即使它们位于不同的DBSets中,这也让我感到困惑。根据我的理解(有缺陷?),两个表(DBSet?)可以具有重复的ID。我可以有一个类型为0,成分为0、1、2和3的三明治ID0。看来 more 会让我感到困惑,类型从0到7,然后成分从8到113,以及从114到174的三明治。从数据库角度来看,这对我来说真的很奇怪。

这是我得到的确切错误:

An unhandled exception occurred while processing the request.
InvalidOperationException: The instance of entity type 'Ingredient' 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.

Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap<TKey>.ThrowIdentityConflict(InternalEntityEntry entry) 

具有以下stacktrace:

Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap<TKey>.ThrowIdentityConflict(InternalEntityEntry entry)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap<TKey>.Add(TKey key, InternalEntityEntry entry, bool updateDuplicate)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(InternalEntityEntry entry)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, bool acceptChanges)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState entityState, bool acceptChanges, Nullable<EntityState> forceStateWhenUnknownKey)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.PaintAction(EntityEntryGraphNode node, bool force)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph<TState>(EntityEntryGraphNode node, TState state, Func<EntityEntryGraphNode, TState, bool> handleNode)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph<TState>(EntityEntryGraphNode node, TState state, Func<EntityEntryGraphNode, TState, bool> handleNode)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.AttachGraph(InternalEntityEntry rootEntry, EntityState entityState, bool forceStateWhenUnknownKey)
Microsoft.EntityFrameworkCore.DbContext.SetEntityState(InternalEntityEntry entry, EntityState entityState)
Microsoft.EntityFrameworkCore.DbContext.SetEntityStates(IEnumerable<object> entities, EntityState entityState)
Microsoft.EntityFrameworkCore.DbContext.UpdateRange(IEnumerable<object> entities)
Microsoft.EntityFrameworkCore.Internal.InternalDbSet<TEntity>.UpdateRange(IEnumerable<TEntity> entities)
EatUp.Backend.Controllers.EatupController..ctor(EatupContext context) in EatupController.cs
+
            _context.Items.UpdateRange(completeModel.Items);
lambda_method(Closure , IServiceProvider , object[] )
Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider+<>c__DisplayClass4_0.<CreateActivator>b__0(ControllerContext controllerContext)
Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider+<>c__DisplayClass5_0.<CreateControllerFactory>g__CreateController|0(ControllerContext controllerContext)
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()
Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

这会在控制器中的以下代码中发生:

    private readonly EatupContext _context;

    public EatupController(EatupContext context)
    {
        _context = context;
        var completeModel = JsonConvert.DeserializeObject<EatUpDataModel>(EatUpDataSet.Complete);

        _context.Items.UpdateRange(completeModel.Items);  //fails here
        _context.Types.UpdateRange(completeModel.Types);
        _context.Ingredients.UpdateRange(completeModel.Ingredients);

        _context.SaveChanges();
    }

如果我使用AddRange,它也会失败;我正在使用Update,所以我不必检查集合是否为空,我只是用json中的最新数据将其擦除。

我不确定应该采取什么方法,我真的不想手动编辑json,但是我看不到如何告诉EF这些是我自己以外的独立对象已经在做。

编辑:

我手动编辑了所有ID,使其只有唯一的ID,但仍然出现错误。

ID出现两次的唯一时间是在不同的三明治中使用相同的成分时,这应该可以接受。

现在我200%感到困惑,我想念什么?

1 个答案:

答案 0 :(得分:0)

通常,您不需要告诉EF这些是单独的对象。它应该解决。您可以复制并粘贴您的迁移吗?