Good day!
I've the following ViewModel class I use for login form:
using System.ComponentModel.DataAnnotations;
...
public class UserLogin : IDataErrorInfo
{
[Required]
[DisplayName("Login")]
public string Login { get; set; }
[Required]
[DisplayName("Password")]
public string Password { get; set; }
[DisplayName("Remember Me")]
public bool RememberMe { get; set; }
#region IDataErrorInfo Members
// This will be a Model-level error
public string Error
{
get
{
if (!WebUser.CanLogin(Login, Password))
{
return Resources.ValidationErrors.InvalidLoginPassword;
}
else
{
return String.Empty;
}
}
}
// All is handled by DataAnnotation attributes, just a stub for interface
public string this[string columnName]
{
get
{
return string.Empty;
}
}
#endregion
}
And this in Global.asax:
DefaultModelBinder.ResourceClassKey = "BinderMessages";
ValidationExtensions.ResourceClassKey = "BinderMessages";
The resource file BinderMessages.resx is placed inside App_GlobalResources it has two keys InvalidPropertyValue (which works) and PropertyValueRequired which doesn't and gives me default message.
Question: Is it possible to modify this message, or it's tied to DataAnnotations?
I've found many posts about this, but without solution. For now I just fallback to this:
[Required(ErrorMessageResourceType = typeof(Resources.ValidationErrors), ErrorMessageResourceName = "Required")]
You can create a custom ValidationAttribute that extends RequiredAttribute and sets the values there. Something like:
public class MyRequiredAttribute : RequiredAttribute
{
public MyRequiredAttribute()
{
ErrorMessageResourceType = typeof(Resources.ValidationErrors);
ErrorMessageResourceName = "Required";
}
}
Then decorate your Model with your custom attribute.
The default message is compiled into the DataAnnotations assembly in the resource file under System.ComponentModel.DataAnnotations.Resources.DataAnnotationsResources.resources and is RequiredAttribute_ValidationError=The {0} field is required.. So to answer your question, yes, that message is part of DataAnnotations.
Edit: PropertyValueRequired is used for errors on null values with non-nullable types. As mentioned below PropertyValueInvalid is used for type conversion errors.
I've done an approach using a singleton class to provide the translations. You still need to derive all attributes as suggested by #bmancini. The upside with my approach is that you can use multiple string tables (or switch translation source) without having to modify any other logic.
Since my blog entry is rather large, I'll just provide a link:
http://blog.gauffin.org/2010/11/simplified-localization-for-dataannotations/
Related
I am getting this error "Self referencing loop detected" while serializing using 'Json.NET'
I have a Book model
public class Book
{
public Book()
{
BookPersonMap = new List<BookPersonMap>();
}
public int BookId { get; set; }
public virtual ICollection<BookPersonMap> BookPersonMap { get; private set; }
(And many other virtual Icollections)
}
And this is the BookPerson Mapping class:
public class BookPersonMap
{
public int BookId { get; set; }
public string PersonName { get; set; }
public int PersonTypeId { get; set; }
public virtual Book Book { get; set; } // Foreign keys
public virtual PersonType PersonType { get; set; }
}
When I try to Serialize the Book object it throws:
"Self referencing loop detected for property 'Book' with type 'System.Data.Entity.DynamicProxies.Book_57F0FA206568374DD5A4CFF53C3B41CFDDC52DBBBA18007A896 08A96E7A783F8'. Path 'BookPersonMap[0]'."
I have tried the things suggested in some of the similar posts
Example:
PreserveReferencesHandling = PreserveReferencesHandling.Objects in Serializer settings returned a string with length 3 million!
ReferenceLoopHandling = ReferenceLoopHandling.Ignore in Serializer settings :
"An exception of type 'System.OutOfMemoryException' occurred in Newtonsoft.Json.dll but was not handled in user code"
^ Same luck with "ReferenceLoopHandling.Serialize"
MaxDepth = 1 : Infinite loop again.
Putting [JsonIgnore] on the virtual properties is working but it is a tedious task (because of numerous FK references) and not efficent, since if I miss one property and it will throw exception.
What is missing from above Json settings for them be not working?
services.AddMvc().AddJsonOptions(opt => {
opt.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});
I have found the best way to solve this type of error is to flatten your model using a view model.
Put a break point on your object before it is serialized and start drilling into the child properties. You will probably find that you can go on indefinitely.
This is what the serializer is choking on.
Create a Constructor for your controller and put on it this line of code :
db.Configuration.ProxyCreationEnabled = false;
//db is the instance of the context.
For asp.net mvc 5 use this
Add the code below to your Application_Start method inside globax.asax file or startup file.
protected void Application_Start()
{
..
GlobalConfiguration.Configuration.Formatters.JsonFormatter
.SerializerSettings
.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
}
Disable lazy loading and
ensure your controller does not return
Json(..obj)
rather it should return
Ok(..obj)
I have a problem with the validation message for Regular expression. Localised messages appear everywhere except for one field below:
[LocalizedDisplayName("LblWordCount", NameResourceType = typeof(ValidationMessages.Messages))]
[Required(ErrorMessageResourceName = "ErrorFieldRequired", ErrorMessageResourceType = typeof(ValidationMessages.Messages))]
[RegularExpression(#"^[0-9]+$", ErrorMessage = "", ErrorMessageResourceName = "ErrorDigitsOnly", ErrorMessageResourceType = typeof(ValidationMessages.Messages))]
public Int32 WordCount { get; set; }
It doesn't matter what I put in the resx file for "ErrorDigitsOnly" - the default message always kicks in: "The value 'zxzza1' is not valid for Word Count." For instance - message for the [Required] appears correctly.
Any suggestions for that?
Cheers,
303
I have checked the code for spelling mistakes but couldn't find any.
I actually ran across a similar situation myself.
Have you tried setting the wordcount validation in another class instead of using the regex pattern in the DataAnnotation Attribute?
For example -
public class EmailAttribute: RegularExpressionAttribute
{
public EmailAttribute() : base(#"[^#\.]+(\.[^#\.]+)*#[^#\.]+(\.[^#\.]+)?(\.[^#\.]{2,})")
{
}
public override string FormatErrorMessage(string name)
{
return Resources.Resources.emailValidation;
}
}
You can then use the attribute like so -
[CustomRequired]
[Email]
public string Email { get; set; }
The advantages of using it this way means that you get strong typing on your resources and it also allows you to write neater validation classes.
Hope it helps!
I'm quite new to asp.net mvc, and right know I'm trying to find out
a good practise to do input validation.
In the project we're going to use entity framework, where you can add
data annotations to properties in the following way:
[Required(ErrorMessage = "Please enter a product name")]
[Column]
public string Name { get; set; }
This is quite nice, however we have a multi language website (like most websites),
so we can't only show the error messages in English.
What can be a way to solve this? Can I change this errormessage #runtime, depending on the user's language?
Should I use Jquery client side validation?
Thanks for the input.
Update I've tried the code on the website of Phil Haack
This will do the trick with static resources however, we use resources that come from a database not static resources.
If I fill in the following for the dataannotations:
[MetadataType(typeof(IncidentsMetaData))]
public partial class INCIDENTS
{
private class IncidentsMetaData
{
[Required(ErrorMessageResourceType = typeof(CustomResourceProviders.DBResourceProviderFactory),
ErrorMessageResourceName="1277")]
public string SUBJECT { get; set; }
}
}
Then I get the following error:
The resource type 'CustomResourceProviders.DBResourceProviderFactory' does not have an accessible static property named '1277'.
Of course there is no such property, it should be accessed by a function.
Any idea what I could do about this?
tnx
You can inherit custom attribute from RequiredAttribute and set your own localized message for property ErrorMessage. It can looks like this:
public class LocalizedRequiredAttribute : RequiredAttribute
{
public LocalizedRequiredAttribute()
: base()
{
// prefix for the selection of localized messages from datebase
// e.x. for "Required" string, localized messages will be: "RuRequired", "EnRequired"
var currentCulture = CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
// logic to get value from datebase
// e.x. using Linq2Sql
using (var context = new dateBaseContext())
{
var query = (from x in context.LocalizedStrings
where x.NameKey == currentCulture + "Required"
select x.NameValue).SingleOrDefault();
if (query != null)
{
base.ErrorMessage = query;
}
else
{
base.ErrorMessage = "UndefinedName";
}
}
}
}
also and you inherit from DisplayNameAttribute and override DisplayName property:
public class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
public LocalizedDisplayNameAttribute(string displayNameKey)
: base(displayNameKey)
{
}
public override string DisplayName
{
get
{
if (!string.IsNullOrEmpty(base.DisplayName))
{
// prefix for the selection of localized messages from datebase
// e.x. if DisplayName is "Country", localized messages will be: "RuCountry", "EnCountry"
var currentCulture = CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
// logic to get value from datebase
// e.x. using Linq2Sql
using (var context = new dateBaseContext())
{
var query = (from x in context.DisplayNames
where x.DisplayNameKey == currentCulture + base.DisplayName
select x.DisplayNameValue).SingleOrDefault();
if (query != null)
{
return query;
}
return base.DisplayName;
}
}
return "UndefinedName";
}
}
}
also you can create your custom validation attributes that inherits from ValidationAttribute class.
Take a look at this post, http://helios.ca/2010/02/17/asp-net-mvc-2-model-validation-with-localization/ good blog on the problem
Phil Haack has written a good blog post that covers how to do this. Essentially it is much the same except you use resource files to provide the messages.
[Required(ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = Required")]
public string MyProperty{ get; set; }
In the world of MVC I have this view model...
public class MyViewModel{
[Required]
public string FirstName{ get; set; } }
...and this sort of thing in my view...
<%= Html.ValidationSummary("Please correct the errors and try again.") %>
<%= Html.TextBox("FirstName") %>
<%= Html.ValidationMessage("FirstName", "*") %>
My question: If I submit this form without supplying a name, I get the following message "The FirstName field is required"
OK. So, I go and change my property to...
[DisplayName("First Name")]
[Required]
public string FirstName{ get; set; }
..and now get "The First Name field is required"
All good so far.
So now I want the error message to display "First Name Blah Blah". How can I override the default message to display DisplayName + " Blah Blah", wihtout annotating all the properties with something like
[Required(ErrorMessage = "First Name Blah Blah")]
Cheers,
ETFairfax
public class GenericRequired: RequiredAttribute
{
public GenericRequired()
{
this.ErrorMessage = "{0} Blah blah";
}
}
It seems that RequiredAttribute doesn't implement IClientValidatable, so if you override the RequiredAttribute it breaks client side validation.
So this is what I did and it works. Hope this helps someone.
public class CustomRequiredAttribute : RequiredAttribute, IClientValidatable
{
public CustomRequiredAttribute()
{
this.ErrorMessage = "whatever your error message is";
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
yield return new ModelClientValidationRule
{
ErrorMessage = this.ErrorMessage,
ValidationType = "required"
};
}
}
Here is a way to do it without subclassing RequiredAttribute. Just make some attribute adapter classes. Here I'm using ErrorMessageResourceType/ErrorMessageResourceName (with a resource) but you could easily set ErrorMessage, or even check for the existence of overrides before setting these.
Global.asax.cs:
public class MvcApplication : HttpApplication {
protected void Application_Start() {
// other stuff here ...
DataAnnotationsModelValidatorProvider.RegisterAdapter(
typeof(RequiredAttribute), typeof(CustomRequiredAttributeAdapter));
DataAnnotationsModelValidatorProvider.RegisterAdapter(
typeof(StringLengthAttribute), typeof(CustomStringLengthAttributeAdapter));
}
}
private class CustomRequiredAttributeAdapter : RequiredAttributeAdapter {
public CustomRequiredAttributeAdapter(ModelMetadata metadata, ControllerContext context, RequiredAttribute attribute)
: base(metadata, context, attribute)
{
attribute.ErrorMessageResourceType = typeof(Resources);
attribute.ErrorMessageResourceName = "ValRequired";
}
}
private class CustomStringLengthAttributeAdapter : StringLengthAttributeAdapter {
public CustomStringLengthAttributeAdapter(ModelMetadata metadata, ControllerContext context, StringLengthAttribute attribute)
: base(metadata, context, attribute)
{
attribute.ErrorMessageResourceType = typeof(Resources);
attribute.ErrorMessageResourceName = "ValStringLength";
}
}
This example would have you create a resource file named Resources.resx with ValRequired as the new required default message and ValStringLength as the string length exceeded default message. Note that for both, {0} receives the name of the field, which you can set with [Display(Name = "Field Name")].
Just change
[Required]
to
[Required(ErrorMessage = "Your Message, Bla bla bla aaa!")]
Edit: I posted this because I was looking for this solution with [Required] attribute and had problem to find it. Creating new Q/A don't look that good, since this question already exists.
I was trying to solve the same thing and I found very simple solution in MVC 4.
First, you would probably have to store error messages in some place. I used custom resources, well described at http://afana.me/post/aspnet-mvc-internationalization.aspx.
Important is, that I can get any resource (even error message) by simply calling ResourcesProject.Resources.SomeCustomError or ResourcesProject.Resources.MainPageTitle etc. Everytime I try to access Resources class, it takes culture info from current thread and returns right language.
I have error message for field validation in ResourcesProject.Resources.RequiredAttribute. To set this message to appear in View, simply update [Required] attribute with these two parameters.
ErrorMessageResourceType - Type which will be called (in our example ResourcesProject.Resources)
ErrorMessageResourceName - Property, which will be called on the type above (In our case RequiredAttribute)
Here is a very simplified login model, which shows only username validation message. When the field is empty, it will take the string from ResourcesProject.Resources.RequiredAttribute and display this as an error.
public class LoginModel
{
[Required(ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName="RequiredAttribute")]
[Display(Name = "User name")]
public string UserName { get; set; }
}
You can write your own attribute:
public class MyRequiredAttribute : ValidationAttribute
{
MyRequiredAttribute() : base(() => "{0} blah blah blah blaaaaaah")
{
}
public override bool IsValid(object value)
{
if (value == null)
{
return false;
}
string str = value as string;
if (str != null)
{
return (str.Trim().Length != 0);
}
return true;
}
}
This is copy of RequiredAttribute from Reflector with changed error message.
using Resources;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Web;
using System.Web.Mvc;
public class CustomRequiredAttribute : RequiredAttribute, IClientValidatable
{
public CustomRequiredAttribute()
{
ErrorMessageResourceType = typeof(ValidationResource);
ErrorMessageResourceName = "RequiredErrorMessage";
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
yield return new ModelClientValidationRule
{
ErrorMessage = GetRequiredMessage(metadata.DisplayName),
ValidationType = "required"
};
}
private string GetRequiredMessage(string displayName) {
return displayName + " " + Resources.ValidationResource.RequiredErrorMessageClient;
}
}
App_GlobalResources/ValidationResource.resx
Now use data annotation
[CustomRequired]
This worked for me. Carefully read the comments inside the code. (Based on Chad's answer).
// Keep the name the same as the original, it helps trigger the original javascript
// function for client side validation.
public class RequiredAttribute : System.ComponentModel.DataAnnotations.RequiredAttribute, IClientValidatable
{
public RequiredAttribute()
{
this.ErrorMessage = "Message" // doesnt get called again. only once.
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
yield return new ModelClientValidationRule
{
ErrorMessage = "Message", // this gets called on every request, so it's contents can be dynamic.
ValidationType = "required"
};
}
}
There appears to be something of a hole in the way DataAnnotations works in that a user entering in some text into a field that will go into an int will never reach the DataAnnotations code. It kicks off a model binding error and displays the error to the user "The value 'a' is not valid for the XXXX field."
Anyway, it's all very nice that it automatically handles this situation, but I actually want to display an error message indicating the problem eg. "The value 'a' is not numeric. Please enter in a numeric value for the XXXX field".
I have tried the solutions set out How to replace the default ModelState error message in Asp.net MVC 2? and ASP.NET MVC - Custom validation message for value types, but I can't get them to work.
It appears that my resource file is not being read at all, since here (http://msdn.microsoft.com/en-us/library/system.web.mvc.defaultmodelbinder.resourceclasskey.aspx) it states "If the property is set to an invalid class key (such as a resource file that does not exist), MVC throws an exception." and even if I change the line to DefaultModelBinder.ResourceClassKey = "asdfasdhfk" there is no exception.
Anyone have any ideas?
EDIT: Here is some code. All of it is working minus my Messages.resx file's messages are not being used. The code for Messages.resx is auto generated so I won't include it.
So entering "a" into ProcessOrder results in a generic message rather than what I have entered into Messages.resx for PropertyValueInvalid (and InvalidPropertyValue for good measure).
Application_Start method
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ModelBinders.Binders.DefaultBinder = new Microsoft.Web.Mvc.DataAnnotations.DataAnnotationsModelBinder(); //set dataanooations to be used
DefaultModelBinder.ResourceClassKey = "Messages"; //set data annotations to look in messages.resx for the default messages
ValidationExtensions.ResourceClassKey = "Messages";
}
Entity Class
[MetadataType(typeof(GLMetaData))]
public partial class GL
{
}
public class GLMetaData
{
public int TransRefId { get; set; }
[DisplayName("Process Order")]
public int? ProcessOrder { get; set; }
[DisplayName("Trans Type")]
[StringLength(50)]
public string TransType { get; set; }
[StringLength(100)]
public string Description { get; set; }
[DisplayName("GL Code")]
[StringLength(20)]
public string GLCode { get; set; }
[DisplayName("Agents Credit No")]
[StringLength(50)]
public string AgentsCreditNo { get; set; }
[Required]
public bool Active { get; set; }
}
Controller Action:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(GL glToBeUpdated)
{
try
{
if (!ModelState.IsValid)
return View(glToBeUpdated);
//set auto properties
glToBeUpdated.UpdateDate = DateTime.Now;
glToBeUpdated.UpdateUser = this.CurrentUser;
glDataLayer.update(glToBeUpdated);
glDataLayer.submitChanges();
return RedirectToAction("Index");
}
catch
{
glDataLayer.abortChanges();
throw;
}
}
What I did to combat a similar issue was to clear the model state, validate against ModelState["XXXX"].Value.AttemptedValue instead of against the nulled value caused by an trying to put an invalid value into the Model's property, populating the error messages and resetting the Model values.
That way I can have the error messages I want and if necessary offer more than one ("a value is required" or "the value must be numeric").
I have battled this for most of the day on MVC4 RC. No matter what i set
DefaultModelBinder.ResourceClassKey
to it never seemed to work. It also never threw an exception when I assigned junk.
This is what I was using to assign the value (to no avail):
DefaultModelBinder.ResourceClassKey = typeof(App_GlobalResources.ValidationMessages).Name;
In the end I decided to tackle this error message on the client side and override the data attribute that jQuery uses to display the message.
#Html.TextBoxFor(m => m.Amount, new Dictionary<string,object>(){{"data-val-number","Invalid Number"}})
this is working how I need it to.
Ironically this works too:
#Html.TextBoxFor(m => m.Amount, new Dictionary<string, object>() {{ "data-val-number", HttpContext.GetGlobalResourceObject("ValidationMessages", "PropertyValueInvalid") } })
Here I have taken Contact number field as string but with Range Attribute so can provide numeric validatioin to use if from your Resource file .
[Required(ErrorMessageResourceType = typeof(Global), ErrorMessageResourceName = "ContactNumberRequired")]
[Range(0, int.MaxValue, ErrorMessageResourceType = typeof(Global), ErrorMessageResourceName = "ValidContactNumber")]
[Display(Name = "Contact Number")]
public string ContactNumber { get; set; }
So now here provided ErrorMessageResourceName as key . You can customize and use it also in Multi Language