Reputation: 23
I am a new in asp.net core and try to develope an education projet (e-shop) with entity framework. I have some problems with model binding. I have 2 models: Product and Category.
public class Product
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public Category Category { get; set; }
public decimal Price { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
ProductController:
[HttpGet]
public IActionResult Create()
{
ViewBag.Categories = new SelectList(categoryRepo.Categories, "Id", "Name");
return View("Edit", new Product());
}
[HttpGet]
public IActionResult Edit(int productId)
{
return View(repo.Products.FirstOrDefault(p => p.Id == productId));
}
[HttpPost]
public IActionResult Edit(Product product)
{
if (ModelState.IsValid)
{
repo.SaveProduct(product);
TempData["Message"] = $"{product.Title} successfully saved";
return RedirectToAction("Index");
}
else
{
ViewBag.Categories = new SelectList(categoryRepo.Categories, "Id", "Name");
return View(product);
}
}
Everything is ok with Create method in ProductController and getting categories to drop-down list in Edit.cshtml
@model Product
@{
ViewData["Title"] = "Add product";
}
<h1>@ViewData["Title"]</h1>
<form asp-action="Edit" asp-controller="Product" method="post">
<div class="form-group">
<label asp-for="Title"></label>
<div>
<span class="text-danger" asp-validation-for="Title"></span>
</div>
<input class="form-control" type="text" asp-for="Title" />
</div>
<div class="form-group">
<label asp-for="Description"></label>
<div>
<span class="text-danger" asp-validation-for="Description"></span>
</div>
<textarea class="form-control" asp-for="Description" cols="15" rows="10"></textarea>
</div>
<div class="form-group">
<label asp-for="Category"></label>
<div>
<span class="text-danger" asp-validation-for="Category"></span>
</div>
<select asp-for="Category" asp-items="ViewBag.Categories">
<option disabled>Choose the category</option>
</select>
</div>
<div class="form-group">
<label asp-for="Price"></label>
<div>
<span class="text-danger" asp-validation-for="Price"></span>
</div>
<input class="form-control" type="text" asp-for="Price" />
</div>
<button class="btn btn-success" type="submit">Add</button>
</form>
But when I try to submit back the form to Edit method in ProductController.cs the Category field isn't binding to Product and I get just validation error "Choose the category".
Please help me with this issue!
P.S. DbContext:
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options){ }
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
}
Implementation of IProductRepository
public class EFProductRepository : IProductRepository
{
private AppDbContext context;
public EFProductRepository(AppDbContext ctx)
{
context = ctx;
}
public IQueryable<Product> Products => context.Products.Include(p => p.Category);
public void SaveProduct(Product product)
{
if (product.Id == 0)
{
context.Products.Add(product);
}
else
{
Product entry = context.Products.FirstOrDefault(p => p.Id == product.Id);
if (entry != null)
{
entry.Title = product.Title;
entry.Description = product.Description;
entry.Category = product.Category;
entry.Price = product.Price;
}
}
context.SaveChanges();
}
}
Implementation of ICategoryRepository
public class EFCategoryRepository : ICategoryRepository
{
private AppDbContext context;
public EFCategoryRepository(AppDbContext ctx)
{
context = ctx;
}
public IQueryable<Category> Categories => context.Categories;
}
Upvotes: 0
Views: 8027
Reputation: 43860
fix ViewBag.Categories of the action
ViewBag.Categories = categoryRepo.Categories
.Select( i=> new SelectListItem {
Value=i.Id.ToString(),
Text=i.Name
}).ToList();
Add CategoryId to Product class, and check your Product fluent Apis. Should not be anything in Product about requied Category, changed everything to CategoryId.
public class Product
{
...
public int? CategoryId {get;set;}
public virtual Category Category { get; set; }
....
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products {get; set;}
}
and fix the view
<select asp-for="CategoryId" asp-items="ViewBag.Categories"></select>
Upvotes: 3
Reputation: 818
You've to add CategoryId object in Product, so your product class should be
public class Product
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public int CategoryId {get;set;}
public Category Category { get; set; }
public decimal Price { get; set; }
}
And in your view page, category select element should be use CategoryId instead of Category in asp-for attribute It should be like
<select asp-for="CategoryId" asp-items="ViewBag.Categories"></select>
Upvotes: 1