简体   繁体   中英

How to bind data from dropdown list in asp.net core

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!

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

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>

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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM