Attribute based validation with fluent validation doesn't seem to work with asp.net-mvc - asp.net-mvc

i followed all of these steps in this tutorial:
Created a validator class
public class ProjectValidator : AbstractValidator<ProjectViewModel>
{
public ProjectValidator()
{
//RuleFor(h => h.Milestone).NotEmpty().WithName("Milestone");
RuleFor(h => h.Applications).NotNull().WithName("Application");
RuleFor(h => h.InitiativeName).NotNull().WithName("Business Aligned Priority");
RuleFor(h => h.BusinessDriverId).NotNull().WithName("Business Driver");
RuleFor(h => h.FundingTypeId).NotNull().WithName("Funding Type");
RuleFor(h => h.Description).NotEmpty().WithName("Description");
RuleFor(h => h.Name).NotEmpty().WithName("Project Name");
RuleFor(h => h.Sponsors).NotNull().WithName("Sponsors");
}
}
Put an attribute on my DTO to specific this validtor
[Validator(typeof(ProjectValidator))]
public class ProjectViewModel
{
}
but after a form post when i go to check the ModelState errors list, the errors i see are coming from the asp.net-mvc default validation.
public ActionResult UpdateMe(ProjectViewModel entity)
{
Project existingProject = this.Repository.Fetch<Project>(entity.Id);
UpdateProperties(entity, existingProject);
var allErrors = ModelState.Values.SelectMany(v => v.Errors);
if (allErrors.Count() > 0)
{
any suggestions on why its not picking up the fluent. validator ?? I have added an image below of what i see on the gui
if i call the validator directly in code it works just fine:
ProjectValidator validator = new ProjectValidator();
ValidationResult result = validator.Validate(entity);

I'm not sure what type of HTML element FundingTypeId is but I'm assuming it is a dropdown list. If nothing is selected then it will give you this error. This is unfortunately one of the limitations of the FV integration with MVC which is caused by the bad design of MVC's default model binder. That message is not generated by FV but rather is produced by the DefaultModelBinder, in this case where the incoming value cannot be converted to the property type.
Check out these 2 questions which I posted on the Fluent Validation discussion forum:
http://fluentvalidation.codeplex.com/discussions/250798
http://fluentvalidation.codeplex.com/discussions/253389

Related

EF6 nested collection count (without fetching) with/without automapper

According to this documentation page there is a neat way to count nested collection elements without actually fetching them. Great. But the example is for a single item.
Of course, I could iterate and do this in post-processing. But that's not neat.
And here could something like Automapper come into play. It has a ProjectTo extension for IQueryable which can be parameterized (in quite a weird way imho).
Well, I can't get it working. Let's say I have a Client entity that has associated Device entities, and I want to set the DeviceCount property of the target.
By combining the examples from the two sources, something like this could work:
_configuration = new MapperConfiguration(cfg =>
{
SMPContext ctx = null; ///why not an extra property in conf?
cfg.CreateProjection<Client, Models.ViewModels.Client>()
.ForMember(d => d.DeviceCount, conf => conf.MapFrom(s => ctx.Entry(s).Collection(c => c.Devices).Query().Count()));
});
}
...
public async Task<IActionResult> Index()
{
var res = await
_context
.Clients.ProjectTo<SMP.Models.ViewModels.Client>(_configuration, new { ctx = _context })
.ToListAsync();
...
I was experimenting with this too:
public async Task<IActionResult> Index()
{
var configuration = new MapperConfiguration(cfg =>
{
cfg.CreateProjection<Client, Models.ViewModels.Client>()
.ForMember(d => d.DeviceCount, conf => conf.MapFrom(s => _context.Entry(s).Collection(c => c.Devices).Query().Count()));
});
var res = await
_context
.Clients.ProjectTo<SMP.Models.ViewModels.Client>(configuration)
.ToListAsync();
...
Either way, I get such an exception, which is quite understandable:
InvalidOperationException: The LINQ expression
'___context_0.Entry(NavigationTreeExpression Value:
EntityReference: Client Expression: c).Collection(c =>
c.Devices).Query() .Count()' could not be translated.
I have tried using Map instead of ProjectTo, but no luck either, as IMapper.Map().AfterMap passes the entire enumerable as parameter instead of per item.
Is there any neat solution?
It looks like there is a simple and neat solution with AutoMapper after all: CreateProjection is smart enough to create a performant SQL mapping in such a case:
builder.Services.AddAutoMapper(config =>
config.CreateProjection<SMP.Models.DataModels.Client, SMP.Models.ViewModels.Client>()
.ForMember(d => d.DeviceCount, o=> o.MapFrom(s => s.Devices.Count))
);
Using it is simple as well:
var res = await _context.Clients
.ProjectTo<Models.ViewModels.Client>(_mapper/*injected IMapper*/.ConfigurationProvider).ToListAsync();
The generated SQL query looks like this:

How to show c# validation attributes on query parameters in Swagger

I'm using Swagger with ASP.Net Core 2.1 Web API project. Here's an example controller action method:
[HttpGet]
public string GetString([Required, MaxLength(20)] string name) =>
$"Hi there, {name}.";
And here's what I get in the Swagger documentation. As you can see, Swagger shows the Required attribute, but not the MaxLength one:
If I use Required and MaxLength attributes on a DTO class that's the argument of a POST action method, then Swagger shows them both:
How can I get Swagger to show MaxLength (and other) validation attributes for query parameters?
Note: I have tried to replace the string name argument with a class that has one string property called name - Swagger produces exactly the same documentation.
In .NET Core, you can use ShowCommonExtensions = true, with given sequence (ConfigObject on top).
public static IApplicationBuilder UseR6SwaggerDocumentationUI(
this IApplicationBuilder app)
{
app.UseSwagger();
app.UseSwaggerUI(c =>
{
//Allow to add addition attribute info on doc. like [MaxLength(50)]
c.ConfigObject = new ConfigObject
{
ShowCommonExtensions = true
};
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Asptricks.net API");
c.RoutePrefix = "api_documentation/index";
c.InjectStylesheet("/swagger-ui/custom.css");
c.InjectJavascript("/swagger-ui/custom.js");
c.SupportedSubmitMethods( new[] { SubmitMethod.Patch });
//Collapse model near example.
c.DefaultModelExpandDepth(0);
//Remove separate model definition.
c.DefaultModelsExpandDepth(-1);
});
return app;
}

Kendo MVC Grid with Popup editor and multiselect

preface : this worked at one point, I changed something and I have no idea what has caused it to stop working. This is on MVC5 with kendo 2014.3.1411.545
So I have a kendo mvc grid and I have my template called out like this
.Editable(configurator =>
configurator
.Mode(GridEditMode.PopUp)
.TemplateName("someEditor")
.Window(x => x.Width(850))
.CreateAt(GridInsertRowPosition.Bottom)
.AdditionalViewData(new {CompanyId = Model})
ok, fine, nothing crazy. The editor template has a kendo multiselect like this
#(Html.Kendo().MultiSelectFor(model => model.OrderIds)
.DataTextField("Text")
.DataValueField("Value")
.DataSource(data =>
{
data.Read("AllOrders", "Orders", new { categoryId });
})
my Model has a list of orderIds that are just strings
public class SomeModel
{
//usual, basic string properties, initialized it as a list
public IEnumerable<string> OrderIds { get; set; }
}
and I've even tried string[], ICollection and list -- nothing seems to serialize it back to the controller, at least not anymore. The binding doesn't seem to pick it up no matter what I try. Might have something to do with trying too many things for way too long now.
The datasource of AllOrders is generating an anonymous object ( new { Text = Order.Number, Value=Order.Id} ) by selecting an orderId and its display number. I tried a List and a few other structures just for fun for the multiselect but nothing has got me back to working. The data is in the post, just as an array of { text:"",value:"" } ... Any ideas what I could be missing?
Try setting ValuePrimitive to true
#(Html.Kendo().MultiSelectFor(model => model.OrderIds)
.DataTextField("Text")
.DataValueField("Value")
.ValuePrimitive(true)
.DataSource(data =>
{
data.Read("AllOrders", "Orders", new { categoryId });
})

Multiple IEnumerable implement in mvc3 on VIew

#{IEnumerable<BC.Models.APPLICATION> data = ViewBag.list;}
#Html.Grid(data).Columns(columns =>
{
columns.Add(c => c.APPLICATION_NO).Titled("Application No").Filterable(true);
})
But i want to do something like that :
#if(some conditon)
{
#{IEnumerable<BC.Models.APPLICATION> data = ViewBag.list;}
}
else
{
#{IEnumerable<BC.Models.RIGHTS> data = ViewBag.list;}
}
#Html.Grid(data).Columns(columns =>
{
columns.Add(c => c.APPLICATION_NO).Titled("Application No");
})
But its not working can anybody have some idea about it.
Now
if i do something like this it works
#if(some conditon)
{
#{IEnumerable<BC.Models.APPLICATION> data = ViewBag.list;}
#Html.Grid(data).Columns(columns =>
{
columns.Add(c => c.APPLICATION_NO).Titled("Application No");
})
}
else
{
#{IEnumerable<BC.Models.RIGHTS> data = ViewBag.list;}
#Html.Grid(data).Columns(columns =>
{
columns.Add(c => c.APPLICATION_NO).Titled("Application No");
})
}
My problem is that APPLICATION_NO property present in both Model class so i don`t want to use
#Html.Grid(data).Columns(columns =>
{
columns.Add(c => c.APPLICATION_NO).Titled("Application No");
})
Twice in my code.
Your problem is that you are not using when of the most important concepts in MVC: view models.
As for your last comment, what you want to use is a view model, i.e. a class created specifically to send data to the view to show it.
To do so:
create a public class which has the APPLICATION_NO (needed for the view model class)
create another public class which will be your view model. That's a class that shapes the data as you need it on the razor template (in this case, it will hold a list of the class defined in 1)
in your controller, return the view passing the model as second parameter. I.e. don't use the ViewBag/ViewData, but a view model instead, like this return View("ViewName", model)
use the model in your view: declare the model type using #model and use it inside the Razor templae with the provided Model variable
In this way, you shape the data on the server, and avoid including a lot of code in you Razor template (which is not advisable). And, of course, you have intellisense, and your templates become typed.
Code for 1:
public class ApplicationModel
{
public int APPLICATION_NO {get; set;}
}
Code for 2:
public class ApplicationsViewModel
{
public List<ApplicationModel> Applications { get; set; }
}
Code for 3 (inside the controller)
var model = new ApplicationsViewModel();
if (...) // include the condition to choose the kind of data inside the list
{
model.Applications = list.Select(item =>
new ApplicationModel { APPLICATION_NO = item.APPLICATION_NO } ).ToList()
}
else
{
// same code here, for the other kind of data in the list
}
// return the view with this model
return View("ApplicationView", model);
Code for 4:
// decalre the model type at the beginning
#model YourNamespace.ApplicationViewModel;
// access the model using the Model variable
#Html.Grid(Model.Applications).Columns(columns =>
{
columns.Add(c => c.APPLICATION_NO).Titled("Application No");
})
This allows you to build MVC applications with several advantages, for example testability, code reusing, readability or availability of a ModelState. Belive me, many of theses things are really,really important, specially the ModelState.
Besides you can use code annotations (attributes that give extra info to the HTML helpers) in your view model, that attributes can provide labels, validation and some other automatic functionality.
Don't doubt to include all the needed properties inside your view model.
Using a view model allows you to create an ad-hoc class without the need to change you domain or business layer classes, i.e. you don't need to use interfaces, code annotations and so on. However many times it's interesting to add the code annotations in the business classes and nest them inside the view models.
Remember tha sometimes you can use the same view model to post the data back to the server, specifying it as the paramter type of your POST action.
By the way, the interface solution is a good one, but this solution doesn't required the interface. (This solution would have a better implementationusing that interface, but that's your choice).
#if(some conditon)
{
#{IEnumerable<BC.Models.APPLICATION> data = ViewBag.list;}
}
else
{
#{IEnumerable<BC.Models.RIGHTS> data = ViewBag.list;}
}
#Html.Grid(data).Columns(columns =>
{
columns.Add(c => c.APPLICATION_NO).Titled("Application No");
})
The code can't work because data is declared into if blocks.
If the grid has to work only on shared fields of the two classes you can think about using an Interface that APPLICATION and RIGHTS will implement and change the code like this:
#{IEnumerable<BC.Models.IAPPLICATION_NO> data = (IEnumerable<BC.Models.IAPPLICATION_NO>)ViewBag.list;}
#Html.Grid(data).Columns(columns =>
{
columns.Add(c => c.APPLICATION_NO).Titled("Application No");
})
where IAPPLICATION_NO is an interface like:
public interface IAPPLICATION_NO
{
string APPLICATION_NO { get; }
}
I don't know what APPLICATION_NO is, so I used string and the interface can define only get for grid.
Otherwise, if you need to display different data for those two types you should consider using two views or different grid declaration in the if blocks.
I worked on a sample of my answer on VS:
I attach you the code:
public interface AInterface
{
string AProperty { get; }
}
public class AList : AInterface
{
public string AProperty { get; set; }
}
public class BList : AInterface
{
public string AProperty { get; set; }
}
these are the classes
now the controller:
public class TestController : Controller
{
public ActionResult Index()
{
var random = new Random((int)DateTime.Now.Ticks);
if (random.Next() % 2 == 0)
ViewBag.List = new List<AList> { new AList { AProperty = "Atestvalue" } };
else
ViewBag.List = new List<BList> { new BList { AProperty = "Atestvalue" } };
return View();
}
}
and the view:
#{
ViewBag.Title = "Index";
IEnumerable<TestMvcApplication.Interfaces.AInterface> test = ViewBag.List;
}
<h2>Index</h2>
#foreach (var item in test)
{
<div>
#item.AProperty
</div>
}
this solves your problem as you can see
Without using interfaces:
#{IEnumerable<dynamic> data = (IEnumerable<dynamic>)ViewBag.list;}
#Html.Grid(data).Columns(columns =>
{
columns.Add(c => c.APPLICATION_NO).Titled("Application No");
})
but you lose the IntelliSense completion and if the member is missing I think you receive a runtime error.
I tried your Grid assembly but it uses c# Expression and it's incompatible with dynamic.
Another solution could be casting one list to another using LINQ in the controller:
IEnumerable<BC.Models.Application> data;
if (some condition)
{
data = applicationList; //applicationList's type is IEnumerable<BC.Models.Application>
}
else
{
data = rightsList.Select(t => new Application { APPLICATION_NO = t.APPLICATION_NO }); //rightsList's type is IEnumerable<BC.Models.RIGHTS>
}
ViewBag.list = data;
In the view you can keep the working code you posted at the top of the question. You have not multitype IEnumerable support because you use only one type but without using a common interface between these classes I think we must go to reflection but I think it's hard to write that code.
Why not
#{string columnName = "default column name";
if(some_condition)
{
// I'm not sure what's going on with the data variable
columnName = "alternate column name";
}
else
{
// Again, do your stuff with the data variable
}
}
#Html.Grid(data).Columns(columns =>
{
columns.Add(c => c.APPLICATION_NO).Titled(columnName);
})
I think that you're going to end up with (at best) confusing code if you try to be too clever in your views. A grid for rendering IEnumerable<A> isn't adaptable to rendering IEnumerable<B> unless A:ISomeInterface and B:ISomeInterface and the grid renders ISomeInterface. Alternatively just pass the column name as a property of the view model.

asp.net mvc validation messages localization for numbers (data-val-number) [duplicate]

Assume this model:
Public Class Detail
...
<DisplayName("Custom DisplayName")>
<Required(ErrorMessage:="Custom ErrorMessage")>
Public Property PercentChange As Integer
...
end class
and the view:
#Html.TextBoxFor(Function(m) m.PercentChange)
will proceed this html:
<input data-val="true"
data-val-number="The field 'Custom DisplayName' must be a number."
data-val-required="Custom ErrorMessage"
id="PercentChange"
name="PercentChange" type="text" value="0" />
I want to customize the data-val-number error message which I guess has generated because PercentChange is an Integer. I was looking for such an attribute to change it, range or whatever related does not work.
I know there is a chance in editing unobtrusive's js file itself or override it in client side. I want to change data-val-number's error message just like others in server side.
You can override the message by supplying the data-val-number attribute yourself when rendering the field. This overrides the default message. This works at least with MVC 4.
#Html.EditorFor(model => model.MyNumberField, new { data_val_number="Supply an integer, dude!" })
Remember that you have to use underscore in the attribute name for Razor to accept your attribute.
What you have to do is:
Add the following code inside Application_Start() in Global.asax:
ClientDataTypeModelValidatorProvider.ResourceClassKey = "Messages";
DefaultModelBinder.ResourceClassKey = "Messages";
Right click your ASP.NET MVC project in VS. Select Add => Add ASP.NET Folder => App_GlobalResources.
Add a .resx file called Messages.resx in that folder.
Add these string resources in the .resx file:
FieldMustBeDate The field {0} must be a date.
FieldMustBeNumeric The field {0} must be a number.
PropertyValueInvalid The value '{0}' is not valid for {1}.
PropertyValueRequired A value is required.
Change the FieldMustBeNumeric value as you want... :)
You're done.
Check this post for more details:
Localizing Default Error Messages in ASP.NET MVC and WebForms
This is not gonna be easy. The default message is stored as an embedded resource into the System.Web.Mvc assembly and the method that is fetching is a private static method of an internal sealed inner class (System.Web.Mvc.ClientDataTypeModelValidatorProvider+NumericModelValidator.MakeErrorString). It's as if the guy at Microsoft coding this was hiding a top secret :-)
You may take a look at the following blog post which describes a possible solution. You basically need to replace the existing ClientDataTypeModelValidatorProvider with a custom one.
If you don't like the hardcore coding that you will need to do you could also replace this integer value inside your view model with a string and have a custom validation attribute on it which would do the parsing and provide a custom error message (which could even be localized).
As an alternate way around this, I applied a RegularExpression attribute to catch the invalid entry and set my message there:
[RegularExpression(#"[0-9]*$", ErrorMessage = "Please enter a valid number ")]
This slightly a hack but this seemed preferable to the complexity the other solutions presented, at least in my particular situation.
EDIT: This worked well in MVC3 but it seems that there may well be better solutions for MVC4+.
From this book on MVC 3 that I have. All you have to do is this:
public class ClientNumberValidatorProvider : ClientDataTypeModelValidatorProvider
{
public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata,
ControllerContext context)
{
bool isNumericField = base.GetValidators(metadata, context).Any();
if (isNumericField)
yield return new ClientSideNumberValidator(metadata, context);
}
}
public class ClientSideNumberValidator : ModelValidator
{
public ClientSideNumberValidator(ModelMetadata metadata,
ControllerContext controllerContext) : base(metadata, controllerContext) { }
public override IEnumerable<ModelValidationResult> Validate(object container)
{
yield break; // Do nothing for server-side validation
}
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
yield return new ModelClientValidationRule {
ValidationType = "number",
ErrorMessage = string.Format(CultureInfo.CurrentCulture,
ValidationMessages.MustBeNumber,
Metadata.GetDisplayName())
};
}
}
protected void Application_Start()
{
// Leave the rest of this method unchanged
var existingProvider = ModelValidatorProviders.Providers
.Single(x => x is ClientDataTypeModelValidatorProvider);
ModelValidatorProviders.Providers.Remove(existingProvider);
ModelValidatorProviders.Providers.Add(new ClientNumberValidatorProvider());
}
Notice how the ErrorMessage is yielded, you specify the current culture and the localized message is extracted from the ValidationMessages(here be culture specifics).resx resource file. If you don't need that, just replace it with your own message.
Here is another solution which changes the message client side without changed MVC3 source. Full details in this blog post:
https://greenicicle.wordpress.com/2011/02/28/fixing-non-localizable-validation-messages-with-javascript/
In short what you need to do is include the following script after jQuery validation is loaded plus the appropriate localisation file.
(function ($) {
// Walk through the adapters that connect unobstrusive validation to jQuery.validate.
// Look for all adapters that perform number validation
$.each($.validator.unobtrusive.adapters, function () {
if (this.name === "number") {
// Get the method called by the adapter, and replace it with one
// that changes the message to the jQuery.validate default message
// that can be globalized. If that string contains a {0} placeholder,
// it is replaced by the field name.
var baseAdapt = this.adapt;
this.adapt = function (options) {
var fieldName = new RegExp("The field (.+) must be a number").exec(options.message)[1];
options.message = $.validator.format($.validator.messages.number, fieldName);
baseAdapt(options);
};
}
});
} (jQuery));
You can set ResourceKey of ClientDataTypeModelValidatorProvider class to name of a global resource that contains FieldMustBeNumeric key to replace MVC validation error message of number with your custom message. Also key of date validation error message is FieldMustBeDate.
ClientDataTypeModelValidatorProvider.ResourceKey="MyResources"; // MyResource is my global resource
See here for more details on how to add the MyResources.resx file to your project:
Here is another solution in pure js that works if you want to specify messages globally not custom messages for each item.
The key is that validation messages are set using jquery.validation.unobtrusive.js using the data-val-xxx attribute on each element, so all you have to do is to replace those messages before the library uses them, it is a bit dirty but I just wanted to get the work done and fast, so here it goes for number type validation:
$('[data-val-number]').each(function () {
var el = $(this);
var orig = el.data('val-number');
var fieldName = orig.replace('The field ', '');
fieldName = fieldName.replace(' must be a number.', '');
el.attr('data-val-number', fieldName + ' باید عددی باشد')
});
the good thing is that it does not require compiling and you can extend it easily later, not robust though, but fast.
Check this out too:
The Complete Guide To Validation In ASP.NET MVC 3 - Part 2
Main parts of the article follow (copy-pasted).
There are four distinct parts to creating a fully functional custom validator that works on both the client and the server. First we subclass ValidationAttribute and add our server side validation logic. Next we implement IClientValidatable on our attribute to allow HTML5 data-* attributes to be passed to the client. Thirdly, we write a custom JavaScript function that performs validation on the client. Finally, we create an adapter to transform the HTML5 attributes into a format that our custom function can understand. Whilst this sounds like a lot of work, once you get started you will find it relatively straightforward.
Subclassing ValidationAttribute
In this example, we are going to write a NotEqualTo validator that simply checks that the value of one property does not equal the value of another.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class NotEqualToAttribute : ValidationAttribute
{
private const string DefaultErrorMessage = "{0} cannot be the same as {1}.";
public string OtherProperty { get; private set; }
public NotEqualToAttribute(string otherProperty)
: base(DefaultErrorMessage)
{
if (string.IsNullOrEmpty(otherProperty))
{
throw new ArgumentNullException("otherProperty");
}
OtherProperty = otherProperty;
}
public override string FormatErrorMessage(string name)
{
return string.Format(ErrorMessageString, name, OtherProperty);
}
protected override ValidationResult IsValid(object value,
ValidationContext validationContext)
{
if (value != null)
{
var otherProperty = validationContext.ObjectInstance.GetType()
.GetProperty(OtherProperty);
var otherPropertyValue = otherProperty
.GetValue(validationContext.ObjectInstance, null);
if (value.Equals(otherPropertyValue))
{
return new ValidationResult(
FormatErrorMessage(validationContext.DisplayName));
}
}
return ValidationResult.Success;
}
}
Add the new attribute to the password property of the RegisterModel and run the application.
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
[NotEqualTo("UserName")]
public string Password { get; set; }
...
Implementing IClientValidatable
ASP.NET MVC 2 had a mechanism for adding client side validation but it was not very pretty. Thankfully in MVC 3, things have improved and the process is now fairly trivial and thankfully does not involve changing the Global.asax as in the previous version.
The first step is for your custom validation attribute to implement IClientValidatable. This is a simple, one method interface:
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
ModelMetadata metadata,
ControllerContext context)
{
var clientValidationRule = new ModelClientValidationRule()
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "notequalto"
};
clientValidationRule.ValidationParameters.Add("otherproperty", OtherProperty);
return new[] { clientValidationRule };
}
If you run the application now and view source, you will see that the password input html now contains your notequalto data attributes:
<div class="editor-field">
<input data-val="true" data-val-notequalto="Password cannot be the same as UserName."
data-val-notequalto-otherproperty="UserName"
data-val-regex="Weak password detected."
data-val-regex-pattern="^(?!password$)(?!12345$).*"
data-val-required="The Password field is required."
id="Password" name="Password" type="password" />
<span class="hint">Enter your password here</span>
<span class="field-validation-valid" data-valmsg-for="Password"
data-valmsg-replace="true"></span>
</div>
Creating a custom jQuery validate function
All of this code is best to be placed in a separate JavaScript file.
(function ($) {
$.validator.addMethod("notequalto", function (value, element, params) {
if (!this.optional(element)) {
var otherProp = $('#' + params);
return (otherProp.val() !=
}
return true;
});
$.validator.unobtrusive.adapters.addSingleVal("notequalto", "otherproperty");
}(jQuery));
Depending on your validation requirements, you may find that the jquery.validate library already has the code that you need for the validation itself. There are lots of validators in jquery.validate that have not been implemented or mapped to data annotations, so if these fulfil your need, then all you need to write in javascript is an adapter or even a call to a built-in adapter which can be as little as a single line. Take a look inside jquery.validate.js to find out what is available.
Using an existing jquery.validate.unobtrusive adapter
The job of the adapter is to read the HTML5 data-* attributes on your form element and convert this data into a form that can be understood by jquery.validate and your custom validation function. You are not required to do all the work yourself though and in many cases, you can call a built-in adapter. jquery.validate.unobtrusive declares three built-in adapters which can be used in the majority of situations. These are:
jQuery.validator.unobtrusive.adapters.addBool - used when your validator does not need any additional data.
jQuery.validator.unobtrusive.adapters.addSingleVal - used when your validator takes in one piece of additional data.
jQuery.validator.unobtrusive.adapters.addMinMax - used when your validator deals with minimum and maximum values such as range or string length.
If your validator does not fit into one of these categories, you are required to write your own adapter using the jQuery.validator.unobtrusive.adapters.add method. This is not as difficulty as it sounds and we'll see an example later in the article.
We use the addSingleVal method, passing in the name of the adapter and the name of the single value that we want to pass. Should the name of the validation function differ from the adapter, you can pass in a third parameter (ruleName):
jQuery.validator.unobtrusive.adapters.addSingleVal("notequalto", "otherproperty", "mynotequaltofunction");
At this point, our custom validator is complete.
For better understanding refer to the article itself which presents more description and a more complex example.
HTH.
I just did this and then used a regex expression:
$(document).ready(function () {
$.validator.methods.number = function (e) {
return true;
};
});
[RegularExpression(#"^[0-9\.]*$", ErrorMessage = "Invalid Amount")]
public decimal? Amount { get; set; }
Or you can simply do this.
#Html.ValidationMessageFor(m => m.PercentChange, "Custom Message: Input value must be a number"), new { #style = "display:none" })
Hope this helps.
I make this putting this on my view
#Html.DropDownListFor(m => m.BenefNamePos, Model.Options, new { onchange = "changePosition(this);", #class="form-control", data_val_number = "This is my custom message" })
I have this problem in KendoGrid, I use a script at the END of View to override data-val-number:
#(Html.Kendo().Grid<Test.ViewModel>(Model)
.Name("listado")
...
.Columns(columns =>
{
columns.Bound("idElementColumn").Filterable(false);
...
}
And at least, in the end of View I put:
<script type="text/javascript">
$("#listado").on("click", function (e) {
$(".k-grid #idElementColumn").attr('data-val-number', 'Ingrese un número.');
});
</script>
a simple method is, use dataanotation change message on ViewModel:
[Required(ErrorMessage ="الزامی")]
[StringLength(maximumLength:50,MinimumLength =2)]
[Display(Name = "نام")]
public string FirstName { get; set; }

Resources