0
votes

When inserting a new product in my DB I am getting the following SQL error message:

Form:

Create

Error SQL:

Sql

I have been looking for information about it and this is a SQL error that can be fixed from Entity Framework but I am not getting good results

My models involved are the following...

Producto.CS:

public class Producto : IEntity
{
    
    public int Id { get; set; }

    [Display(Name = "Nombre")]
    [MaxLength(50, ErrorMessage = "El campo {0} solo puede contener {1} caracteres de largo.")]
    [Required]
    public string Name { get; set; }

    [Display(Name = "Descripción")]
    [MaxLength(150, ErrorMessage = "El campo {0} solo puede contener {1} caracteres de largo.")]
    [Required]
    public string Description { get; set; }

    [Display(Name = "Precio")]
    [DisplayFormat(DataFormatString = "{0:C2}", ApplyFormatInEditMode = false)]
    public decimal Price { get; set; }

    [Display(Name = "Imagen")]
    public string ImageUrl { get; set; }

    public User User { get; set; }

    public Category Category { get; set; }
}

Category.CS:

 public class Category : IEntity
    {
        
        public int Id { get; set; }

        [Display(Name = "Nombre Categoría")]
        [Required (ErrorMessage = "El campo categoría es obligatorio.")]
        [MaxLength(30, ErrorMessage = "el campo {0} solo puede contener {1} caracteres de largo.")]
        public string Name { get; set; }

    }

IEntity.CS:

   public interface IEntity
   {
       int Id { get; set; }
            
   }

Attached my POST action of my product controller..

 [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create(ProductoViewModel view)
        {
            if (ModelState.IsValid)
            {
                var path = string.Empty;

                if (view.ImageFile != null && view.ImageFile.Length > 0)
                {
                    var guid = Guid.NewGuid().ToString();
                    var file = $"{guid}.jpg";

                    path = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot\\images\\Productos", file);

                    using (var stream = new FileStream(path, FileMode.Create))
                    {
                        await view.ImageFile.CopyToAsync(stream);
                    }

                    path = $"~/images/Productos/{file}";
                }

                //TRANSFORMO EL PRODUCTO PRODUCTOVIEWMODEL A PRODUCTO
                var producto = this.ToProducto(view, path);

                //USUARIO CREADOR
                producto.User = await userHelper.GetUserByEmailAsync(User.Identity.Name);

                //CATEGORIA DEL PRODUCTO
                producto.Category = await categoryRepository.GetByIdAsync(view.CategoryId);

                await productoRepository.CreateAsync(producto);

                //FALTA VALIDAR QUE DEVUELVE (TRUE OR FALSE)            
                return RedirectToAction(nameof(Index));
            }

            return View(view);
        }

        private Producto ToProducto(ProductoViewModel view, string path)
        {
            return new Producto
            {
                Id = view.Id,
                ImageUrl = path,
                IsAvailabe = view.IsAvailabe,
                LastSale = view.LastSale,
                Name = view.Name,
                Description = view.Description,
                Price = view.Price,
                Stock = view.Stock,
                User = view.User,
                Category = view.Category
            };
        }

By following up with a breakpoint I make sure that I am sending a valid model

Breakpoint

I am occupying the repository pattern and my system crashes on the following line in my POST action

 await productoRepository.CreateAsync(producto);

I attach my IProductoRepository.CS:

  public interface IProductoRepository :  IGenericRepository<Producto>
    {

        IQueryable GetAllWithCategories();

        IQueryable GetAllWithUser();

        IEnumerable<SelectListItem> GetComboProductos();

    }

GenericRepository.CS:

  public class GenericRepository<T> : IGenericRepository<T> where T : class, IEntity
    {
        private readonly DataContext context;

        public GenericRepository(DataContext context)
        {
            this.context = context;
        }

        public async Task<T> CreateAsync(T entity)
        {
            await this.context.Set<T>().AddAsync(entity);
            await SaveAllAsync();
            return entity;
        }
    }

What's going on? Why can't I save a product with its respective category?

I have verified my various keys in my SQL engine

Sql

I am attaching my DataContext.CS as additional information:

     public class DataContext : IdentityDbContext<User>
        {
           
            public DbSet<Producto> Productos { get; set; }
            public DbSet<Category> Categories { get; set; }
    
    
            #region Constructor
            public DataContext(DbContextOptions<DataContext> options) : base(options)
            {
    
            }
            #endregion

   //METODO QUE DESHABILITA EL BORRADO EN CASCADA 
        //Y ELIMINA LOS WARNING AL MIGRAR LA BASE DE DATOS
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            //MANTISA DE 18 NUMEROS Y DOS DECIMALES
            modelBuilder.Entity<Producto>().Property(p => p.Price).HasColumnType("decimal(18,2)");

            //DESHABILITAR EL BORRADO EN CASCADA
            var cascadeFKs = modelBuilder.Model
                .G­etEntityTypes()
                .SelectMany(t => t.GetForeignKeys())
                .Where(fk => !fk.IsOwnership && fk.DeleteBehavior == DeleteBehavior.Casca­de);

            foreach (var fk in cascadeFKs)
            {
                fk.DeleteBehavior = DeleteBehavior.Restr­ict;
            }

            base.OnModelCreating(modelBuilder);
        }
    }
    
        }

I have tried adding the following DataAnnotations to my classes in question:

  1. [Key]
  2. [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
  3. [ForeignKey("....")]

Also deleting the Database I have tried everything again

I have also verified the Identity Specification value of my SQL

IdentitySpecification

I know this is a known ASP.NET Core bug, but how do I fix it in version 2.1? Am I forgetting something? Any help for me?

1
This is strange which should not happen. Do one thing, open your package manager console > add a new migration and update database. It might be possible that some changes were not committed to database that involved CategoryId. - noobprogrammer
I have migrated the DB without obtaining good results @noobprogrammer - Cristofher Ambiado
Since it's an IDENTITY column that's being set in the database backend, you must NOT set an explicit value for the Id column - don't include this line: Id = view.Id, - just leave Id unassigned - and your view shouldn't have an Id anyway...... - marc_s
I have commented on the line that you indicate, getting the same error, any other suggestions? @marc_s - Cristofher Ambiado
@CristofherAmbiado somewhere you are setting the Id explicitly that's why the error. I see that IEntity is being inherited to your Category class, that "could" be one reason of error. - noobprogrammer

1 Answers

1
votes
public class Category : IEntity
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]   
    public int Id { get; set; }

    [Display(Name = "Nombre Categoría")]
    [Required (ErrorMessage = "El campo categoría es obligatorio.")]
    [MaxLength(30, ErrorMessage = "el campo {0} solo puede contener {1} caracteres de largo.")]
    public string Name { get; set; }

    public IList<Producto> Productos { get; set; }
}

public class Producto : IEntity
{    
  [Key]
  [DatabaseGenerated(DatabaseGeneratedOption.Identity)]   
  public int Id { get; set; }

  [Display(Name = "Nombre")]
  [MaxLength(50, ErrorMessage = "El campo {0} ....")]
  [Required]
  public string Name { get; set; }

  [Display(Name = "Descripción")]
  [MaxLength(150, ErrorMessage = "El campo {0} solo puede contener {1} 
  caracteres de largo.")]
  [Required]
  public string Description { get; set; }

  [Display(Name = "Precio")]
  [DisplayFormat(DataFormatString = "{0:C2}", ApplyFormatInEditMode = false)]
  public decimal Price { get; set; }

  [Display(Name = "Imagen")]
  public string ImageUrl { get; set; }

  public User User { get; set; }

  public int CategoryId { get; set; }

  [ForeignKey(nameof(CategoryId))]
  public Category Category { get; set; }
 }