views:

503

answers:

3

Hi, I am trying to understand something a bit better with being new to C#, .NET 3.5 and MVC.

I am running through the MVC NerdDinner example and if you look at the ViewModel here: http://nerddinnerbook.s3.amazonaws.com/Part6.htm#highlighter_662935

You can see the Country list and how it gets populated, this seems to work fine but I tried to do a similar thing below using LINQ and I am having problems, with the SelectList approach even though it inherits from the IEnumerable interface.

I have got a task table with a foreign key to a status table. The below code gives me a NullReferenceException when I do a GET on a create action. I can see that an anonymous task object would not have a status set.. so I probably need to check for it, but I dont understand how this is not done for the NerdDinner example??

        public class TaskViewModel {
     // Properties

     public Task Task { get; private set; }
     public SelectList Status { get; private set; }

     // Constructor
     public TaskViewModel(Task task) {
      TaskRepository taskRepo = new TaskRepository();
      Task = task;
      Status = new SelectList(taskRepo.GetStatus(), Task.Status.description);
     }

    }

        //
        // GET: /Tasks/Create

        public ActionResult Create()
        {
      Task task = new Task();

            return View(new TaskViewModel(task));
        } 


//Code from TaskRepository 

        private TaskManagerDataContext db = new TaskManagerDataContext();

     public IQueryable<Status> GetStatus() {
      return from status in db.Status
          orderby status.description
          select status;
     }

I did another approach using LINQ for the type dropdown and the population of the drop down works but I am yet to test if it selects the correct value once a post is made and the details view is returned. I am also wondering whether this should some how be moved into my repository rather than have a class in my controller doing this sort of thing??

Here is the code:

//In TaskViewModel Class
     public IEnumerable<SelectListItem> Types { get; private set; }

//In TaskViewModel constructor

          IList<NPType> types = taskRepo.GetTypes().ToList();

      Types =
       from type in types
       select new SelectListItem {
        Selected = (type.typeId == task.typeId),
        Text = type.description,
        Value = type.typeId.ToString()
       };

//The TaskForm partial View that is used for the Create action of the TaskController
            <p>
                <label for="type">type:</label>
                <%= Html.DropDownList("Type", Model.Types)%>
                <%= Html.ValidationMessage("type", "*") %>
            </p>
            <p>
                <label for="status">status:</label>
                <%= Html.DropDownList("Status", Model.Status)%>
                <%= Html.ValidationMessage("status", "*") %>
            </p>

and the TaskForm view inherits System.Web.Mvc.ViewUserControl

A: 

What's in your task constructor? What is the value of .typeId on a newly created task? Is it a null reference?

For the view model sent to your Create view, you shouldn't be trying to set the selected list item unless your task constructor (or other initialization code) sets default values for those properties. If task.typeId is null, then your code that is building the select list will get an error.

Dennis Palmer
A: 

I understand that I will get a null value for the type or status if I dont add a value to the newly created task. What I dont understand and which I didnt make clear is the below. You can see the view model has a Countries property, and its selected value is set to Dinner.Country.. now Dinner.Country is not being set in the create action.. so how come this does not give a null exception?

//viewmodel code
            public DinnerFormViewModel(Dinner dinner) {
            Dinner = dinner;
            Countries = new SelectList(PhoneValidator.Countries, Dinner.Country);
        }

//controller code
        public ActionResult Create() {

            Dinner dinner = new Dinner() {
                EventDate = DateTime.Now.AddDays(7)
            };

            return View(new DinnerFormViewModel(dinner));
        } 

//view code
            <p>
            <label for="Country">Country:</label>
            <%= Html.DropDownList("Country", Model.Countries) %>                
            <%= Html.ValidationMessage("Country", "*") %>
        </p>
Pricey
A: 

My attempt at trying to understand this better.

    //controller code creating a select list in the viewmodel class.
//taskRepo.GetStatus() returns an IQueryable<Status>
        Status = new SelectList(taskRepo.GetStatus(), Task.Status);

//MVC Framework SelectList class constructor and ToEnumerable method
            public SelectList(IEnumerable items, string dataValueField, string dataTextField, object selectedValue)
            : base(items, dataValueField, dataTextField, ToEnumerable(selectedValue)) {
            SelectedValue = selectedValue;
        }

        private static IEnumerable ToEnumerable(object selectedValue) {
            return (selectedValue != null) ? new object[] { selectedValue } : null;
        }

I can see that SelectList uses its base class of MultiSelectList and that constructor is here:

        public MultiSelectList(IEnumerable items, string dataValueField, string dataTextField, IEnumerable selectedValues) {
        if (items == null) {
            throw new ArgumentNullException("items");
        }

        Items = items;
        DataValueField = dataValueField;
        DataTextField = dataTextField;
        SelectedValues = selectedValues;
    }

When I run this project the html is given as:

    <select id="Status" name="Status"><option>NPTaskManager.Models.Status</option>
<option>NPTaskManager.Models.Status</option>
<option>NPTaskManager.Models.Status</option>
<option>NPTaskManager.Models.Status</option>
</select>

Which is to be expected.

If I change the controller code to:

Status = new SelectList(taskRepo.GetStatus(), Task.Status.statusId.ToString(), Task.Status.description);

Then I get a NullReferenceException. Since this is not an ArgumentNullException It seems to me that the root of the exception is not the first SelectList argument. What I am trying to understand is how this all occurs? Is it because Task.Status needs to be added to Task in the create action of the controller?

I will change this code to use the LINQ approach that I used for the task above, all I am trying to achieve now is some understanding of what is going on.

Pricey
were you able to solve tht problem? if yes, how?
Shaharyar