I'm trying to add a record in a table, but it's not working. The error message tells me that I cannot insert an explicit value in identity column for ProductCategories
, but I don't know that I'm doing that. Maybe there is something I'm not getting about entity navigation, or my models are somehow not correctly linked?
SqlException: Cannot insert explicit value for identity column in table 'ProductCategories' when IDENTITY_INSERT is set to OFF. Cannot insert explicit value for identity column in table 'Products' when IDENTITY_INSERT is set to OFF. System.Data.SqlClient.SqlCommand+<>c.b__108_0(Task result)
DbUpdateException: An error occurred while updating the entries. See the inner exception for details. Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch+d__32.MoveNext()
This is the code that fails (at await _context.SaveChangesAsync();
):
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> FrontPageProduct(ViewModelFrontPageProduct frontPageProduct)
{
var fpp = new FrontPageProduct()
{
ProductCategoryId = frontPageProduct.ProductCategoryId,
ProductId = frontPageProduct.ProductId,
SortOrder = 0
};
_context.Add(fpp);
await _context.SaveChangesAsync();
return View("Index", new { id = fpp.ProductCategoryId, tab = 2 });
}
These are the involved entity models:
public class Product
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Title { get; set; }
public string Info { get; set; }
public decimal Price { get; set; }
public List<FrontPageProduct> InFrontPages { get; set; }
public List<ProductInCategory> InCategories { get; set; }
}
public class ProductCategory
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int SortOrder { get; set; }
public string Title { get; set; }
[ForeignKey(nameof(ParentCategory))]
public int? ParentId { get; set; }
public ProductCategory ParentCategory { get; set; }
public ICollection<ProductCategory> Children { get; set; } = new List<ProductCategory>();
public List<ProductInCategory> ProductInCategory { get; set; }
}
public class FrontPageProduct
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int ProductCategoryId { get; set; }
public int ProductId { get; set; }
public int SortOrder { get; set; }
[ForeignKey("ProductId")]
public virtual Product Product { get; set; }
[ForeignKey("ProductCategoryId")]
public virtual ProductCategory ProductCategory { get; set; }
}
The debug inspection of the fpp
-object shows that the values for ProductCategoryId
and ProductId
are correct:
Where is/are my mistake/s?
EDIT I added the suggested [Key]
and [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
to all the models, but I still get the same error.
I think that the Id
field is always set to be primary key as default in EF anyway.
EDIT 2 I tried adding [FromBody]
in my controller method, but that only resulted in a blank screen, no error message, and no changes being made to the database.
EDIT 3 I added [ForeignKey("ProductId")]
and [ForeignKey("ProductCategoryId")]
to the FrontPageProduct
model, but still get the same SqlException.
EDIT 4 These are the foreign keys of my four tables:
FrontPageProducts
:
FK_FrontPageProducts_ProductCategories_ProductCategoryId
FK_FrontPageProducts_Products_ProductId
ProductCategories
:
FK_ProductCategories_ProductCategories_ParentId
Products
:
none
ProductsInCategories
:
FK_ProductsInCategories_FrontPageProducts_FrontPageProductId
FK_ProductsInCategories_ProductCategories_ProductCategoryId
FK_ProductsInCategories_Products_ProductId
Looks like you don't need to set DatabaseGenerated attribute for "int Id" fields, it is a convention and assumed that it is Identity Column. Btw, are you sure you are getting the same error? The imgur link does not work for me. Do you really need Product and ProductCategory on your FrontPage class, if you can easily comment them out and test, please do so, if not you can try setting the values for them like below.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> FrontPageProduct(ViewModelFrontPageProduct frontPageProduct)
{
var fpp = new FrontPageProduct()
{
ProductCategoryId = frontPageProduct.ProductCategoryId,
ProductId = frontPageProduct.ProductId,
Product = _context.Set<Product>().Find(frontPageProduct.ProductId),
ProductCategory = _context.Set<ProductCategory>().Find(frontPageProduct.ProductCategoryId),
SortOrder = 0
};
_context.Add(fpp);
await _context.SaveChangesAsync();
return View("Index", new { id = fpp.ProductCategoryId, tab = 2 });
}
Apply Key and DatabaseGenerated attributes to every Id column on your models, so EF knows that it is an identity column and it needs to get back the generated id value.
public class Product
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Title { get; set; }
public string Info { get; set; }
public decimal Price { get; set; }
[InverseProperty("Product")]
public IEnumerable<FrontPageProduct> InFrontPages { get; set; }
public List<ProductInCategory> InCategories { get; set; }
}
public class ProductCategory
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int SortOrder { get; set; }
public string Title { get; set; }
[ForeignKey(nameof(ParentCategory))]
public int? ParentId { get; set; }
public ProductCategory ParentCategory { get; set; }
public ICollection<ProductCategory> Children { get; set; } = new List<ProductCategory>();
public List<ProductInCategory> ProductInCategory { get; set; }
}
public class FrontPageProduct
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int ProductCategoryId { get; set; }
public int ProductId { get; set; }
public int SortOrder { get; set; }
[ForeignKey("ProductId")]
public virtual Product Product { get; set; }
[ForeignKey("ProductCategoryId ")]
public virtual ProductCategory ProductCategory { get; set; }
}
Edit: you should add navigation properties, just like I added above. Making them virtual also helps with lazy loading.