I want to create custom client-side validator, but I want define validation rules via Data Annotations attributes at business logic layer. How can I access model validation attributes in runtime?
I want to write ‘generator’, which will convert this code:
public class LoginModel
{
[Required]
[MinLength(3)]
public string UserName { get; set; }
[Required]
public string Password { get; set; }
}
into this one:
var loginViewModel= {
UserName: ko.observable().extend({ minLength: 3, required: true }),
Password: ko.observable().extend({ required: true })
};
But not from .cs sources, of course. =)
Maybe reflection?
UPD
I’ve found this method: MSDN. But can’t understand how to use it.
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
This is the universal way how to do this:
private string GenerateValidationModel<T>()
{
var name = typeof(T).Name.Replace("Model", "ViewModel");
name = Char.ToLowerInvariant(name[0]) + name.Substring(1);
var validationModel = "var " + name + " = {n";
foreach (var prop in typeof(T).GetProperties())
{
object[] attrs = prop.GetCustomAttributes(true);
if (attrs == null || attrs.Length == 0)
continue;
string conds = "";
foreach (Attribute attr in attrs)
{
if (attr is MinLengthAttribute)
{
conds += ", minLength: " + (attr as MinLengthAttribute).Length;
}
else if (attr is RequiredAttribute)
{
conds += ", required: true";
}
// ...
}
if (conds.Length > 0)
validationModel += String.Format("t{0}: ko.observable().extend({{ {1} }}),n", prop.Name, conds.Trim(',', ' '));
}
return validationModel + "};";
}
Usage:
string validationModel = GenerateValidationModel<LoginModel>();
Output:
var loginViewModel = {
UserName: ko.observable().extend({ minLength: 3, required: true}),
Password: ko.observable().extend({ required: true}),
};
It’s good idea to cache the output
Method 2
As commented above – I believe T4 might be worth a shot here. A huge benefit is that it’s not executed at runtime (though it can, if that’s your requirement) and you can avoid all possible issues with runtime file generation. Hopefully a sufficient starting point:
<#@ template language="C#" debug="True" hostspecific="true" #>
<#@ output extension="js" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="EnvDTE" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="EnvDTE" #>
<#
var serviceProvider = Host as IServiceProvider;
if (serviceProvider == null)
{
throw new InvalidOperationException("Host is not IServiceProvider");
}
var dte = serviceProvider.GetService(typeof(DTE)) as DTE;
if (dte == null)
{
throw new InvalidOperationException("Unable to resolve DTE");
}
var project = dte.Solution.Projects
.OfType<Project>()
.Single(p => p.Name == "ConsoleApplication2");
var model = project.CodeModel
.CodeTypeFromFullName("MyApp.LoginModel")
as CodeClass;
//might want to have a list / find all items matching some rule
#>
var <#= Char.ToLowerInvariant(model.Name[0])
+ model.Name.Remove(0, 1).Replace("Model", "ViewModel") #>= {
<#
foreach (var property in model.Members.OfType<CodeProperty>())
{
var minLength = property.Attributes
.OfType<CodeAttribute>()
.FirstOrDefault(a => a.Name == "MinLength");
var required = property.Attributes
.OfType<CodeAttribute>()
.FirstOrDefault(a => a.Name == "Required");
var koAttributes = new List<String>();
if (minLength != null)
koAttributes.Add("minLength: " + minLength.Value);
if (required != null)
koAttributes.Add("required: true");
#>
<#= property.Name #>: ko.observable().extend({<#=
String.Join(", ", koAttributes) #>}),
<#
}
#>
}
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0