When I try to create a new product in ASP.NET Core with EF Core, the Id
is always 0.
This is the value I get when I try to create:
Here is my Product
model class:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace test.Models
{
public class Product
{
public int Id { get; set; }
[Required]
public string? Title { get; set; }
public string? Description { get; set; }
[Required]
public string? ISBN { get; set; }
[Required]
public string? Author { get; set; }
[Required]
[Range(1,1000)]
[Display(Name ="ListPrice")]
public int ListPrice { get; set; }
[Required]
[Range(1, 1000)]
[Display(Name = "Price for 1-50")]
public int Price { get; set; }
[Required]
[Range(1, 1000)]
[Display(Name = "Price for 50+")]
public int Price50 { get; set; }
[Required]
[Range(1, 1000)]
[Display(Name = "Price for 100+")]
public int Price100 { get; set; }
}
}
Here is my controller:
public IActionResult Create()
{
return View();
}
[HttpPost]
public IActionResult Create(Product product)
{
if (ModelState.IsValid)
{
_productRepository.Add(product);
_productRepository.Save();
TempData["Success"] = "Product created successfully";
return RedirectToAction("Index");
}
return View();
}
Here is my view:
@{
}
@model Product
<div class="row justify-content-center">
<div class="col-10">
<div class="card shadow border-0 mt-4">
<div class="card-header bg-secondary bg-gradient m-lg-0 py3">
<div class="row">
<div class="col-12 text-center">
<h2 class="text-white py-2">Update Category</h2>
</div>
</div>
</div>
<div class="card-body p-4">
<form method="post" class="row">
<input asp-for="Id" hidden />
<div class="row">
<div class="col-10">
<div class="border pt-3">
<div class="py-2">
<input type="text" asp-for="Title" placeholder="Title" class="form-control form-floating shadow border-0" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="py-2">
<textarea asp-for="Description" placeholder="Description" class="form-control form-floating shadow border-0"></textarea>
</div>
<div class="py-2">
<input type="text" asp-for="ISBN" placeholder="ISBN" class="form-control form-floating shadow border-0" />
<span asp-validation-for="ISBN" class="text-danger"></span>
</div>
<div class="py-2">
<input type="text" asp-for="Author" placeholder="Author" class="form-control form-floating shadow border-0" />
<span asp-validation-for="Author" class="text-danger"></span>
</div>
<div class="py-2">
<input type="number" asp-for="ListPrice" placeholder="ListPrice" class="form-control form-floating shadow border-0" />
<span asp-validation-for="ListPrice" class="text-danger"></span>
</div>
<div class="py-2">
<input type="text" asp-for="Price" placeholder="Price for 1-50" class="form-control form-floating shadow border-0" />
<span asp-validation-for="Price" class="text-danger"></span>
</div>
<div class="py-2">
<input type="text" asp-for="Price50" placeholder="Price for 50+" class="form-control form-floating shadow border-0" />
<span asp-validation-for="Price50" class="text-danger"></span>
</div>
<div class="py-2">
<input type="text" asp-for="Price100" placeholder="Price for 100+" class="form-control form-floating shadow border-0" />
<span asp-validation-for="Price100" class="text-danger"></span>
</div>
<div class="row mt-5">
<div class="col-6">
<button type="submit" class="btn btn-primary form-control">Create</button>
</div>
<div class="col-6">
<a asp-controller="Product" asp-action="Index" class="btn btn-outline-primary form-control">Back to List</a>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
@section Scripts {
<script>
tinymce.init({
selector: 'textarea',
plugins: 'ai tinycomments mentions anchor autolink charmap codesample emoticons image link lists media searchreplace table visualblocks wordcount checklist mediaembed casechange export formatpainter pageembed permanentpen footnotes advtemplate advtable advcode editimage tableofcontents mergetags powerpaste tinymcespellchecker autocorrect a11ychecker typography inlinecss',
toolbar: 'undo redo | blocks fontfamily fontsize | bold italic underline strikethrough | link image media table mergetags | align lineheight | tinycomments | checklist numlist bullist indent outdent | emoticons charmap | removeformat',
tinycomments_mode: 'embedded',
tinycomments_author: 'Author name',
mergetags_list: [
{ value: 'First.Name', title: 'First Name' },
{ value: 'Email', title: 'Email' },
],
ai_request: (request, respondWith) => respondWith.string(() => Promise.reject("See docs to implement AI Assistant")),
});
</script>
@{
<partial name="_ValidationScriptsPartial" />
}
}
I use a repository pattern to perform CRUD operations.
Here is my repository interface IRepository
:
using System.Linq.Expressions;
namespace test.dataAccess.Repository.IRepository
{
public interface IRepository<T> where T : class
{
IEnumerable<T> GetAll();
T Get(Expression<Func<T, bool>> filter);
void Add(T entity);
void Remove(T entity);
void RemoveRange(IEnumerable<T> entity);
}
}
And here is the implementation of IRepository
:
using Microsoft.EntityFrameworkCore;
using System.Linq.Expressions;
using test.dataAccess.Data;
using test.dataAccess.Repository.IRepository;
namespace test.dataAccess.Repository
{
public class Repository<T> : IRepository<T> where T : class
{
private readonly ApplicationDbContext _db;
internal DbSet<T> dbSet;
public Repository(ApplicationDbContext db)
{
_db = db;
this.dbSet = _db.Set<T>();
}
public void Add(T entity)
{
dbSet.Add(entity);
}
public T Get(Expression<Func<T, bool>> filter)
{
IQueryable<T> query = dbSet.Where(filter);
return query.FirstOrDefault();
}
public IEnumerable<T> GetAll()
{
IQueryable<T> query = dbSet;
return query.ToList();
}
public void Remove(T entity)
{
dbSet.Remove(entity);
}
public void RemoveRange(IEnumerable<T> entity)
{
dbSet.RemoveRange(entity);
}
}
}
Here is the interface of IProductRepository
:
using test.Models;
namespace test.dataAccess.Repository.IRepository
{
public interface IProductRepository : IRepository<Product>
{
void Update(Product product);
void Save();
}
}
And the implementation of IProductRepository
:
using test.dataAccess.Data;
using test.dataAccess.Repository.IRepository;
using test.Models;
namespace test.dataAccess.Repository
{
public class ProductRepository : Repository<Product>, IProductRepository
{
private readonly ApplicationDbContext _db;
public ProductRepository(ApplicationDbContext db) : base(db)
{
_db = db;
}
public void Save()
{
_db.SaveChanges();
}
public void Update(Product product)
{
_db.Update(product);
}
}
}
I'm trying to add product but when I try to do that the modelstate
is always invalid. When I debug, I found that id
is always set to 0.
Why is that? What is the problem?
The reason why the modelstate is always false is you have set the id input at view as hidden field.
<input asp-for="Id" hidden />
This means when during the form post, the client will post the product id input hidden value to the backend.
Since you don't set the value for it, it will post the "" value to the backend which caused the model state invalid.
To solve the issue, you could set the input value to 0 like below:
<input asp-for="test" hidden value="0" />
Result:
Besides, as marc_s said normally, for the product model's id, we will set it as key which will generated by the sql database. So there is no need to add the input for it. You could directly remove the input hidden for the id property.