UpdateModel without validation - c#

Is there a possibility in ASP.NET MVC3 to set the properties of a view model from a FormCollection without invoking validation specified by validation attributes on the properties?
UpdateModel<T>(T model) also invokes validation.
The background:
I can only validate the model if a connection to the database is present, but this connection can only be established, if the data from one specific form field is correct (kind of an access code for each organisation). If the connection is not established, an exception is thrown.
When the data entered in this field is incorrect, I don't want to loose the values entered in all other form fields, but present the already entered values again to the user and give him a change to correct the errors.
So what I need is basically something like conditional validation or no validation by the model binder at all.
Is there anything like this built-in in ASP.NET MVC or do I need to write my own UpdateModel method, calling a (custom) model binder?

Why not pass the viewmodel into your method, rather than formscollection? That way you can do
[HttpPost]
public ActionResult Update(UpdateViewModel model)
{
if (!Model.IsValid)
{
return View(model);
}
}
So if the validation fails your user is going to be directed back to the Update view, with the model already populated from the information they submitted.
Now, in my opinion, having a validation attribute require a database connection, which in turn can throw exceptions is the wrong way to go about this. Those sorts of validation I move to a validation service, which is injected into the controller (and takes in the repository stuff it needs via DI as well). Each validation service will take in a view model and return a collection of ValidationResult instances, which I then attach to the model validation results via an extension method to the Controller class (both lifted from the Project Silk stuff MS P&P is pushing out)
This allows for easier testing as you can mock up the repository and have it return the correct results for testing ...

Related

Updating Controller and Views after adding column to Model

I am new to Entity framework, so this is probably a stupid question.
I have added a new field to a Model class. I have created the migration and updated the database and this all works as expected. The new column has appeared in the SQL table.
I have manually modified the Create/Details Views (I'm guessing this isn't automatic) to include the new column. The new column is called "Level".
However, my Controller class doesn't seem to have picked up the changes. It doesn't insert the value when creating a new row. There are references to column names in the Controller class, but the new column name isn't there. E.g.
public async Task<IActionResult> Create([Bind("Id,Name")] Course course)
Is there any way to "refresh" the Controller to pick up the changes? Or will I have to manually edit the lines where the columns are named and add the new column?
If your view has a control bound to this new Level field and you want to pass it to the controller to write, then you will need to add it to the Bind attribute:
public async Task<IActionResult> Create([Bind("Id,Name,Level")] Course course)
While it might look like when you pass a Model to a view, and have a Form in the view that calls an action on the Controller passing that Model back, this isn't actually what happens. ASP.Net MVC is merely formatting the code in that way. Honestly this is a bit of a bad practice when using EF Entities as it trips up quite a few developers expecting that Entity references are being passed between server and view. It leads to confusing problems and is also an open door to data tampering when used incorrectly.
When the controller passes the Model to the View(), it is passing that Model to the View Renderer to compose the HTML view that will be sent to the browser. So if you're passing a Course entity, that entity does not travel to the client, it is consumed by the Server to build the HTML.
When your Form goes to call the controller, it will automatically append any bound fields that it knows about. This will include any bound entry controls, and if there are any details that don't have edit controls bound to them, you need to use bound Hidden controls to ensure that the MVC Javascript knows to pick those up too.
If you use a Bind attribute, this tells MVC which properties coming back from a Form submit or Ajax call that you actually want to accept. If "Level" isn't in there, the value will be ignored even if passed. The View does not pass an actual entity, it will be a set of named parameters that MVC has some magic smarts to convert into a Model object based on convention.
The Action could be written as either:
public async Task<IActionResult> Create([Bind("Id,Name,Level")] Course course)
or
public async Task<IActionResult> Create(int id, string name, string level)
where in the second case you'd need to handle the creation and population of your Course entity using the values passed in.

asp.net mvc ViewState implementation

I working on a web project where I first get data from the database and bind to the Html control. If there is a validation error I will send the same view back for rendering with the displayed validation errors. When the page comes up, there is an exception. I stepped through the code and found that the model was passed will null collection. Basically any property that was not binded to a textbox was changed to null. I was told not to use session or viewdata to keep temp storage. So I call a method SaveViewState where it save all the property value of the ViewModel property to a static variable like so
private static MyViewModel _viewModel;
private MyViewModel SaveViewModel(MyViewModel viewModel)
{
if (_viewModel == null)
{
_viewModel = new MyViewModel ();
}
if (!string.IsNullOrEmpty(viewModel.MyName))
_viewModel.MyName= viewModel.MyName;
if (!string.IsNullOrEmpty(viewModel.Number))
_viewModel.Number= viewModel.Number;
if (!string.IsNullOrEmpty(viewModel.Address))
_viewModel.Address= viewModel.Address;
if (!string.IsNullOrEmpty(viewModel.State))
_viewModel.State= viewModel.State;
}
It works but I think it is very inefficient and there must be a better way to implement ViewState in MVC with Session or ViewData or HiddenFields? By the way, I was told not to use those three.
Any help is appreciated. Thanks.
I am not sure if this solution is worse than using a session or hidden fields. In your action you should return the corresponding view with the same model that was posted. The ActionResult should be something like this:
public ActionResult SomePost(SomeModel model)
{
if (!ModelState.IsValid())
{
//error in validation
return View(model);
}
//post save redirect and stuff
return ... redirect?
}
The ModelState.IsValid() will test according to the DataAnnotations. Standard attributes like [Required], [MaxLength] etc. are available.
In this configuration, the use of a SaveViewModel function is not required. If your collection is null after post: re-query it, post it or fetch it from a ViewData like object.
There are good reasons not to use those three you mentioned, but if you know that reason you might want to consider it:
1) Use of session: will make scalability difficult because every request in a session must hit that specific server.
2) Hidden fields: Not really a problem IFF you realize the hidden field can be manipulated in a browser. So don't store ID's there
3) ViewData: basically breaks the MVC pattern; you can use it to store data but that's what a model is for. It totally legitimate to use ViewData from a filter. To provide some general functionality for example.

What is the point of the MVC implementation of IValidatableObject if it can't be used to perform full validation of the object?

Please can someone help me, because I'm having real trouble getting on with validating my objects that have bound in my controller actions in one swoop.
I thought that IValidatableObjects Validate() method would fire every time the binding takes place, but this isn't true... if there is a general model error it won't fire.
This leads me to wonder, how am I supposed to perform a full complex validation on my object and return the full set of validation errors? No one wants to fix all of the reported errors on a web form, then submit it to have more returned to them.
I thought I might just be able to perform all of my validation in the Validate() method, but this isn't true because seemingly there is no way of getting away from the general validation of models. For example if you were to attempt to bind a string to an int, it doesn't silently fail, it adds a model validation error and then doesn't fire Validate to perform further validation on the object.
So I can't perform all of my validation just using one method:
General model validation using validation attributes
Reasons
Can't perform advanced validation, such as conditional validation based on other values within the model
It isn't designed to perform anything more than individual field validation
Implement IValidatableObject and perform full validation in Validate() method
Reasons
There is no getting away or 'switching off' the general model validation so that it fails silently so that I can perform full validation on the object
IValidatebleObject isn't always fired if general model validation fails in any way, such as a failed binding
How can I perform full validation of my object in one go, no matter whether the binding was successful or not?
Why doesn't IValidatableObject fire the Validation() method regardless of the binding success?
The problem you seem to be running into is that validations occur in different parts of the framework for different reasons.
First, client validation occurs. If all your fields have unobtrusive client validation, then all validation will occur at once on the client.
Second comes model binding. If an error occurs in trying to bind an item to it's model entry, then those items will first fail. For instance, if you try to bind the string "xxx" to a DateTime, it's going to throw a validation error because it cannot convert "xxx" to a DateTime. And, since DateTime is non-nullable, it can't simply put null there.
Third comes your actual server-side data attribute validations. If you have only partial client-side validation (meaning not all fields have client-side validation) then you can get the odd situation that it throws validation errors for some items client-side, then after the user has fixed those problems and submits, the server-side validation finds errors and you get more errors thrown.
Finally, IValidatableObject is called. Unfortunately, IValidatableObject has no client-side validation, so you would either have to create client-side validation for these fields, or completely disable client-side validation in order to get server-side validation to occur all at once.
IValidatableObject is a rather general purpose interface, and is used in more places than just MVC. MVC uses it because it's a convenient already existing interface. And, it was present before client-side validation was added to MVC. A better approach is to create a ValidationAttribute derived attribute that implements client-side validation, then supplying the proper javascript plug-ins for unobtrusive-validation.
If validation fails in any of the steps, it does not go on to the next step. ie., if client-side validation fails, it doesn't call server-side. If Data attributes fail, it doesn't call IValidatableObject.
One way around this is described in this post:
How to force MVC to Validate IValidatableObject
A few different options come to mind. If you need complex validation to be done on the client use of a javascript framework such as Angular or Knockout would be beneficial, or failing that adding a jQuery validation plugin such as:
http://jqueryvalidation.org/
It may also be worthwhile reviewing the ModelStateDictonary methods and seeing if these could be of help - e.g. manually calling ModelState.AddModelError for complex validation scenarios in your controller.
http://msdn.microsoft.com/en-us/library/system.web.mvc.modelstatedictionary_methods(v=vs.118).aspx
example:
bool valid = true;
if (string.IsNullOrEmpty(model.JobNumber))
{
ModelState.AddModelError("jobNumber", "Please enter job number");
valid = false;
}
return View(model);

MVC - validation - should ViewModel have access to DB

I have a view model which should check that label of a new entity is unique (not in DB yet).
At the moment I've done it in the view model class:
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (PowerOrDuty != null)
{
if (PowerOrDuty.Identifier == null)
{
using (var db = new PowersAndDutiesContext())
{
var existingLabels = db.PowersAndDuties.Select(pod => pod.Label);
if (existingLabels.Contains(PowerOrDuty.Label))
{
yield return new ValidationResult("Cannot create a new power or duty because another power or duty with this label already exists");
}
}
}
......
Please note that this is a small internal app with small DB and my time is limited, so the code is not perfect.
I feel that DB access from view models might be a bad practice. Should view model have direct DB access? Should it be able to call a repository to get the available labels? Should validation requiring DB access be done in a controller instead?
Should view model have direct DB access?
I think this should be avoided at all cost
Should it be able to call a repository to get the available labels ?
This is not the concern of a ViewModel.
This would introduce some complexity in the testing of your ViewModel (which should almost need none) I guess it is a sign of trouble coming.
Should validation requiring DB access be done in a controller instead ?
Maybe, if by "DB" you mean "Repository". But what comes to mind is a separate custom validation class that you will be able to (un)plug, test, and reuse, in another controller for ajax validation, etc
I think that accessing DB from VM is not wrong... AFAIK it is not breaking MVC concept (since it is a presentation layer concept). Said that, it could be better if you have the Validate method provided by a Service Layer.
But all the logic related to the content of the ViewModel, it is better kept in the VM than in the Controller. Cleaner controllers is better.
Your view model should not be tied to your context, it only cares about displaying data and validating it after a submit. You can perform validation like a required field or a value in range, but you can't know if a label already exists in your database.
You can't also fetch a list of "forbidden labels" before displaying your form, in order to test your label afterwards, because that list could have changed during this time (another user updating you database).
In my opinion, validation at model level should focus on what it can validate without knowledge of the data source, and let your database notify you errors like submitting a duplicate value in a field which has an unique constraint. You'll catch exceptions coming from your database for errors like those, and manage them accordingly.
Anyway, i think there's no straightforward answer for a problem like this.
I personally like the ViewModels to be anemic -- simply classes with properties.
For custom server-side validation like this, I prefer it go either in a service, with the consumption of the service in your controller, or even behind a custom validator.
With a custom validator, you could even (optionally) execute the validation remotely. That gets a little more complex though, but I've done it using a generic remote validator that consumes an Ajax action method to perform the validation, and wire that up through both the client validator and remote validator (to ensure you have your validation logic in a single method).
But which ever way you go, I think it is more common -- and in my opinion, more clean -- to keep all logic out of your ViewModel. Even in a simple app, your ViewModel should be dumb to your database context. Ideally, only services (not necessarily web services, but just an abstraction layer) are aware of your database context.
This, to me, should be done regardless of the size of application. I think the effort and complexity (it only adds another assembly to your solution) is worth the abstraction you get. Down the road, if you happen to decide to consume your services from another application, or if you decide to swap out your database context, it's much easier with that abstraction in place.

Refresh ModelState to remove errors

Refreshing the ModelState
Hi, I have a question about the ModelState in an ASP.NET MVC controller.
When the user selects a certain option from the view, the start date and end date for the "certification" will be set based on the other dates entered.
The problem with this is the certification dates come back as null and our CertificationMetaData class specifys the fields as [Required] so the ModelState is invalid as soon as the action loads.
Removing the ModelSate errors manually allows this to work but I was wondering if there is a better way to do this? Is there a way to refresh the ModelState? Should I make the fields not required? Or should I add a date value from the view with javascript?
public ActionResult Create(FormCollection fc, Certification certification, Absence absence)
{
if (certification.CertificationTypeID == 1)
{
certification.CertificationStartDate = absence.StartDate;
certification.CertificationEndDate = absence.StartDate.AddDays(7);
this.ModelState.Remove("CertificationStartDate");
this.ModelState.Remove("CertificationEndDate");
}
if (this.ModelState.IsValid)
{
// save
return RedirectToAction("Index");
}
return View();
}
Also as you can see I have hardcoded the ID value for the certification type. What is the best way to compare values with lookup table values? Is an enum the best way to go?
Thanks
The following approach refreshes the model state and allows you to keep your model design consistent with [required] attributes etc.
In my case I want my model to have a required field that normal level users using an API can't change, so I've done this:
ModelState.Remove("ChangeDate");
ModelState.Add("ChangeDate", new ModelState());
ModelState.SetModelValue("ChangeDate", new ValueProviderResult(club.ChangeDate, DateTime.Now.ToString(), null));
That way you don't need to remove your required fields, and you also don't need to supply a date in javascript.
Obviously this is a personal thing, but I wouldn't remove the error messages.
If I was going for the simple solution then I would remove the [Required] attribute and add validation code to the controller to add the error if the dates were missing or set them to the alternate value if it was the correct type.
If I was going for the more complex solution I would put the validation at the Model level. Possibly a base class or and interface that the model must implement. A ValidationHelper class with a static Validate(IValidate object) method that will inspect the ValidationAttributes and calls a Validate method on the Model. It would then return a collection of ValidationErrors. Then a custom ModelBinder would be written that understands the Model validation and maps these to ModelState errors.

Categories

Resources