1

I'm just picking up .net MVC and I've come across something that I can't work out. I'm obviously missing some basic principle but would love some help.

I have a ViewModel with two IEnumerables that I want to use to create dropdownlistfors. My GET works fine, the lists are populated as expected.

Now I'm posting the ViewModel back to a POST method, not to do anything useful but just to try and understand how mvc works. I expected that I would simply be able to re-populate the dropdownlistfors from the model that was posted back - but I get a null reference exception.

Other values, such as partyid, in the ViewModel survive the POST so i'm confused.

I can get it to work if I repopulate the lists but that seems wrong.

Can someone give me a pointer?

My ViewModel

public class DemoViewModel
{
    //properties
    public IEnumerable<tbl_server_lookup> servers { get; set; }
    public int serverId { get; set; }

    public IEnumerable<tbl_site_lookup> sites { get; set; }
    public int siteId { get; set; }

    public int partyid { get; set; } 

    public string message { get; set; }

    public DemoViewModel()
    {

    }

}

My Controller

// GET: /Demos/Test/
[HttpGet]
public ActionResult Test()
{

DemoViewModel demo = new DemoViewModel();

using (var dbContext = new ADAPI.Models.db_ad_apiEntities2())
{
    var serverList = dbContext.tbl_server_lookup.Where(s => s.server_name != null);
    demo.servers = serverList.ToList();
    var siteList = dbContext.tbl_site_lookup.Where(w => w.site_name != null);
    demo.sites = siteList.ToList();
}
demo.message = "Enter the user id you would like to look up in the box below.";

return View(demo);
}

//
//POST: /Demos/Test/
[HttpPost]
[ValidateAntiForgeryToken] 
public ActionResult Test(DemoViewModel demo)
{

//It works if I uncomment this block...
/*using (var dbContext = new ADAPI.Models.db_ad_apiEntities2())
{
    var myQuery = dbContext.tbl_server_lookup.Where(s => s.server_name != null);
    demo.servers = myQuery.ToList();
    var siteList = dbContext.tbl_site_lookup.Where(w => w.site_name != null);
    demo.sites = siteList.ToList();

}*/

demo.message = "the user id you posted is: " + demo.partyid + ". The Server you selected is: ";// +demo.serverId;
return View(demo);

}

My View

@model ADAPI.ViewModels.DemoViewModel


<h2>Demos</h2>
<h3>@Model.message</h3>

@using (Html.BeginForm("Test","Demos"))
{
@Html.AntiForgeryToken()
<div class="">
<h4>Party ID</h4>
@Html.ValidationSummary(true)
<!-- input box for party id-->

@Html.TextBoxFor(model => model.partyid)



<!-- dropdown list of server types eg live vs test-->
@Html.DropDownListFor(model => model.serverId, new SelectList(Model.servers, "server_Id","server_name"))
@Html.DropDownListFor(model => model.siteId, new SelectList(Model.sites, "site_short_name","site_name"))



<input type="submit" value="Try" />

</div>
}

The Error The exception

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
pedro5000
  • 95
  • 1
  • 4
  • 2
    Because that list is not posted in the post call, which is completely normal by the way. In your `[HttpPost] Test()` function you should reload the list items. – Marthijn Dec 05 '14 at 10:50
  • 1
    Just to extend what Marthijn already point out, you would not want to send to the server what server already has. So only the selected drop down item is what should be sent to the server. – SBirthare Dec 05 '14 at 10:52
  • 1
    The values posted back to the view should be the values the user has selected for the fields. There is no point passing back the list of available options in a drop down list as the user does not provide this data. Simply repopulate the data from the original source in your post method. – Ben Robinson Dec 05 '14 at 10:52
  • Got it, thanks all. I've got to remember that with MVC what is in the html is pretty much all there is. So simple. btw I knew I shouldn't post the whole model back but was curious to see if it worked and if not why not. – pedro5000 Dec 05 '14 at 11:15
  • You're conflating MVVM with MVC. You shouldn't have a "view model" in MVC, unless you're using a client-side MVVM framework like knockoutjs. –  Dec 05 '14 at 15:26
  • @Will, could you explain a bit more? I'm following several recent MVC tutorials that all seem to encourage use of a ViewModel with MVC. Apart from my basic misunderstanding that made me ask this question, I'm finding it a nice way to work. – pedro5000 Dec 05 '14 at 21:49
  • @Will - Technically, yes, that's true.. by definition, your MVC "Model" is your ViewModel (as opposed to your DomainModel) because it's the presentational view. However, Most people, including Microsoft refer to the "Model" as your domain model, and they refer to a ViewModel as a model whose sole purpose is to provide a model suitable for rendering as opposed to a model suitable for your domain. ASP.NET-MVC terminology therefore includes ViewModel's that have nothing to do with client-side scripting. – Erik Funkenbusch Dec 07 '14 at 07:49
  • @pedro5000 - I believe Will is being pedantic in that MVC does not technically have a "view model", however the term is used to represent a model specific to your view rather than your domain (or data) model, which technically should be a separate concern from asp.net-MVC.. but again, this is pedantry. Most people just refer to them as view models. – Erik Funkenbusch Dec 07 '14 at 07:52
  • @Erik - Thanks for taking the time to explain. As you might have guessed I'm not a developer, more of a project manager. But I like to at least be able to understand the basics so I can talk to the dev teams properly. This has been really useful. – pedro5000 Dec 08 '14 at 08:29
  • @ErikFunkenbusch Not trying to be pedantic; new users may accidentally conflate MVVM and MVC. They are two different patterns. The concept of a "view model" has no place in MVC; the Controller performs the duties of the view model from MVVM. I know specifically that MS refers to the class passed to the View as the Model (see `@Model`) so I'm not sure where you're seeing View Model in MS docs or other material. Anyhow, OP seems confused, is trying to cram a ViewModel into MVC, thought I'd let him know of the error. Not being pedantic. –  Dec 08 '14 at 14:32
  • @ErikFunkenbusch Aaand the ironic hand of irony just dropped this perfect example of what I'm talking about [right here](http://stackoverflow.com/questions/27363213/how-to-resolve-arguments-are-not-valid-for-system-componentmodel-propertychangin). You and OP may get a kick out of it. –  Dec 08 '14 at 17:49
  • 1
    @Will - I realise I originally applied the wrong tags to this question but I don't think I am conflating MVVM and MVC. It seems to be accepted practice to use a "ViewModel" in .Net MVC. However, this is a different type of "ViewModel" than the one used in MVVM. See this link http://stackoverflow.com/questions/1939403/mvvm-viewmodel-vs-mvc-viewmodel?rq=1 Your comments have really got me thinking and I'm now going to look at creating a version of my test app with knockout or similar. Thanks! – pedro5000 Dec 08 '14 at 18:36
  • @Will - Yes, its confusing. The term "view model" does not refer to a formal ViewModel as in MVVM, but rather a model tailored to the view rather than the domain. It's really more of a subclass of "model" just as "domain model" is a subclass of the term model. As I said, technically MVC only has one "model" as you point out, this could be a view model or a domain model. view model is the recommended approach. good practice is that you use a model that contains only the necessary information to render your view. Domain models often have too much information, or conflicting info. – Erik Funkenbusch Dec 08 '14 at 18:52
  • @Will - For instance, your domain model might use DataAttributes (with entity framework) that mark a field as "required", however your view may not need this to be required at this point in the workflow, so you would use a customized model called a View Model rather than passing your domain model to the view for rendering. A simple google search of "MVC ViewModel" should give you all the information you need. – Erik Funkenbusch Dec 08 '14 at 18:54

2 Answers2

2

In MVC, model on the views are loaded in the controller action, they are not posted back along with the post action.

If you are used to ASPX's viewstate, there is no such thing in MVC, you need to load what you need for every view in the current action.

Joanvo
  • 5,677
  • 2
  • 25
  • 35
  • Joanvo, that was exactly my problem, I was thinking in terms of a viewstate. The comments above helped me sort my thinking out and you've described the problem perfectly. – pedro5000 Dec 05 '14 at 21:40
0

Dropdown lists are rendered into html as tag and returned to the server as plain single value.

You have to rebind/repopulate them on the server, wchich is annoying in scenarios like validation, where the same model should be returned to the client.

There is no support for that in the framework - you have to do it on your own.

One more thing - if you absolutely have to return the list items and want them back on the server, you can serialize tham and hide in some hidden field. But it's ugly and unsecure since anyone can change its value.

cyberhubert
  • 203
  • 1
  • 9