1

I am trying to put a list of medical product brand names in a dropdown list on my edit.cshtml file using Html.DropDownListFor(). I am trying to use the Html helper properly but my efforts have been unsuccessful. Model.BrandSelectListItem is an IEnumerable that is supposed to go into the DropDownList.

Also, I don't understand why I need to reference the model as 'model' in the lambda expression, but as 'Model' in the second argument of the following code segment:

@Html.DropDownListFor(model => model.BrandName, Model.BrandSelectListItem)

when executing the code, I receive the following run-time error:

The ViewData item that has the key 'BrandName' is of type 'System.String' but must be of type 'IEnumerable'.

Here are some classes that may be relevant to this error:

MedicalProduct

public class MedicalProduct
{
    [Key]
    public int ID { get; set; }

    [Required]
    [StringLength(50)]
    public string Name { get; set; }

    [Required]
    [DataType(DataType.Currency)]
    public double Price { get; set; }

    // is a foreign key
    public Nullable<int> BrandID { get; set; }
}

MedicalProductViewModel

public class MedicalProductViewModel
{
    [Key]
    public int ID { get; set; }

    [Required]
    [StringLength(50)]
    public string Name { get; set; }

    [Required]
    [DataType(DataType.Currency)]
    public double Price { get; set; }

    public Nullable<int> BrandID { get; set; }

    [DisplayFormat(NullDisplayText="[Generic]")]
    public string BrandName { get; set; }

    public IEnumerable<SelectListItem> BrandSelectListItem { get; set; }
}

MedicalProductController

public class MedicalProductController : Controller
{
    private MvcMedicalStoreDb _db = new MvcMedicalStoreDb();

    //
    // GET: /MedicalSupply/Edit/5

    public ActionResult Edit(int id = 0)
    {
        MedicalProduct medicalProduct = _db.Products.Find(id);
        if (medicalProduct == null)
        {
            return HttpNotFound();
        }

        var viewModel = GetMedicalProductViewModel(medicalProduct);
        return View(viewModel);
    }

    public MedicalProductViewModel GetMedicalProductViewModel(MedicalProduct product)
    {
        var mapper = new MedicalProductMapper(_db);

        return mapper.GetMedicalProductViewModel(product);            
    }
}

MedicalProductMapper

public class MedicalProductMapper
{
    private MvcMedicalStoreDb _db;

    public MedicalProductMapper(MvcMedicalStoreDb db)
    {
        _db = db;
    }

    public MedicalProductViewModel GetMedicalProductViewModel(MedicalProduct source)
    {
        MedicalProductViewModel viewModel = new MedicalProductViewModel();
        var dbBrands = _db.Brands.ToArray();

        viewModel.ID = source.ID; 
        viewModel.Name = source.Name;
        viewModel.Price = source.Price;
        viewModel.BrandID = source.BrandID;
        if (viewModel.BrandID != null)
            viewModel.BrandName = dbBrands.SingleOrDefault(b => b.ID == source.BrandID).Name;

        var queryBrands = from b in dbBrands
                          select b;

        // BrandSelectListItem is 'null' before this assignment statement executes. 
        // and Stays null after the statement executes, why?
        viewModel.BrandSelectListItem = queryBrands as IEnumerable<SelectListItem>;

        return viewModel;
    }
}

Edit.cshtml

@model MvcMedicalStore.Models.MedicalProductViewModel

@{
    ViewBag.Title = "Edit";
}

<h2>Edit</h2>

@using (Html.BeginForm()) {
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>MedicalProduct</legend>

        @Html.HiddenFor(model => model.ID)

        <div class="editor-label">
            @Html.LabelFor(model => model.Name)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Name)
            @Html.ValidationMessageFor(model => model.Name)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Price)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Price)
            @Html.ValidationMessageFor(model => model.Price)
        </div>


        <div class="editor-label">
            @Html.LabelFor(model => model.BrandName)
        </div>
        <div class="editor-field">
            @Html.DropDownListFor(model => model.BrandName, Model.BrandSelectListItem)
            @Html.ValidationMessageFor(model => model.BrandName)
        </div>

        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}
ArmorCode
  • 739
  • 4
  • 15
  • 33
  • see my answer here on how to build the dropdown list http://stackoverflow.com/questions/18963618/how-to-populate-an-dropdownlistfor-from-database-in-asp-net-mvc-4/18965261?noredirect=1#comment28017688_18965261 – Matt Bodily Sep 25 '13 at 21:56
  • I edited my answer, are you still having trouble with this? – Kyle Gobel Sep 26 '13 at 09:31

1 Answers1

0

Also, I don't understand why I need to reference the model as 'model' in the lambda expression, but as 'Model' in the second argument of the following code segment:

You don't need to reference it as model in the lambda expression. The first part of the lambda (before the => symbol) you are just specifying your parameters and you can name them whatever you want

anything => anything.BrandName

would work the same as using model in place of anything

The lambda expression in this case you can think of it as a method that looks like this

TProperty GetPropertyYouWantToCreateADropdownFor(MedicalPropertyViewModel model)
{
    return model.BrandName
}

As for the dropdown, here is a good post explaining how to create a dropdown

How to write a simple Html.DropDownListFor()?

Edit

I plugged in your all your code except your mapper, and just made a little fake implementation of your ViewModel. Your code worked fine.

Here's what I used for my fake controller

 var viewModel = new MedicalProductViewModel
 {
     BrandID = 12,
     BrandName = "Some Brand",
     ID = 6,
     Name = "Some Name",
     Price = 12.27,
     BrandSelectListItem = new List<SelectListItem>()
     {
           new SelectListItem() {Text = "Item 1", Value = "1"},
           new SelectListItem() {Text = "Item 2", Value = "2"}
     }
 };

 return View(viewModel);

So seeing that this worked, the only thing I can guess is that your problem lies in your Mapper, can you give an example of what viewModel.BrandSelectListItem equals after you get data from the database? Does throwing in fake data work for you?

Edit 2:

queryBrands has to be compatible with SelectListItem if your going to cast it into an IEnumerable<SelectListItem>

You can modify your existing

var queryBrands = from b in dbBrands
                      select b;

to something like

var queryBrands = dbBrands.Select(b => new SelectListItem() 
                                 {
                                     Text = b.PropertyWithWhatYouWantToDisplay,
                                     Value = b.PropertyYouWantAsTheValue,
                                     Selected = //true if you want this item selected, false otherwise
                                 });
Community
  • 1
  • 1
Kyle Gobel
  • 5,530
  • 9
  • 45
  • 68
  • I've updated the class named `MedicalProductMapper` in my question with a `//comment` that says what the debugger shows `BrandListSelectItem` is equal to. The comment says: "BrandSelectListItem is 'null' before this assignment statement executes. and stays null after the statement executes, why?" Refer to the class for the location of the problem assignment statement. – ArmorCode Sep 26 '13 at 15:31
  • Most likely because your trying to cast queryBrands into an ``IEnumberable`` of ``SelectListItem``. ``SelectListItem`` has 3 properties, `Selected`, `Text`, and `Value` (http://msdn.microsoft.com/en-us/library/system.web.mvc.selectlistitem(v=vs.108).aspx). I just added a 2nd edit. – Kyle Gobel Sep 26 '13 at 15:59
  • Also: I tried inserting your fake `List()` and that worked. – ArmorCode Sep 26 '13 at 16:01