In a recent project - i've hit an unexpected snag.
A class with simple public fields (note not properties) doesn't seem to want to play nice with the
ASP.net MVC 3.0 model binder.
Is this by design?
besides changing the fields to properties - any options here?
update
The reason for the simple fields (instead of properties) is because I'm working with a shared library between MVC and a Script Sharp project. Script sharp supports properties - but it becomes a mess with javascript in the view (using knockout.js)
So despite what i'd love (which is to just use properties) i'm using public fields in my dto classes.
I wanted to avoid having multiple definitions of the same classes. sigh
Is this by design?
Yes, only properties with public getter/setters work with the default model binder.
besides changing the fields to properties - any options here?
That's what you should do as you should be using view models anyway. And view models are specifically designed for the views. So changing the fields to properties is the correct way to do. Now if you don't follow good practices and try to use your domain models as input/output to actions then you will have to write a custom model binder that works with fields. It will be a lot of work though.
I know it's against the c# team's philosophy, but I think fields are clean in poco classes. Seems like less moving parts to me. Anyway,
Here's a model binder that will load fields. It's fairly trivial. Note that you can use a new object with Activator.CreateInsance or start with an existing object as a starting point. I was using Yes/No dropdowns for bool, you may have check boxes, in which case unfortunately you'll have to loop through fieldinfos and look for missing form inputs b/c html doesn't submit form items if it's a false checkbox.
~/Binders/FieldModelBinder.cs
using System;
using System.Reflection;
using System.Web.Mvc;
namespace MyGreatWebsite.Binders
{
public class FieldModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var obj = controllerContext.HttpContext.Session["CurrentObject"];// Activator.CreateInstance(bindingContext.ModelType);
var form = controllerContext.HttpContext.Request.Form;
foreach (string input in form)
{
FieldInfo fieldInfo = obj.GetType().GetField(input);
if (fieldInfo == null)
continue;
else if (fieldInfo.FieldType == typeof(bool))
fieldInfo.SetValue(obj, form[input] == "Yes");
else
fieldInfo.SetValue(obj, Convert.ChangeType(form[input], fieldInfo.FieldType));
}
return obj;
}
}
}
Startup.cs
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
ModelBinders.Binders.Add(typeof(FieldModelBinder), new FieldModelBinder());
}
}
Usage
[HttpPost]
public ActionResult MyGreatAction([ModelBinder(typeof(FieldModelBinder))] MyGreatProject.MyGreatNamespace.MyGreatType instance, string myGreatParameters)
{
DoSomethingGreatWithMyInstance();
return View();
}
Related
I have 2 questions.
First=> How can i pass value between 2 ViewModels ?
For example, I m adding data and showing it into the MainPAGE, and simultaneously I want to show the same data ( Observable Collection ) in the ChildPAGE too. Inside the ChildPAGE Xaml I assigned the BindingContext to ViewModel and I assigned listview’s data source to that Observable Collection .But I couldn’t make it work . I tried some examples, but I couldn’t manage it to work. If the data load in the ChildPAGE’s constructor then it works else doesn’t work. I thought , I would improve the performance with using one ObservableCollection, but I think, the mechanism in MVVM is different.
So how can I use one ObservableCollection in 2 pages.
Second => How can I pass data between ViewModels without using the constructor.
Example: I Have 2 Pages ( MainPage and ChidPage) and 2 ViewModels ( MainVM and ChildVM ) .
Situation => If I would pass data from MainPage to ChildPage , I would send data within the constructor .But I want to get data from Childpage to MainPage . So PopAsync doesn’t have a constructor. I also tried EventHandler but it doesn’t work.
Is the only solution is Messaging center? Or what do you advice for better performance? Also does the MessagingCenter reduces the performance because of high usage of RAM?
NOTE: ( I want to learn the mvvm architecture, so I don’t want to use other MVVM Frameworks. I want to get the idea of MVVM and C# comprehensively.)
Thanks in advance
You could create a model class that both view models depend on. The model itself would hold the observable collection, while the view models reference the observable collection. This is a good way to share data across various view models throughout the life of your app. Usually you will want to make the model a singleton to ensure that it really is the same data.
If you don't want to use messaging center, you can use an MVVM framework which comes with other benefits. I use Prism to simplify navigation and you can pass navigation parameters along.
Finally, and this is not the best option usually, you may maintain data in the App object. You can set values in the Properties collection. This is not advisable for complex objects.
EDIT
First you would have some data transfer object which the ObservableCollection would contain, unless you're just holding integers or something.
public class MyDTO
{
//fields for Data Transfer Object
}
DTO's are often among your models, but sometimes you need a composite class to hold DTO's and collections of DTO's
Here is a simple Model that would contain your ObservableCollection
public class MyCollectionModel
{
#region Singleton Pattern
private MyCollectionModel()
{
}
public static MyCollectionModel Instance { get; } = new MyCollectionModel();
#endregion
private ObservableCollection<MyDTO> _dtos;
public ObservableCollection<MyDTO> MyObservableCollection
{
get { return _dtos; }
set { _dtos = value; }
}
}
Note that this implements the Singleton pattern. It could even implment INotifyPropertyChanged as well.
Next your view models, imagine a MyVM1 and MyVM2, would reference the ObservableCollection in your Model with something like this.
public class MyVM1 : INotifyPropertyChanged // Do the same with MyVM2, which would be the binding context for view 2
{
private MyCollectionModel _model;
public MyVM1 ()
{
_model = MyCollectionModel.Instance;
}
private ObservableCollection<MyDTO> myVar;
public ObservableCollection<MyDTO> MyProperty //Bind to this in your View1
{
get
{
return _model.MyObservableCollection;
}
set
{
myVar = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I'm leaving quite a few things out here, and don't really have time to build an entire project just to test. I hope this helps.
You normally use await navService.PushAsync(new MyPage2(dataToShare)); to pass data between pages/VMs. This involves using constructors. Here is a simple example of it: https://stackoverflow.com/a/47873920/1508398
Since you don't want to use constructors, you may load the data on your child page from your service. Alternatively, you may cache the data on the local storage (client-side) and simply load it from there in your child/main page.
I'm investigating Web Api in ASP.NET vNext using the daily builds. In a web api 2x project, I use HttpParameterBinding and ParameterBindingAttribute in some situations (see http://bit.ly/1sxAxdk); however, I can't seem to find either in vNext. Do/will these classes exist? If not, what are my alternatives?
Edit (1-22-15):
I want to be able to serialize a complex JS object to a JSON string, put the JSON string in a hidden form field (say name="data"), submit the form, and then bind my parameter to that JSON object on the server. This will never be done by a human, but rather by a machine. I also want this very same mechanism to work if the JSON is sent directly in the request body instead of form data. I also need this to work for several different types of objects.
I've been able to accomplish this scenario in Web Api 2.2 in a few different ways, including a custom ModelBinder; however, I remember reading an MSFT blog post that suggested to use a ModelBinder for query string binding, formatters for request body, and HttpParameterBinding for more general scenarios. Is it okay to read the request body in a ModelBinder ASP.NET 5, or is there a better mechanism for that? If so, then case closed and I will port my ModelBinder with a few minor changes.
I'm not sure that IInputFormatter will work for me in this case either.
Here are two rough approaches
Approach 1:
A quick and dirty approach would be to start with a Dto model
public class Dto
{
public Serializable Result { get; set; }
public Serializable FromForm
{
get { return Result; }
set { Result = value; }
}
[FromBody]
public Serializable FromBody
{
get { return Result; }
set { Result = value; }
}
}
public class Serializable
{
}
And an action method
public IActionResult DoSomething(Dto dto)
{
// Do something with Dto.Result
}
Then write a custom model binder for Serializable, that just works with Request.Form this way you never actually read the body yourself, and Form only reads it if it of type Form.
The down side of this is that ApiExplorer will not provide correct details (but I think since this is none-standard you are going to be in trouble here anyways).
Approach 2
You can alternatively just use the code from BodyModelBinder and create a custom binder for Serializable type above, that first tries to get it from the Form, and if it fails tries to get it from the Body. The Dto class in that case is not necessary.
Here is some pseudo code
if (inputType is yourtype)
{
if (request.Form["yourkey"] != null)
{
Use Json.Net to deserialize your object type
}
else
{
fall back to BodyModelBinder code
}
}
With this approach you can make it generic, and ApiExplorer will say the way to bind the type is unknown/custom (we haven't decided on the term yet :) )
Note:
Instead of registering the model binder you can use the [ModelBinder(typeof(customBinder))] attribute to apply it sparingly.
Here is a link to the BodyModelBinder code.
There is a new [FromHeader] attribute that allows you to bind directly to http header values if that is what you need.
https://github.com/aspnet/Mvc/issues/1671
https://github.com/aspnet/Mvc/search?utf8=%E2%9C%93&q=fromheader
I'm still reasonably new to ASP.NET MVC 3. I have come across view models and their use for passing data from a controller to the view. In my recent question on model binding two experts suggested that I should use view models for model binding as well.
This is something I haven't come across before. But both guys have assured me that it is best practise. Could someone maybe shed some light on the reasons why view models are more suitable for model binding?
Here is an example situation: I have a simple class in my domain model.
public class TestParent
{
public int TestParentID { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
}
And this is my controller:
public class TestController : Controller
{
private EFDbTestParentRepository testParentRepository = new EFDbTestParentRepository();
private EFDbTestChildRepository testChildRepository = new EFDbTestChildRepository();
public ActionResult ListParents()
{
return View(testParentRepository.TestParents);
}
public ViewResult EditParent(int testParentID)
{
return View(testParentRepository.TestParents.First(tp => tp.TestParentID == testParentID));
}
[HttpPost]
public ActionResult EditParent(TestParent testParent)
{
if (ModelState.IsValid)
{
testParentRepository.SaveTestParent(testParent);
TempData["message"] = string.Format("Changes to test parents have been saved: {0} (ID = {1})",
testParent.Name,
testParent.TestParentID);
return RedirectToAction("ListParents");
}
// something wrong with the data values
return View(testParent);
}
}
So in the third action method which gets invoked when an HTTP POST arrives I used TestParent for model binding. This felt quite convenient because the browser page that generates the HTTP POST request contains input fields for all properties of TestParent. And I actually thought that's the way the templates that Visual Studio provides for CRUD operations work as well.
However the recommendation that I got was that the signature of the third action method should read public ActionResult EditParent(TestParentViewModel viewModel).
It sounds appealing at first, but as your models and view actions get increasingly complex, you start to see the value of using ViewModels for (most) everything, especially input scenarios.
Case 1 - Most web frameworks are susceptible to over-posting. If you are binding straight to your domain model, it is very possible to over-post data and maliciously change something not belonging to the user. I find it cleaner to bind to an input view model than have long string lists of white lists or black lists, although there are some other interesting ways with binding to an interface.
Case 2 - As your input grows in complexity, you'll run into times when you need to submit and validate fields not directly in the domain model ('I Agree' checkboxes, etc)
Case 3 - More of a personal thing, but I find model binding to relational domain objects to be a giant pain at times. Easier to link them up in AutoMapper than deal with MVC's modelbinder for complicated object graphs. MVC's html helpers also work more smoothly against primitive types than deep relational models.
The negatives of using ViewModels is that it isn't very DRY.
So the moral of the story is, binding to domain models can be a viable solution for simple things, but as the complexity increases, it becomes easier to have a separate view model and then map between the two.
I'm currently working on an MVC 3 project using Ninject as my DI, the business objects are stored in a separate assembly. I'm running into an issue with the controller parameters, when posting back for CRUD operations I'm getting the error "Cannot create an instance of an interface". I am aware that you can't create an instance of an interface, but it seems like the only way I can get around this is to use a custom model binder and pass the FormCollection through. This seems really messy and I want to keep as much type specific code out of the project as I can - hence interfaces everywhere and Ninject to DI the concretes. Not only does custom model binding seem messy - won't I also lose my DataAnnotations?
Some code to describe what I have:
public ActionResult Create()
{
// I'm thinking of using a factory pattern for this part
var objectToCreate = new ConcereteType();
return (objectToEdit);
}
[HttpPost]
public ActionResult Create(IRecord record)
{
// check model and pass to repository
if (ModelState.IsValue)
{
_repository.Create(record);
return View();
}
return View(record);
}
Has anyone run into this before? How did you get over it?
Thanks!
but it seems like the only way I can get around this is to use a custom model binder
A custom model binder is the correct way to go. And by the way you should use view models as action arguments, not domain models or interfaces.
Not only does custom model binding seem messy - won't I also lose my DataAnnotations?
I don't know why you think that a custom model binder would make things messy. For me it's a great way to separate mapping logic into a reusable class. And, no you will not lose DataAnnotations. They will work perfectly fine on the concrete instance that the custom model binder would return.
Data passed to controllers action are simply holders for values. There shouldn't be any logic in them so there is nothing to decouple from. You can use concrete types (e.g Record) instead of interface (IRecord)
I made the same simple mistake. Ninject injects parameters into your constructor, but you added parameters to the Index Controller action.
It should look like this:
public class HomeController : Controller
{
private IRecord _record;
public HomeController(IRecord record)
{
_record = record;
}
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application. " +
_record .HelloWorld();
return View();
}
}
Make sense?
I have one view model which is common for 3 to 4 views in this model I also define validation rules.Now problem is that in one of that view I want to overwrite that view model validation rules for two to three fields.so what I do? I don't want to make new view model for that view.
From an MVC architecture standpoint - this is exactly why you use view models.
You should create separate view models for each case. Use automapper (available for free on codeplex) to copy the values between your view model and your entity.
Don't even consider a different way, inheritance, etc - this is what ViewModels are for.
Three options I can think of:
Make a separate ViewModel using AutoMapper to handle some of the heavy lifting.
Make a subclass having different validation rules.
Make a custom ValidationAttribute which is context sensitive (Either by overriding the IsValid(Object, ValidationContext)method, or relying other context information from static methods/properties.
For instance, this Required validation attribute would be ignored if the request came from a certain URL:
public class CustomRequiredAttribute : RequiredAttribute
{
public override bool IsValid(object value)
{
if (HttpContext.Current.Request.Url != "urlwhennotrequired")
return base.IsValid(value);
return true;
}
}
If you do go ahead and use inheritance, then make sure that you inherit from abstract class. I think that as the system grows, you are likely to come across a scenario where your abstract class will have to be modified heavily,therefore If I were you, I'd create more view models, even if the code appears to be repetative. In the long term run you'll benefit because you'll be able to modify parts of your applications with as little side affects as possible.
My recommendation is basically what you don't want: create new model classes, but use inheritance to avoid repeating the properties you want. If you are adamantly opposed to creating separate models, you might look into implementing IValidatableObject and have it inspect other properties before validating the properties that you wish to vary.
EDIT:
I don't disagree with Tuliper's answer, but to flesh out my suggestions, consider a scenario in which you want to save a user's data. From one form, you are creating a user; from another, you are simply updating (this is a bit of a stretch but it's for purposes of illustration). The "create" form might require the name of a person referring the user, while the "update" form might not.
Using inheritance, you could do the following:
public class SaveUserModel
{
public int? UserId { get; set; }
...
}
public class CreateUserModel : SaveUserModel
{
[Required]
public string ReferredByName { get; set; }
}
Using IValidatableObject, you could do it this way:
public class SaveUserModel : IValidatableObject
{
public int? UserId { get; set; }
public string ReferredByName { get; set; }
...
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// if UserId is null, we are creating a user vs. updating
if (UserId != null && string.IsNullOrWhiteSpace(ReferredBySiteUrl))
yield return new ValidationResult("Please specify the name of the person who referred you.", new[] { "ReferredByName" });
}
}
To reiterate, I am not trying to push my answer. I would be inclined to reuse models if they are exactly the same across different views, but generally there are enough differences to warrant simply creating separate models. In the end, any perceived technical debt alleviated by adhering to DRY in this situation would a bit of a wash; models tend to be easy to maintain.