【发布时间】:2019-07-15 13:53:14
【问题描述】:
我正在学习。
我在 .netcoreapp 2.1 中使用Microsoft.EntityFrameworkCore,重点是通过 API 公开静态数据。
目前,我有一个 json 对象中的数据。让示例保持简单,我试图公开一份三明治和这些三明治的成分列表。三明治有多种类型(百吉饼、长款、短款等)
首先,我不完全确定使用 EF 是正确的工具,但由于我稍后必须管理这些项目(能够订购食物),所以我从那里开始。如果有更好的工具,我会全力以赴,但现在我的问题是使用 EF 来公开它。
我正在阅读应用程序中硬编码的 json,并将其用作我的DbContext 的起点。我只是在我的构造函数中反序列化它并将它加载到我的上下文对象中,然后将通过 API 公开。与待办事项列表模板项目一起工作就像一个魅力。
这就是它的样子,我只是根据需要添加了更多 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,这让我很困惑。根据我(有缺陷的?)的理解,两个表(DBSets?)可以有重复的 id。我可以有一个 ID 为 0 的三明治,类型为 0,成分为 0、1、2 和 3。如果类型从 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)
使用以下堆栈跟踪:
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% 感到困惑,我错过了什么?
【问题讨论】:
-
你能显示失败的代码和异常堆栈跟踪吗?
-
好的,原帖中已经修改了。
-
如果您应该使用 EF 并将所有内容存储在数据库中...为什么还需要从 JSON 文件中读取所有这些内容?这是一个无用的重复。选择一个或另一个。另外:不确定,但您可能想先添加类型,然后添加成分,最后添加项目。你也清理了你的数据库吗?
-
我正在使用该 JSON 来生成数据库的初始内容。那么它就不会再被使用了。
标签: c# json entity-framework