I have an MVC 3 app and I have created a generic wrapper object, which has some navigation properties and the wrapped object of T, whose values I'm editing/displaying.
public class NavigationViewModel<T>
{
public T Model { get; set; }
public NavigationHelper NavigationHelper { get; set; }
public NavigationViewModel() { }
public NavigationViewModel(T model, NavigationHelper helper)
{
this.Model = model;
this.NavigationHelper = helper;
}
}
My controller resolves this object nicely in an action like this:
public ActionResult Foo(NavigationViewModel<Bar> viewModel)
Code in my view looks like this:
#Html.EditorFor(model => model.Model.SomeProperty)
My colleague said that that code is not nice to read. I already have a strongly typed view, the Model and this Model has another property called Model. He suggested to rename the Model property to ViewModel and I agreed with his reasoning.
Now, the code with the renamed properties does not work anymore: NavigationViewModel viewModel is null. So I changed the signature of the HttpPost method to the following and it works again:
[HttpPost]
public ActionResult Foo(NavigationHelper helper, Bar viewModel)
I like this very much! I can directly access my viewModel in code, the code in the view makes sense and the helper object does not get in the way. I haven't seen this convention before and I guess it worked before because of the naming convention. Using a property called Model hinted at how to resolve the object. Without that property, it couldn't resolve it anymore.
I would like to adopt this for other kinds of helpers that contain view-specific properties, like select-lists or other properties that I otherwise might have put in my ViewBag. Would you guys recommend this approach or will I run into trouble later on using this?
I think I have a really simple answer for you, just don't name your action parameter viewModel, so change:
public ActionResult Foo(NavigationViewModel viewModel)
public ActionResult Foo(NavigationViewModel model)
Or any other parameter name that does not collide with your ViewModel property on your NavigationViewModel class.
Related
I have this controller
public class DownloadController : Controller
{
[HttpPost]
public FileContentResult GetFile(MyModel model)
{
// Action code
}
}
And this model
public class MyModel
{
public string Test { get; set; }
}
Passing model from the View to the controller works fine like this
#using (Html.BeginForm("GetFile", "Download", FormMethod.Post))
{
#Html.HiddenFor(m => m.Test)
<button type="submit" name="submit" class="submit">Download</button>
}
The model is correctly passed to the controller and I can do what I need to do with it.
Now, what I'm trying to achieve is to make this GetFile() controller action to be generic, so I can pass it any model, without strongly typing the model class in method signature, like I did in the example above.
I know I can achieve this by overriding GetFile() method once for each model that I have, but I'm wondering is there a better way to do this, to stay DRY as much as possible?
Thank you.
I'd suggest using a base class:
public class BaseGetFileModel {}
which various models will derive from.
[HttpPost]
public FileContentResult GetFile(BaseGetFileModel model)
EDIT:
OK, if you want a generic way of doing this, then you could do this:
[HttpPost]
public FileContentResult GetFile()
{
var someValue = Request["SomeValue"];
}
You don't accept any model parameter, you simply pick up POST'd values from the request. Or you could iterate through the request values collection, if you want to avoid hard-coding key names.
In ASP.Net Web API (RC) I have a test model class like so:
[ModelBinder]
public class TestRequest
{
public string Foo { get; set; }
public string Bar { get; set; }
}
My controller looks like this:
public class TestController : ApiController
{
public TestRequest Get(TestRequest model)
{
return model;
}
}
Now if I invoke the action via:
http://.../test?foo=abc&bar=xyz
neither values bind, because the model binder is expecting model prefixes, such that I actually need to call:
http://.../test?model.foo=abc&model.bar=xyz
I can understand that this is so that other action parameters can bind correctly, but in my case the model is a clean way of encapsulating all the possible action parameters so that I don't need to have a nasty action method signature with a whole lot of optional parameters. It also allows for easy model validation.
Is there any easy way to cause model binding to behave the same way as it would in MVC, or in a POST request?
Removing the ModelBinder attribute from your model class should work in the example you've posted. You'll run into issues for more complex method signatures, see Rick Strahl's comment: http://blogs.msdn.com/b/jmstall/archive/2012/04/16/how-webapi-does-parameter-binding.aspx#10302750
When a view model is created you can populate the options (e.g. used in a dropdown list) into a setter property of the view model.
The problem is that when that view model is later passed as a parameter (by the framework!) into an action method, those property values has not become automagically
repopulated, so if you need to redisplay the form because of validation errors, you need to repopulate those options again.
One potential solution, which I am asking for specifically in this question, is how to make the MVC framework instantiate the view model with constructor injection, which would provide the view model constructor with an implementation of some kind of data access object (e.g. a repository) that can be used for retrieving the options when they are requested by the view (e.g. in the helper method "DropDownListFor") ?
I think the solution might have something to do with implementations of IModelBinderProvider or IModelBinder but after having experimented with these things from example code snippets here and there on the net, I am still looking for a completely working example, with downloadable executable code without any missing piece of how putting all things together.
If you are looking for some alternative discussion about how to populate a select list, e.g. with "Dependecy Lookup" instead of "Dependecy Injection" you may want to check out the following discussion:
Best way to populate SelectList for ViewModel on GET/POST
Best way to populate SelectList for ViewModel on GET/POST
Some days ago I wrote the following follow-up-question in that thread about the "Dependecy Injection" I am now looking for in this thread:
https://stackoverflow.com/a/8674525/310457
(which provides a code example about the problem I am looking for a solution of)
But instead of hoping that someone will find that old thread with a less specific title, I have created this new question with a more specific subject about what I am looking for.
And I will also provide a link from that thread into this new question for anyone that want to follow-up regarding this specific solution I am looking for.
I'm assuming you want to have your ViewModels automatically injected with something via their Constructor - for example some kind of configuration object that the View will use to determine what to show. I'm also assuming that this approach is causing a "No parameterless constructor defined for this object" error when MVC tries to automatically create and bind a model instance, from the arguments of your Controller Action. Let's also then assume that we will use a DI framework to inject the SiteConfig object into our Controllers automatically at runtime.
This means that the only problem we have to solve is how to get the injected object from our Controller into its Actions' ViewModels when they are automatically bound.
So let's define a base model for others to inherit from.
BaseViewModel
public class BaseViewModel
{
public ISiteConfig SiteConfig { get; set; }
public BaseViewModel(ISiteConfig siteConfig)
{
this.SiteConfig = siteConfig;
}
}
And now let's create a model that inherits from it.
IndexViewModel
public class IndexViewModel : BaseViewModel
{
public string SomeIndexProperty { get; set; }
public IndexViewModel (ISiteConfig siteConfig) : base(siteConfig) {}
}
And now let's define a Base Controller that our Controllers will inherit from.
BaseController
public abstract class BaseController : Controller
{
protected BaseController(ISiteConfig siteConfig)
{
_siteConfig = siteConfig;
}
private readonly ISiteConfig _siteConfig;
public ISiteConfig SiteConfig
{
get
{
return _siteConfig;
}
}
}
Now we define our actual controller.
HomeController
public HomeController: BaseController
{
public HomeController(ISiteConfig siteConfig): base(siteConfig) {}
}
Assuming we're using Ninject for DI, Ninject would be configured to automatically create the Controller and pass a concrete ISiteConfig object into its Constructor at runtime.
Now we add our Action to the Controller.
Index Action
public ActionResult Index(IndexViewModel model)
{
return View(model);
}
And so this is the point where without doing anything else, MVC will explode with a "Parameterless Constructor" error if you try to call the Index Action, because MVC can't find a ViewModel constructor that takes no arguments.
And so, the answer. We need to override the default ModelBinder.
BaseViewModelBinder
public class BaseViewModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
if (modelType == typeof(BaseViewModel) || modelType.IsSubclassOf(typeof(BaseViewModel)))
{
var baseControl = controllerContext.Controller as BaseController;
if (baseControl == null)
{
throw new Exception("The Controller must derive from BaseController");
}
var instance = Activator.CreateInstance(modelType, baseControl.SiteConfig);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => instance, modelType);
return instance;
}
else
{
return base.CreateModel(controllerContext, bindingContext, modelType);
}
}
}
And we need to set this as the default model binder in global.asax.cs :
protected void Application_Start()
{
...
ModelBinders.Binders.DefaultBinder = new BaseViewModelBinder();
}
That's all. As you can see, when you view the Index Action now, MVC will use our custom model binder. It will realise that the IndexViewModel derives from BaseViewModel, and so will attempt to spin up an IndexViewModel instance using the ISiteConfig it can find in the Action's Controller (because the Controller derives from BaseController).
I've successfully removed references to Request.Form in my code, but I haven't seen any ASP.NET MVC 3 support for binding Request.Files to my view model. Would best practice be to just pass the Request.Files object (HttpFileCollectionBase) to whatever method processes my attachments? E.g.,
[HttpPost]
public ActionResult UpdateStatus(StatusViewModel vm)
{
bool updated = HandleUpdate(Request.Files, vm);
...
return View("Updated");
}
You can simply declare Action parameters of type HttpPostedFileBase, like so:
public ActionResult Method(HttpPostedFileBase fileUploaded)
You can bind to arrays of like-named instances, as well.
public ActionResult Method(HttpPostedFileBase[] filesUploaded)
and, HttpPostedFileBase can be a member of your view model.
public class MyModel{
public string Name{get; set;}
public HttpPostedFileBase FileUploaded{get; set;}
}
I'm [finally!] tackling MVC (version 3) after years of ASP.NET forms development. I have a strong background in n-tier application architecture, and I'm trying to approach this new project properly, with a clear separation of concerns, etc.
What I've done is start out with code-first by creating my POCOs. From this, the framework created my database.
Then, I implemented the Repository pattern by putting all my EF query and CRUD methods in a Repository class for each of my POCO classes in my Models assembly. This way, my Controllers don't need to know a thing about how I access my data via the EF. Great.
Finally, I created a few ViewModel classes in my Models assembly. My intent is, for certain actions (such as create and edit), I reference my ViewModel classes from the RAZOR views, instead of my POCO classes. This way, I can have my POCO class as well as a SelectList for populating a Drop Down within my ViewModel. Both populated by references to the associated Repository classes, which are called from my Controller Actions. I think I'm on a roll now:
class MyObject
{
public int ID {get;set}
[Required]
[StringLength(512)]
public string Name {get;set;}
}
class MyViewModel // ViewModel for a specific view
{
public MyObject MyModel {get;set;} // the model that is being edited
// other data the view might need, set by the controller
public string SomeMessage { get; set; }
public List<SomeObject> SomeObjects {get;set;} /// e.g. for a drop-down list
// My constructor below that populates the "SomeObjects" list, and accepts the
// "MyObject" class as a parameter in order to set the "MyModel" property above...
// ..........
}
The Problem...
Before I started using my ViewModel classes from my Controller Create and Edit actions, I passed in the POCO class directly. Everything worked fine when I hit the save button from my Edit form within my view:
Old Code:
[HttpPost]
public ActionResult Edit(MyObject mine)
{
if (ModelState.IsValid)
{
myRepository.Update(mine);
myRepository.SaveChanges();
return RedirectToAction("Index");
}
return View(mine);
}
When I hit save, my POCO class (MyObject) would be returned, automagically populated with values from the form, it would successfully save, and life was peachy.
When I switched to passing in my ViewModel (MyViewModel), everything fell apart.
I was able to refer to my ViewModel (MyViewModel) by setting the #model reference at the top of my Edit view. I was then able to populate my form fields from my POCO class (MyObject) that is part of the ViewModel. I was even able to populate the DropDownList from the SomeObjects collection in the ViewModel and preselect the correct one from my MyObject class I was editing. Everything seemed fine UNTIL...
When I hit the save button and my Controller's Edit ActionResult (POST action) was called, the MyObject class that is passed in to the ActionResult (public ActionResult Edit(MyObject mine)) was null.
Then, I tried changing the passed in object to my ViewModel (public ActionResult Edit(MyViewModel myVM)), which had the referenced MyObject class (MyModel) as null.
What am I missing?
I know it has to be something so incredibly simple that it's staring me in the face and I cannot see it!
Look at the FormCollection, the names of the keys should match the properties of the class you want to fill. This is how the default modelbinding of MVC works.
Wim,
Thanks so much for your help. I did have s parameter-less constructor, I had just omitted it from the example.
I actually tracked down the issue. In all fairness, the code I typed in was not the actual code since I didn't have it in front of me when I posted this. What the issue was is that my entity model class reference in my ViewModel actually had its set accessor as private:
public MyObject MyModel {get;private set;}
This had prevented the modelbinder from populating that property when posting back during the controller's save method.
What I'm left to do now is to move my validation logic from my EF POCO to my ViewModel, as seems to be the recommended action in this type of pattern.
Thanks for your time, and I hope this helps out other people with similar issues who are new to this framework.