How to add MetaData to a dynamically build MVC3 ViewModel? - c#

One of the key features of a project I'm working on is the ability for the user to configure Forms (as in "Forms" to fill-up) based on a pool of pre-existing field types (well known types, for instance "user name", "date of birth" etc. but also "generic types" like "string", "DateTime" etc.).
We used to have a static ViewModel that worked fine for the "well known" types and looked like this:
public class UserInputModel
{
[StringLength(200)]
public string Name { get; set; }
[Required(ErrorMessageResourceName = "BirthDateEmptyError", ErrorMessageResourceType = typeof(Resources.ErrorMessages))]
public DateTime BirthDate { get; set; }
//Here comes a lot of other properties
}
All the known properties were listed and we were showing or hiding them given the context.
But the last requirement came and changed all that. The user shall now be able to add as many generic type fields as he wants. In order to do this, we decided to make this InputModel entirely dynamic. It now looks like this:
public class UserInputModel
{
// Each ModelProperty has an "Id" and a "Value" property
public ICollection<ModelProperty> Properties { get; set; }
}
This works like a charm. The razor view only has to iterates over the collection, create the corresponding controls for each property of the collection in a more than standard way:
#Html.TextBoxFor(m => m.Properties[index].Value);
... and we nicely get the data back as a filled form.
=> This works fine, but we don't have any client-side validation. For this, we would need some Metadata... which we don't have via annotations anymore since we're dynamically creating the model.
In order to provide those MetaData, I created a CustomModelMetadataProvider that inherits from DataAnnotationsModelMetadataProvider and registered it as the new ModelMetadataProvider in the Global.asax. The CreateMetadata() function gets called upon creation of the ViewModel, and that for each of the properties of my ViewModel... sofar so good.
Where the problem starts: in order to add some metadata to the current property, I first need to identify which property I am currently looking at ("Name" has a maxlength of 200, "date of birth" hasn't so I cannot assign a maxlength to every property per default). And somewhow I didn't manage to do that yet since all the properties have the same name Value and the same container type ModelProperty.
I tried accessing the container of the property via reflection, but since the ModelAccessor's target is the ViewModel itself (because of the lambda expression m => m.Properties), the following construct gives me the ViewModel as a whole, not just the ModelProperty:
var container = modelAccessor.Target.GetType().GetField("container");
var containerObject = (UserInputModel)container.GetValue(modelAccessor.Target);
I've been flipping this over and over but cannot find a way to identify which ModelProperty I have in hand. Is there a way to do this?
Update: after flipping this in every possible direction for a while, we finally went another way. We are basically using unobstrusive javascript to use MVC's validation capabilities without touching attributes nor metadata. In short, we add HTML attributes like value-data="true" (and all other required attributes) to the #Html.TextBoxFor() statements. This works wonderfully for all the atomic validations (required, stringlength etc.).

Tim, you can leverage what appears to be client-side validation through Ajax with the Remote attribute on your properties.
Basically, you'll need to set up a validation controller and then write some smarts into that controller. But at least you'd be able to write some helper methods and keep it all in one place. You would have a series of validators, based on the meta data that you are presenting to the end users, and each validator method would work for a particular type with good re-use.
The one pitfall to this approach would be that you would need to write a validation method for each type and condition that you want to support. Sounds like you're having to go down that road anyways, though.
Hope this helps.

See if this article help you: Technique for carrying metadata to View Models with AutoMapper.
Also use this one for ideas (custom model metadata provider): changing viewmodel's MetadataType attribute at runtime
Fluent validation is probably the best option for you in my mind, but its obviously up to you to select the best match among those above.
Update
Try use ModelMetadata and override ModelMetadataProvider: Dive Deep Into MVC: ModelMetadata and ModelMetadataProvider. This way you completely customize your model metadata (this replaces data annotations) and you have complete control on what is happening, rather than relying on ASP.NET MVC.
Another good place to look at it is Creating your own ModelMetadataProvider to handle custom attributes.
Hope this all is of help to you.

Related

.NET c# - Customize Display Attribute

.NET MVC Application EF code first, using Identity 2
Each application user belongs to a specific user_type (ApplicationUser.user_type). (the application uses Identity roles too, but they are completely independent of this user_type).
also, I am extensively using Display attribute on properties of models and viewmodels with:
[Display(Name="some literal"]
public string someProperty { get; set; }
and in the view:
#Html.LabelFor(model => model.someProperty)
or
#Html.DisplayNameFor(model => model.someProperty)
But now I am required to, in some cases, display different things if the logged-in user is of a specific user_type ("Client").
What would be the best way to implement this? I am guessing maybe its possible to customize the Display attribute so that it accepts a parameter "userType", so I could decorate the properties with something like:
[Display(Name="This will be shown to users with user_type: Client", UserType="Client"]
[Display(Name="This will be shown to everyone else"]
public int myProperty { get; set; }
or something like that... but I have no idea how to do it... any help is appreciated
To me it seems that you are trying to put too much logic/responsibility in one place.
I recon you would manage to come up with something to deal with this scenario, but, if you do, you'll risk ending up with an inter tangled property which behaviour will depend on all sorts of external parameters. The more you'll add, the more complex it will become. That can be hard to maintain.
I am not fond of "keep it simple" but I think it does apply here, by keeping it simple in maintenance.
IMO you have a couple of options to help you out:
create a complete new view and a model for this client page
add a propery to your (view)model which contains the string.
add the string to the page and handle it with razor.
use a viewbag or similar temp data container
So, to sum it: I dont think expanding the Display attribute would be the way to go here and consider one (or another) of the methods mentioned above.

How to use same entity in POST and PATCH action with required property? [duplicate]

Say I have a controller CatController with actions for GET, POST and PUT. They all use the same Cat resource which could look like this:
public class CatDto {
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public bool IsFriendly {get; set; }
}
However, the Name and IsFriendly properties should only be required when creating a new cat (POST), but optional when updating it (PUT) to allow updating only a single property.
The way I've handled this until now is simply having two classes, a CreateCat and UpdateCat which have the same properties but different data annotations. However I don't want to have to maintain two almost identical classes.
I could of course validate the model manually in each action, but data annotations are very useful for things like global model validators and automatic generation of Swagger schemas.
I'm also using the Swagger schema to automatically generate SDK's (using ApiMatic), and that results in having two duplicate classes generated (CreateCat and UpdateCat) for what really should only be a single resource (Cat).
Is there an alternative way to achieve what I'm trying to do with only a single class?
I prefer to keep separate models to be honest.
You could have a base abstract ( or not ) model with all the common properties although this is not required and simply adds a third class. Is there a need for it? I'd say no.
There are slight differences between POST and PUT.Neither POST nor PUT require the Id property if you already have that in the PUT endpoint. This negates the need of checking if that Id in URL matches the Id in the model.
Your example does not make the difference visible, but in many cases there are fields you don't really want to update. For example let's say you have a Created and Updated date fields, you would not want to change your Created date via a PUT for example. The more data you have that you do not want to update via a PUT, the more obvious and worthwhile the differences between the models become.
In your case even with those 2 properties I would still create 2 different models, even if they are virtually the same, this sets the expectation on how the API works and creates a clear design in the mind of everyone else who is working on it.
I would recommend against the design you are asking for. According to RFC [RFC7231] you can find here it is advised not to have partial content update in PUT methods.
"An origin server that allows PUT on a given target resource MUST send
a 400 (Bad Request) response to a PUT request that contains a
Content-Range header field (Section 4.2 of [RFC7233]), since the
payload is likely to be partial content that has been mistakenly PUT
as a full representation. Partial content updates are possible by
targeting a separately identified resource with state that overlaps a
portion of the larger resource, or by using a different method that
has been specifically defined for partial updates (for example, the
PATCH method defined in [RFC5789])."
The preferred solution is to use PATCH method instead of PUT. Patch method is described in the RFC in the this link. PATCH method were introduced for partial resource modifications
So look up PATCH methods or if you want to use PUT maybe have a separate endpoint that only takes one of the two values.
More information about PATCH method can be found here
So either go for a PATCH method or create different models and end point to cater for partial update using PUT.

Different model requirements for POST and PUT

Say I have a controller CatController with actions for GET, POST and PUT. They all use the same Cat resource which could look like this:
public class CatDto {
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public bool IsFriendly {get; set; }
}
However, the Name and IsFriendly properties should only be required when creating a new cat (POST), but optional when updating it (PUT) to allow updating only a single property.
The way I've handled this until now is simply having two classes, a CreateCat and UpdateCat which have the same properties but different data annotations. However I don't want to have to maintain two almost identical classes.
I could of course validate the model manually in each action, but data annotations are very useful for things like global model validators and automatic generation of Swagger schemas.
I'm also using the Swagger schema to automatically generate SDK's (using ApiMatic), and that results in having two duplicate classes generated (CreateCat and UpdateCat) for what really should only be a single resource (Cat).
Is there an alternative way to achieve what I'm trying to do with only a single class?
I prefer to keep separate models to be honest.
You could have a base abstract ( or not ) model with all the common properties although this is not required and simply adds a third class. Is there a need for it? I'd say no.
There are slight differences between POST and PUT.Neither POST nor PUT require the Id property if you already have that in the PUT endpoint. This negates the need of checking if that Id in URL matches the Id in the model.
Your example does not make the difference visible, but in many cases there are fields you don't really want to update. For example let's say you have a Created and Updated date fields, you would not want to change your Created date via a PUT for example. The more data you have that you do not want to update via a PUT, the more obvious and worthwhile the differences between the models become.
In your case even with those 2 properties I would still create 2 different models, even if they are virtually the same, this sets the expectation on how the API works and creates a clear design in the mind of everyone else who is working on it.
I would recommend against the design you are asking for. According to RFC [RFC7231] you can find here it is advised not to have partial content update in PUT methods.
"An origin server that allows PUT on a given target resource MUST send
a 400 (Bad Request) response to a PUT request that contains a
Content-Range header field (Section 4.2 of [RFC7233]), since the
payload is likely to be partial content that has been mistakenly PUT
as a full representation. Partial content updates are possible by
targeting a separately identified resource with state that overlaps a
portion of the larger resource, or by using a different method that
has been specifically defined for partial updates (for example, the
PATCH method defined in [RFC5789])."
The preferred solution is to use PATCH method instead of PUT. Patch method is described in the RFC in the this link. PATCH method were introduced for partial resource modifications
So look up PATCH methods or if you want to use PUT maybe have a separate endpoint that only takes one of the two values.
More information about PATCH method can be found here
So either go for a PATCH method or create different models and end point to cater for partial update using PUT.

What does [Required] do?

I found nothing on the web what [Required] actually does. The msdn-article is not explorative at all.
static class Program
{
public static Main()
{
var vustomer = new CustomerClass();
}
}
public class CustomerClass
{
public string m_FirstName;
[Required]
public string m_LastName;
}
As far as i understand, that should throw an exception since m_LastName is required, but not set. But i don't get one. I don't get what it's good for and what this actually does.
RequiredAttribute, like all other attributes, does nothing by itself other than annotate something (in this case, a field of a type). It is entirely up to the application that consumes the type to detect the presence of the attribute and respond accordingly.
Your sample program does not do this, so the attribute does not have any visible effect. Certain frameworks such as ASP.NET MVC and WPF do check for and respond to the presence of the attribute.
This attribute is used by the Validator class to add validation errors based on any types that inherit from ValidationAttribute. This is used by MVC model validation, for example.
In C#, attributes are almost decoration to classes and properties.
Except for a few security related attributes, most do nothing. They are used by a higher-level framework to do something.
In the case of ASP.NET 4 MVC, only when the object is part of a request that attribute is used to generate an error.
If you want to use that attribute in any other environment, you must write code to inspect it.
It won't do anything from a plain old public static void Main()
Documentation on RequiredAttribute:
Specifies that a data field value is required.
However this validation is typically only performed in the UI layer. It is not "baked" into the constructor or other low-level usage. If you want to manually fire the validation you could do something like:
var customer = new CustomerClass();
var context = new ValidationContext(customer, serviceProvider: null, items: null);
var results = new List<ValidationResult>();
var isValid = Validator.TryValidateObject(customer, context, results);
if (!isValid)
{
foreach (var validationResult in results)
{
Console.WriteLine(validationResult.ErrorMessage);
}
}
To add to the current answers, these are some of the practical uses I can think of out of my head:
Entity Framework use this to model the database as not nullable fields.
Javascript client side validation provides javascript libraries for checking if the input field has any data, else prevent the form submission avoiding unnecesary roundtrips to the server.
Server side validation (when doing model binding) also check when you are posting a model with that decorator that the attribute passed in is in the request. This determines if the model state should be set to an invalid state or not. (1)
There are also annotation for JSON.NET library that changes how the model is serialized/unserialized. I'm pretty confident (but not sure) that the Validate Schema of Json.net does take into consideration the 'Required' attribute when validating a schema.
Other attribute decorators are used on web services, but 'Required' is not one I know has it uses in this scenario.
Web api uses this attribute to mark the property as required on documentation help pages and model binding.
You can create your own application logic that can be aware of the 'Required' property. For example, on a view to mark the property with an * in the label if it's required.
This are some uses but you are not limited to them.
(1) Note: if your property is, for example, an int and you decorate it with Required, model state will never be on a invalid state. You should use Nullable properties for this use-cases.

Validation in ASP.MVC 3.0

I have a model class :
public class YearlyChageRate
{
public int Year { get; set; }
public double Rate { get; set; }
}
and I want to check that Yeae is unique or no and in condition Year is not unique application show an error message to users.How can I check the Year filed is repeated or not?
Here is a good example:
http://tugberkugurlu.com/archive/asp-net-mvc-remote-validation-for-multiple-fields-with-additionalfields-property
And here too: MVC validation for unique
You can use Remote attribute in your model to perform check for unique value in database.
This is official example of Remote attribute: http://msdn.microsoft.com/en-us/library/gg508808(v=vs.98).aspx
And one more: http://www.a2zdotnet.com/View.aspx?Id=198
You could use the [Remote] validation attribute on your view model.
Although you can use DataAnnotations attributes for validation and the [Remote] attribute for checks against the DB, it's not a very good design choice.
Let me explain:
data access is a data-layer matter
validation is a business-layer matter
user input and feedback is a ui matter
With DataAnnotations, you're mixin 3 in 1. It can be faster, but surely not well designed.
You could try a more disciplinate approach, like this:
Have a method at business level that will take your object as a parameter, perform validation internally using a validation framework of your choiche;
This method will call the data access to persist the object only if the validation passed;
This method will always return to the UI the validated object, plus a collection of fields/errors if anything didn't validate;
When you read the output of the method in your ui, you can either display a success page if there were no errors, or redisplay the form with the validation errors returned. To do this, the use of the PRG pattern is highly recommended, as you should never display a page on a POST method. Google for the PRG pattern to learn more about it. MvcContrib has a nice ActionFilter called ModelStateToTempData to make the implementation of the PRG pattern something trivial.

Categories