Search code examples
c#asp.netasp.net-mvcdropdownlistfor

The key 'CategoryID' is of type 'System.Int32' but must be of type 'IEnumerable<SelectListItem>'


Got a little problem with saving my Product. Dropdownlist is getting populated, abut when I try to save I have an error which is stated in title. I read tons of articles about it, but any of it helped me out. What can affect my code? Any ideas?

Controller:

// GET: Admin/Products/Create
public ActionResult Create()
{
    string domain = Request.Url.Host;
    int clientid = (from a in db.Client where a.Domain == domain select a.ID).First();

    int maxID = db.Product.Where(c => c.ClientID == clientid).Max(c => (int?)c.ProductID) ?? 0;
    ViewBag.MaxID = maxID + 1;

    List<SelectListItem> categories = new List<SelectListItem>();
    foreach (var cat in db.Category.Where(c => c.ClientID == clientid))
    {
        categories.Add(new SelectListItem() { Text = cat.Name, Value = cat.CategoryID.ToString() });
    }

    ViewBag.Categories = categories;

        return View();
}

// POST: Admin/Products/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for 
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ID,ProductID,Name,CategoryID,Price,Promotion,Image1,Image2,Image3,Image4,Description,ClientID")] Product product, HttpPostedFileBase file1, HttpPostedFileBase file2, HttpPostedFileBase file3, HttpPostedFileBase file4)
{
    if (ModelState.IsValid)
    {
        if (file1 != null)
        {
            var fileName1 = Path.GetFileName(file1.FileName);
            var path1 = Path.Combine(Server.MapPath("~/Images/Products/"), fileName1);
            file1.SaveAs(path1);

            @product.Image1 = Url.Content("~/Images/Products/" + fileName1);
        }

        if (file2 != null)
        {
            var fileName2 = Path.GetFileName(file2.FileName);
            var path2 = Path.Combine(Server.MapPath("~/Images/Products/"), fileName2);
            file2.SaveAs(path2);

            @product.Image2 = Url.Content("~/Images/Products/" + fileName2);
        }

        if (file3 != null)
        {
            var fileName3 = Path.GetFileName(file3.FileName);
            var path3 = Path.Combine(Server.MapPath("~/Images/Products/"), fileName3);
            file3.SaveAs(path3);

            @product.Image3 = Url.Content("~/Images/Products/" + fileName3);
        }

        if (file4 != null)
        {
            var fileName4 = Path.GetFileName(file4.FileName);
            var path4 = Path.Combine(Server.MapPath("~/Images/Products/"), fileName4);
            file4.SaveAs(path4);

            @product.Image4 = Url.Content("~/Images/Products/" + fileName4);
        }

        string domain = Request.Url.Host;
        int clientid = (from a in db.Client where a.Domain == domain select a.ID).First();

        List<SelectListItem> categories = new List<SelectListItem>();
        foreach (var cat in db.Category.Where(c => c.ClientID == clientid))
        {
            categories.Add(new SelectListItem() { Text = cat.Name, Value = cat.CategoryID.ToString() });
        }

        ViewBag.Categories = categories;

        db.Product.Add(product);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    return View(product);
}

Dropdownlist in View:

<div class="col-md-2">
    <div class="form-group">
        <label>Kategoria</label>
        @Html.DropDownListFor(model => model.CategoryID, (IEnumerable<SelectListItem>)ViewBag.Categories, new { @class = "form-control" })
        @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
    </div>
</div>

Model:

public partial class Product
    {
        public int ID { get; set; }
        public int ProductID { get; set; }
        public string Name { get; set; }
        public int CategoryID { get; set; }
        public decimal Price { get; set; }
        public int Promotion { get; set; }
        public string Image1 { get; set; }
        public string Image2 { get; set; }
        public string Image3 { get; set; }
        public string Image4 { get; set; }
        [DataType(DataType.MultilineText)]
        [AllowHtml]
        public string Description { get; set; }
        public int ClientID { get; set; }
    }

Solution

  • You're not setting the ViewBag.Categories in your post if ModelState is invalid.

    Move your Select list code outside of the IsValid block

    // POST: Admin/Products/Create
    // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
    // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create([Bind(Include = "ID,ProductID,Name,CategoryID,Price,Promotion,Image1,Image2,Image3,Image4,Description,ClientID")] Product product, HttpPostedFileBase file1, HttpPostedFileBase file2, HttpPostedFileBase file3, HttpPostedFileBase file4)
    {
        if (ModelState.IsValid)
        {
            if (file1 != null)
            {
                var fileName1 = Path.GetFileName(file1.FileName);
                var path1 = Path.Combine(Server.MapPath("~/Images/Products/"), fileName1);
                file1.SaveAs(path1);
    
                @product.Image1 = Url.Content("~/Images/Products/" + fileName1);
            }
    
            if (file2 != null)
            {
                var fileName2 = Path.GetFileName(file2.FileName);
                var path2 = Path.Combine(Server.MapPath("~/Images/Products/"), fileName2);
                file2.SaveAs(path2);
    
                @product.Image2 = Url.Content("~/Images/Products/" + fileName2);
            }
    
            if (file3 != null)
            {
                var fileName3 = Path.GetFileName(file3.FileName);
                var path3 = Path.Combine(Server.MapPath("~/Images/Products/"), fileName3);
                file3.SaveAs(path3);
    
                @product.Image3 = Url.Content("~/Images/Products/" + fileName3);
            }
    
            if (file4 != null)
            {
                var fileName4 = Path.GetFileName(file4.FileName);
                var path4 = Path.Combine(Server.MapPath("~/Images/Products/"), fileName4);
                file4.SaveAs(path4);
    
                @product.Image4 = Url.Content("~/Images/Products/" + fileName4);
            }
    
            db.Product.Add(product);
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        string domain = Request.Url.Host;
        int clientid = (from a in db.Client where a.Domain == domain select a.ID).First();
    
        List<SelectListItem> categories = new List<SelectListItem>();
        foreach (var cat in db.Category.Where(c => c.ClientID == clientid))
        {
            categories.Add(new SelectListItem() { Text = cat.Name, Value = cat.CategoryID.ToString() });
            } 
        ViewBag.Categories = categories;
        return View(product);
    }
    

    while you're at it.. Since you're also missing the MaxID value, you could just move the Viewbag code to a separate method so you just have to call the method before you return the view.

    private void LoadCreateViewBagData()
    {
        string domain = Request.Url.Host;
        int clientid = (from a in db.Client where a.Domain == domain select a.ID).First();
        int maxID = db.Product.Where(c => c.ClientID == clientid).Max(c => (int?)c.ProductID) ?? 0;
    
        ViewBag.MaxID = maxID + 1;
    
        List<SelectListItem> categories = new List<SelectListItem>();
        foreach (var cat in db.Category.Where(c => c.ClientID == clientid))
        {
            categories.Add(new SelectListItem() { Text = cat.Name, Value = cat.CategoryID.ToString() });
        }
    
        ViewBag.Categories = categories;
    }
    
    // GET: Admin/Products/Create
    public ActionResult Create()
    {
        LoadCreateViewBagData();
        return View();
    }
    
    
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create([Bind(Include = "ID,ProductID,Name,CategoryID,Price,Promotion,Image1,Image2,Image3,Image4,Description,ClientID")] Product product, HttpPostedFileBase file1, HttpPostedFileBase file2, HttpPostedFileBase file3, HttpPostedFileBase file4)
    {
        if (ModelState.IsValid)
        {
            //yada yada
        }
        LoadCreateViewBagData();
        return View(product);
    }