Custom Data Annotations ( DataType ) - c#

Is it possible to add new datatypes to the existing DataAnnotations (I'm not looking for a validator but a raw data type). For example
Currnetly you have
[DataType(DataType.Html)]
public string Footer {get; set;}
And into the mix you can add ~Views/Shared/EditorTemplates/Html.cshtml
I'd like to be able to add [DataType(DataType.CSS)] I know in theory I could use a UIHint for adding a specific view, but if possible I'd like to do it at an even earlier stage and specify the datatype rather than relying on UI Hints.
Any pointers would be greatly appreciated. A Quick search of S.O seems a lot of answers around Custom meta-data types, custom validators, and multiple datatyps but I can't seem to find one for adding a new core data-type.

DataType has a second constructor that takes a string. However, internally, this is actually the same as using the UIHint attribute.
Adding a new core DataType is not possible since the DataType enumeration is part of the .NET framework. The closest thing you can do is to create a new class that inherits from the DataTypeAttribute. Then you can add a new constructor with your own DataType enumeration.
public NewDataTypeAttribute(DataType dataType) : base(dataType) { }
public NewDataTypeAttribute(NewDataType newDataType) : base (newDataType.ToString()) { }

Yes, you can. DataTypeAttribute has a constructor that accepts string.

Related

Apply SuppressImplicitRequiredAttributeForNonNullableReferenceTypes only to particular paths

Can you apply the SuppressImplicitRequiredAttributeForNonNullableReferenceTypes option to only a particular path pattern, e.g., .../v3/...?
We've gone through the trouble of enabling nullable contexts throughout our code, and ensuring all our parameters have the correct nullability. Now we want to utilize that for API validation. But since we don't want break any of our exising API behavior, we only want to apply the implicit Required attribute behvaior on paths for particular API versions. I.e., v2 would NOT have the validation, but v3+ would.
Is there any way to do this?
I can show you the way, but you have to walk through it and complete the implementation!
Ok, to see how SuppressImplicitRequiredAttributeForNonNullableReferenceTypes works, let's check the source code first!
Here's when the parameter is being used in DataAnnotationsMetadataProvider class.
https://github.com/dotnet/aspnetcore/blob/3ea008c80d5cc63de7f90ddfd6823b7b006251ff/src/Mvc/Mvc.DataAnnotations/src/DataAnnotationsMetadataProvider.cs#L343
Now let's see where DataAnnotationsMetadataProvider is used! It is added as a ModelMetadataDetailsProviders to MvcOptions.ModelMetadataDetailsProviders.
https://github.com/dotnet/aspnetcore/blob/3ea008c80d5cc63de7f90ddfd6823b7b006251ff/src/Mvc/Mvc.DataAnnotations/src/DependencyInjection/MvcDataAnnotationsMvcOptionsSetup.cs#L54
So you would need to create your own CustomValidationMetadataProvider and add it to MvcOptions.
builder.Services.AddControllers(op =>
{
op.ModelMetadataDetailsProviders.Add(new CustomValidationMetadataProvider());
});
public class CustomValidationMetadataProvider : IValidationMetadataProvider
{
public void CreateValidationMetadata(ValidationMetadataProviderContext context)
{
// context.ValidationMetadata.IsRequired = ???
}
}
Here you can have your own logic to set context.ValidationMetadata.IsRequired. Unfortunately I'm not sure if you can access the request path here, but you do have access to attributes on the model. So theoretically you could add an attribute to the models on your v3.
There are a few things that I could suggest here:
If you on C# 8 you can try nullable properties/fields for places where you want to allow nullable values. (recommended simplest one)
You can use custom Parameter Binding (non-trivial approach). You can find more details here https://www.strathweb.com/2013/04/asp-net-web-api-parameter-binding-part-1-understanding-binding-from-uri/
You can disable standard model validation and provide your own (where you should be able to specify path) and again non-trivial approach.

Not getting data-val attributes for GUID

I'm working on a bigger project and we have many views and almost all of them have a SelectList or more, whose value is a GUID. The viewmodel works fine and server side validation as well, the problem is that the HTML select element does not get any data-val attributes, we are using Html.DropDownListFor. It works fine when the value is short, string etc but not GUID.
Is there a way to get data-val attributes without adding an ValidationAttribute to all GUID properties in the viewmodels? Because there are a loot of them.
What worked for me in the end:
I got on the right track with Stephen Muecke's answer: we are using our own RequiredAttribute:
public class LocalizedRequiredAttribute : RequiredAttribute
{
public LocalizedRequiredAttribute()
{
ErrorMessageResourceName = "Required";
ErrorMessageResourceType = typeof (Resources.ErrorMessages);
}
}
But this does not add any client side validation attributes like the basic [Required] does, but it was easy to fix. Just add this code to your Application_Start():
DataAnnotationsModelValidatorProvider.RegisterAdapter(
typeof (LocalizedRequiredAttribute),
typeof (RequiredAttributeAdapter));
And now you will get data-val=true data-val-required="message".
Solution found here: https://stackoverflow.com/a/12573540/1225758
No data-val-* attributes are rendered because there are no jquery.validate.unobtrusive adaptors for GUID. The only one you could get out of the box is data-val-required (and the associated data-val) if you were to make the property nullable and add the [Required] attribute.
If you want some client validation you could use a [RegularExpression] attribute (not tested but I think ^[A-Za-z0-9]{8}-[A-Za-z0-9]{4}-[A-Za-z0-9]{4}-[A-Za-z0-9]{4}-[A-Za-z0-9]{12}$ should work).
However it seems unnecessary since you are using #Html.DropDownListFor() and (I assume) your building a SelectList on the controller which would contain only GUID's for the SelectListItem.Value property (why would you render an option which is not valid - other than perhaps a "--Please select--" label option for use with a [Required] attribute?).
Firstly, I would want to know what the problem is with having multiple GUID data annotations on your model properties?
Secondly I would say that it is far clearer and readable to other developers working on the project to have explicit validation going on with data annotations on each property than it is to have some "non-standard" validation voodoo going on.
You could probably achieve this with action filters (look for any properties of type Guid) but I think this will make the use/specification of your models less expressive of their intentions and simply confuse.
Guids have dashes in content and may occur problems. You may try to use .ToString('N') at the end of the Guids where they are being generated to remove dashes. Or you may write a jquery hack to add escape characters before dashes on client side. Or even more; try to implement your own guid validation approach as here: How to test valid UUID/GUID?
Or even even more, you can try to implement your own guid annotation attribute as here:
Validation of Guid

Model conversion coverage

We recently introduced a completely new data model which is different from our current model from the logical structure point of view. We also changed the language of the model from German to English, because we want to open the models structure as XML to our customer.
In order to be able to convert the model we implemented a explicit conversion which basically matches all properties from the different classes of the new model into our old model.
Like this:
private OldModel Convert(NewModel src)
{
var dst = new OldModel();
dst.Prop1 = src.SomeOtherProp
dst.Prop2 = Convert(src.ComplexProp);
//....
return dst;
}
Now we want to make sure, all of the properties of the new model are written into the old model for coverage and testing purposes. We also want to make sure we didn't forget any property and also guarantee that for future model extensions, we don't forget a property.
My idea would be to parse the codefile, extract all properties which are read from the new model, run over the new model with reflection compare them with the actual properties within it.
This solution feels not like a good one :-) Any suggestions?
I appreciate any help!
I suggest to use mapping libs like AutoMapper. They allow to configure mapping, converters and operate with public properties and specific methods.
We finally decided to parse the codefile with regular expressions like this:
#"private static [a-zA-Z0-9.]+[ ]+Convert[(][^)]*[)]\s*[{](?<body>[^{}]*(((?<Open>[{])[^{}]*)+((?<Close-Open>[}])[^{}]*)+)*(?(Open)(?!)))[}]";
It will match methods like this private static Namespace.ClassName Convert(Namespace.ClassName input) and will extract the methods body.
Since the converter methods follow a simple structural pattern, it was easy to extract the information I needed.

.Net/C# Reflection--Populating ComboBox

Greetings all,
I have a list of "Types" meeting a certain critera that I obtained through reflection. Each of the types is a different feature that the user will potentially choose at runtime. If I add more subclasses later, this dynamic implementation would save my having to remember to update the user control is the idea here.
The list of types is nice, but it'd be nice to display something more meaningful than the Name as it's written in code. For example, instead of "RacingBikeDesigner", I'd like to display "Racing Bike Designer", and maybe even display other properties associated with that type like "Description" so that the user knows what that particular choice does.
So I guess the question is, given a Type, how can I provide a more meaningful representation to the user? Could I maybe add a static field to each subclass and call that from the Type, or could I perhaps use a type converter somehow?
The user control (ListBox, ComboBox, etc) is bound to the return value below, but it's not user-friendly:
List<string> LeftHandedUserChoices = new List<string>();
Type[] AllTypesInThisAssembly = Assembly.GetAssembly(typeof(UserChoices)).GetTypes();
foreach (Type _currentType in AllTypesInThisAssembly)
if (_currentType.IsSubclassOf(typeof(UserChoices)))
LeftHandedUserChoices.Add(_currentType.Name);
return LeftHandedUserChoices;
Cheers,
Q
You have a couple of options for doing this. You could use an attribute on your type for the description, or put it in a static field/property on the Type and retrieve that using reflection.
If localization is an issue, you will probably want to store the resource string name, and display the resource value at runtme.
Add custom C# Attributes to your types.
One method is for you to parse class names based on the naming convention you are using (looks like Pascal in your case). For instance RacingBikeDesigner will become Racing Bike Designer. Here is a parsing example.

View model properties has changing validation rules at run time

I'm new to C# MVC and I'm trying to add some dynamic validation checks to my view models that are used in a form. For example, I have a string property called FirstName. I can add the attribute StringLength(10) and Required() to it.
My problem is, depending on some other field, the FirstName StringLength could vary from 10 to 20, etc. I still want to use the MVC validations but be able to modify it. I know that attributes are bound to the class so maybe I'm using the wrong thing.
I want the abilities for attribute validation but have it modifiable at run time. Is this possible?
The values in an attribute have to be literals. You can still use attribute based validation, but you will need to use the CustomValidation tag and point it at a method to use. If it depends on multiple fields in the object, you will want to put this on the class rather than the property.
It seems you can add validation attributes at runtime by implementing DataAnnotationsModelValidatorProvider:
Dynamic Attributes # forums.asp.net

Categories

Resources