1
votes

In my API project, there is the entity Category as below:

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

And the Entity Product where Category is a property:

    [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; }

This is the Create Method in the ProductController:

    [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);
        }
    }

When I try to create a new Product I receive the error message as below:

System.ArgumentException: An item with the same key has already been added. Key: 280CF21D at System.Collections.Generic.Dictionary2.TryInsert(TKey key, TValue value, InsertionBehavior behavior) at System.Collections.Generic.Dictionary2.Add(TKey key, TValue value) at Microsoft.EntityFrameworkCore.InMemory.Storage.Internal.InMemoryTable1.Create(IUpdateEntry entry) at Microsoft.EntityFrameworkCore.InMemory.Storage.Internal.InMemoryStore.ExecuteTransaction(IList1 entries, IDiagnosticsLogger1 updateLogger) at Microsoft.EntityFrameworkCore.InMemory.Storage.Internal.InMemoryDatabase.SaveChangesAsync(IList1 entries, CancellationToken cancellationToken) at 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.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask1 actionResultValueTask) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger) at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

2
Can you post _product.GenerateId(model); too, pls?Serge
see: public void GenerateId(Product product) { product.Id = Guid.NewGuid().ToString().Replace("-", "").ToUpper().Substring(0, 8); }Gustavo Kaic Corrêa

2 Answers

0
votes

fix model

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

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

You don't need a special constructor for Product too since you are using Product as a model

fix your action


  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
        {
            .....
        }
    }
0
votes

yes because the Category already exist in the database so that the category to be related to the product you just need to pass the CategoryId when you create the Product like the Following :

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

no need to pass category object.