Dynamic form validation - emulation of RegularExpressionAttribute in CustomMetadataProvider - c#

My task is to perform validation on form which fields are constructed dynamically(upon database query). I would like to use data annotations. While model is dynamic I can't decorate properties with annotations, but I can use custom metadata provider for example inheriting from DataAnnotationsModelMetadataProvider.
Simply in global.asax at Application_start I supplied my own MetaDataProvider:
ModelMetadataProviders.Current = new MetadataProvider.CustomModelMetadataProvider();
I made a little hack, cause ModelMetadataProviders.Current is per application, my problem needed serving different metadata in each request, but it was not so hard.
This work fine for emulating IsRequired attribute, because metadata provider uses System.Web.Mvc.ModelMetadata and there is IsRequired property , but there is no property such as RegularExpression or anything similar.
So I run debugger and looked at ModelMetadata returned by original DataAnnotationsModelMetadataProvider for property with RegularExpression attribute, and I hadn't found regular expression there anyway.
I would love to get some hints on that.

I figured answer (by inspecting MVC 3 source code) which is as follows:
create custom ModelValidatorProvider for example inheriting from DataAnnotationsModelValidatorProvider
override GetValidators method
add yout custom provider to ModelValidatorProviders.Providers collection
GetValidators method returns IEnumerable<ModelValidator> so its enough to return RegularExpressionAttributeAdapter which inhertis from ModelValidator.

Related

Customize parameter in NSwag/ApiExplorer

I'm using NSwag to generate a Swagger document for my ASP.NET 6 API. My application uses strongly typed ids which are simple records with a value property.
public record Id(Guid Value);
Such ids are used as parameters in controller methods. Example:
public class MyController
{
[HttpPost]
public void Test(Id id) {}
}
Execution wise this works fine. I have a custom model binder alongside with a factory that automatically handles binding from GUID strings. Same goes for serialization where a custom json converter handles this. To ensure that NSwag properly generates a string property I have a type mapper that maps Id properties as strings in the model.
The only remaining issue is that NSwag generates strange input parameters for my controller actions. It ends up looking like this:
I was able to dig into the code of NSwag and find the corresponding code for generating parameters. The OperationParameterProcessor uses the data generated by the api explorer provided by ASP.NET. The api explorer seems to interpret this property wrong and therefore generates two parameters instead of just a single one.
I was unable to find any resources on how to customize the behavior of the api explorer so you can customize models. How do I convince ASP.NET that my Id object is just a plain string instead of a complex object?
It seems that adding a pseudo type converter did the trick. I decorated my Id class with a custom type converter
internal class IdentityObjectTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) =>
sourceType == typeof(string);
}
While this converter itself does not handle the value conversion it does provide ASP.NET with enough metadata so that the Api Explorer generated the proper definition. I would prefer a type converter over a model binder, however type converters are not flexible enough when it comes to polymorphism.
In the end the type converter fakes the type while the model binder does the actual binding. It's not ideal but probably the best solution as long as type converters are missing proper metadata when converting a value.

Localization in IValidatableObject.Validate()

I use Data Annotations to validate my Web API 2 models. For basic attribute-based validation (Required, Range, etc.) it's pretty easy to provide localized messages by injecting custom ModelMetadataProvider. However, for more complex rules I implement IValidatableObject which returns a sequence of ValidationResult:
public class ValidationResult
{
public ValidationResult(string errorMessage);
public ValidationResult(string errorMessage, IEnumerable<string> memberNames);
}
It looks like there is no way to specify ErrorMessageResourceName here. And I do not want to make my models dependent on the localization provider. How can I solve the problem?
It can happen if IValidatableObject.Validate method is called before the culture is available to the system. If the Validate method is called manually from the controller action, the error messages are properly localized.
Where you are setting the culture? you need to set it in Controller > ExecuteCore. Please have a look at this post or This post may help.

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.

How to add MetaData to a dynamically build MVC3 ViewModel?

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.

how to get data annotation attributes from properties

I am trying to build a grid, using class info and its properties along with the supported dataannotion attributes.
Class and annotations might be defined in different classes, like (Product and ProductMetaData classes) and tied togather with the metadatatype attribute, (might also be defined in a single class).
Would this make the DataAnnotaion attributes available in the base class itself or are there other means of getting the dataannotation attributes?
Any sample code that you can link, would also be very helpful.
thanks.
To read model metadata from a class you should generally create an instance of the DataAnnotationsModelMetadataProvider (in System.ComponentModel).
This class generates a ModelMetadata object which is a summary of the meta data on the class.
One of the main advantages of this approach is that it automatically respects metadata on buddy metadata classes.
By using ModelMetadata you also introduce a nice abstraction between the specific metadata attributes and the interpretation of their meaning. For example if you create some of your own metadata attributes, or you want to enforce other sources of metadata (e.g. treat all properties ending in Date as if they had DataType.Date applied even if they don't) then you can create your own DataAnnotationsModelMetadataProvider, add these extra rules, and all your other code works unchanged because it reads from a ModelMetadata class - not directly from knowledge of specific attributes.

Categories