Search code examples
c#asp.netasp.net-mvcmodel-view-controllerasp.net-mvc-5

Binding DropDownList from Database


I'm building a view which will display tabular data based on selections from two dropdownlists, the first which selects departments is populated from the database. The second is a static dropdownlist for "Year" which I haven't got to yet. There are answers to similar questions, but I've looked at them and tried everything. I've got to the point where I'm getting a null exception in the view where I created the markup for the dropdownlist.

View:

Departments is coming back as null. I'm new to MVC and pieced this together from online examples. I'm guessing my model is wrong.

@model IEnumerable<BudgetDemo.Models.BudgetsActualsViewModel>

@foreach (var item in Model)
{
    @Html.DropDownListFor(m => item.Departments, 
        new SelectList(item.Departments.Select(x => x.Text)))
}

ViewModel:

public class BudgetsActualsViewModel
{
    [Display(Name = "Cost Center/Department")]
    [Required(ErrorMessage = "Cost Center/Department is required.")]
    [StringLength(62)]
    public string SelectedDepartment { get; set; }
    public IEnumerable<SelectListItem> Departments { get; set; }
}

Controller:

public ActionResult GetBudgetsActuals()   
{
    repo = new BudgetDemoRepository();
    ModelState.Clear();
    return View(repo.GetBudgetsActuals());
}

Repository Class:

public List<BudgetsActualsViewModel> GetBudgetsActuals()  
{
    ...
    List<BudgetsActualsViewModel> budgetsActualsList = new List<BudgetsActualsViewModel>();
    // Query returning correct data from DB here

    for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
    {
        budgetsActualsList.Add(
            new BudgetsActualsViewModel
             {
                 SelectedDepartment = ds.Tables[0].Rows[i]["Department"].ToString()
             }
        );
    }
    return budgetsActualsList;
}

Solution

  • So, You are saving the values in SelectedDepartment property and accessing the Departments list. Which is never initialized so it would obviously be null.

    Modify your GetBudgetsActuals method like this:

    public BudgetsActualsViewModel GetBudgetsActuals()  
    {
        ...
        BudgetsActualsViewModel budgetsActuals = new BudgetsActualsViewModel(){ Departments = new List<SelectListItem>() };
        // Query returning correct data from DB here
    
        for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
        {
            budgetsActuals.Departments
                          .Add(new SelectListItem 
                              { 
                                  Text = ds.Tables[0].Rows[i]["Department"].ToString(), 
                                  Value = ds.Tables[0].Rows[i]["YourRowId"].ToString() 
                              });
        }
        return budgetsActuals;
    }
    

    And this is how your View should look like:

    @model BudgetDemo.Models.BudgetsActualsViewModel
    @Html.DropDownListFor(m => m.SelectedDepartment, Model.Departments))
    

    Notice, we got rid of the foreach loop. We need DropDownListFor() method to be called only once.


    UPDATE

    public class BudgetsActualsViewModel
    {
        [Display(Name = "Cost Center/Department")]
        [Required(ErrorMessage = "Cost Center/Department is required.")]
        [StringLength(62)]
        public string SelectedDepartment { get; set; }
        public List<SelectListItem> Departments { get; set; } // Modify it to list instead IEnumerable.
    }