【问题标题】:I'm adding an Entity (Category) as a property in another Entity (Product). System.ArgumentException: An item with the same key has already been added我正在添加一个实体(类别)作为另一个实体(产品)中的属性。 System.ArgumentException:已添加具有相同键的项目
【发布时间】:2021-07-05 14:10:58
【问题描述】:

在我的 API 项目中,实体类别如下:

    [Key]
    public string Id { get; set; }                
    public string Title { get; set; }
    public Category(string id, string title)
    
    

还有 Category 是属性的实体产品:

    [Key]
    public string Id { get; set; }

    [Required(ErrorMessage = "Título é obrigatório.")]
    [MaxLength(60, ErrorMessage = "Este campo deve conter enre 3 e 60 caracteres.")]
    [MinLength(3, ErrorMessage = "Este campo deve conter enre 3 e 60 caracteres.")]
    public string Title { get; set; }

    [MaxLength(1024, ErrorMessage = "Este campo deve ter no máximo 1024 caracteres")]
    public string Description { get; set; }

    [Required(ErrorMessage = "Este campo é obrigatório")]
    [Range(1, int.MaxValue, ErrorMessage = "O preço deve ser maior que zero")]
    public decimal Price { get; set; }
    public int AvailableQuantity { get; set; }

    [Required(ErrorMessage = "Este campo é obrigatório")]
    public string CategoryId { get; set; }

    public Category Category { get; set; }

这是 ProductController 中的 Create 方法:

    [HttpPost]
    [AllowAnonymous]
    public async Task<ActionResult<Product>> Create([FromBody] Product model)
    {
        var category = _context.Categories.AsNoTracking().FirstOrDefault(x => x.Id == model.CategoryId);

        if (category == null)
            return Ok("Categoria informada não existe");


        if (ModelState.IsValid)
        {
            _product.GenerateId(model);
            var product = new Product(model.Id, model.Title, model.Description, model.Price, model.CategoryId, category);
            _context.Products.Add(product);
            await _context.SaveChangesAsync();
            return product;
        }
        else
        {
            return BadRequest(ModelState);
        }
    }

当我尝试创建新产品时,我收到如下错误消息:

System.ArgumentException:已添加具有相同键的项目。钥匙:280CF21D 在 System.Collections.Generic.Dictionary2.TryInsert(TKey key, TValue value, InsertionBehavior behavior) at System.Collections.Generic.Dictionary2.Add(TKey 键,TValue 值) 在 Microsoft.EntityFrameworkCore.InMemory.Storage.Internal.InMemoryTable1.Create(IUpdateEntry entry) at Microsoft.EntityFrameworkCore.InMemory.Storage.Internal.InMemoryStore.ExecuteTransaction(IList1 个条目,IDiagnosticsLogger1 updateLogger) at Microsoft.EntityFrameworkCore.InMemory.Storage.Internal.InMemoryDatabase.SaveChangesAsync(IList1 个条目,CancellationToken cancelToken) 在 Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList1 entriesToSave, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(DbContext _, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken) at TesteAPI.Controllers.ProductsController.Create(Product model) in C:\Users\tcho3\source\repos\TesteAPI\TesteAPI\Controllers\ProductsController.cs:line 93 at lambda_method56(Closure , Object ) at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.&lt;InvokeActionMethodAsync&gt;g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask1 actionResultValueTask) 在 Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|10_0(ControllerActionInvoker 调用程序,任务 lastTask,下一个状态,作用域范围,对象状态,布尔 isCompleted) 在 Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed 上下文) 在 Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(状态&下一个,范围&范围,对象&状态,布尔& isCompleted) 在 Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync() --- 上一个位置的堆栈跟踪结束 --- 在 Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|19_0(ResourceInvoker 调用程序,任务 lastTask,下一个状态,作用域范围,对象状态,布尔 isCompleted) 在 Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker 调用程序,任务任务,IDisposable 范围) 在 Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(端点端点,任务 requestTask,ILogger 记录器) 在 Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext 上下文) 在 Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext 上下文) 在 Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext) 在 Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext,ISwaggerProvider swaggerProvider) 在 Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext 上下文)

【问题讨论】:

  • 你能发布 _product.GenerateId(model);也请?
  • 参见:public void GenerateId(Product product) { product.Id = Guid.NewGuid().ToString().Replace("-", "").ToUpper().Substring(0, 8); }

标签: c# .net entity-framework asp.net-core .net-core


【解决方案1】:

修复模型

public class Category 
{
    [Key]
    public string Id { get; set; }                
    public string Title { get; set; }
    .....

   public virtual ICollection<Product> Products { get; set}
}

您也不需要 Product 的特殊构造函数,因为您使用 Product 作为模型

修正你的行为


  public async Task<ActionResult<Product>> Create([FromBody] Product model)
    {
     
//---------- I don't think that you really need this

  var category = _context.Categories.AsNoTracking().FirstOrDefault(x => x.Id == model.CategoryId);
        if (category == null)  return Ok("Categoria informada não existe");

//---------

 //would be enough
    if (model.category == 0)  return Ok("Categoria informada não existe");



        if (ModelState.IsValid)
        {
            _product.GenerateId(model);
            _context.Products.Add(model;
            await _context.SaveChangesAsync();
            return model;
        }
        else
        {
            .....
        }
    }

【讨论】:

  • 好的,这样就不再需要对象类别了。对吗?
  • 是的,如果您保存了 categoryId,则不需要它。仅当您想添加具有新类别的新产品时才需要它。它通常用于自动生成的密钥。所以你也不需要它。我不知道您为什么选择 Id 作为字符串。整数会更有效和方便
  • 是的,这样它就会运行。但对于其他操作,我想使用下面的包含,为此我需要传递对象类别。 var products = await _context.Products.Include(x => x.Category).AsNoTracking()。其中(x => x.Category.Id == id).ToListAsync();关于作为字符串的 id,我将其生成如下: public void GenerateId(Product product) { product.Id = Guid.NewGuid().ToString().Replace("-", "").ToUpper()。子串(0, 8); }
  • 你的问题是关于添加新的,我的代码也是。如果要使用 include 进行选择,请使用它。它与添加新的无关。
【解决方案2】:

是的,因为类别已经存在于数据库中,因此与产品相关的类别只需在创建产品时传递CategoryId,如下所示:

var product = new Product(model.Id, model.Title, model.Description, model.Price, model.CategoryId);

无需传递category 对象。

【讨论】:

  • 是的,这样它就会运行。但是对于其他操作,我想使用下面的包含,为此我需要传递对象类别。 var products = await _context.Products.Include(x => x.Category).AsNoTracking()。 where(x => x.Category.Id == id).ToListAsync();
  • @GustavoKaicCorrêa 如果您的意思是“但对于其他操作”是另一种操作方法;您可以完美地做到这一点(在添加操作中,EF 只需要 categoryId 来构建关系)。
  • 这样您就可以使用我已经在添加操作中发布给您的查询,您可以使用您已经在评论中发布给我的查询来检索产品及其相关类别信息。跨度>
  • 明白。非常感谢!
  • @GustavoKaicCorrêa 如果我回答了您的问题,您可以将其标记为已回答
猜你喜欢
  • 1970-01-01
  • 2017-04-15
  • 2018-06-29
  • 2015-09-28
  • 2015-01-08
  • 2014-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多