8

I'm using System.ComponontModel.DataAnnotations to validate my model objects. How could I replace messages standard attributes (Required and StringLength) produce without providing ErrorMessage attribute to each of them or sub classing them?

Artem Tikhomirov
  • 21,497
  • 10
  • 48
  • 68

3 Answers3

8

Writing new post because I need more formatting than comments provide.

Look at ValidationAttribute - base class of validation attributes.

If validation error occured, error message will be created by method:

public virtual string FormatErrorMessage(string name)
{
    return string.Format(CultureInfo.CurrentCulture, this.ErrorMessageString, new object[] { name });
}

Next look at ErrorMessageString property:

protected string ErrorMessageString
{
    get
    {
        if (this._resourceModeAccessorIncomplete)
        {
            throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, DataAnnotationsResources.ValidationAttribute_NeedBothResourceTypeAndResourceName, new object[0]));
        }
        return this.ResourceAccessor();
    }
}

Property ResourceAccessor can be setted from:

ValidationAttribute..ctor(Func<String>)
ValidationAttribute.set_ErrorMessage(String) : Void
ValidationAttribute.SetResourceAccessorByPropertyLookup() : Void

First of it is exactly used by dervided classes to format messages, second - the case when we set error message trough ErrorMessage property, and third - when resource strings used. Depending on your situation, you may use ErrorMessageResourceName.

Elsewhere let's look at derived constructors, for our example, Range Attribute:

private RangeAttribute()
    : base((Func<string>) (() => DataAnnotationsResources.RangeAttribute_ValidationError))
{
}

Here RangeAttribute_ValidationError is loaded from resource:

internal static string RangeAttribute_ValidationError
{
    get
    {
        return ResourceManager.GetString("RangeAttribute_ValidationError", resourceCulture);
    }
}

So you can create resource file for different tan default culture and overwrite messages there, like this:

http://www.codeproject.com/KB/aspnet/SatelliteAssemblies.aspx

http://msdn.microsoft.com/en-us/library/aa645513(VS.71).aspx

friism
  • 19,068
  • 5
  • 80
  • 116
Vitaliy Ulantikov
  • 10,157
  • 3
  • 61
  • 54
7

You can use ErrorMessage property of base class ValidationAttribute for all DataAnnotations validators.

For example:

[Range(0, 100, ErrorMessage = "Value for {0} must be between {1} and {2}")]
public int id;

Maybe it'll help.

Vitaliy Ulantikov
  • 10,157
  • 3
  • 61
  • 54
0

For ASP.NET Core validation, refer to this page, where it explains how to set it up using the service engine:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc()
        .AddDataAnnotationsLocalization(options => {
            options.DataAnnotationLocalizerProvider = (type, factory) =>
                factory.Create(typeof(SharedResource));
        });
}

Otherwise (WPF, WinForms or .NET Framework), you can use reflection to interfere with the DataAnnotations resources and replace them with your own, which can then be automatically culture-dependent on your app's current culture. Refer to this answer for more:

void InitializeDataAnnotationsCulture()
{
  var sr = 
    typeof(ValidationAttribute)
      .Assembly
      .DefinedTypes
      //ensure class name according to current .NET you're using
      .Single(t => t.FullName == "System.SR");

  var resourceManager = 
    sr
      .DeclaredFields
      //ensure field name
      .Single(f => f.IsStatic && f.Name == "s_resourceManager");

  resourceManager
    .SetValue(null, 
      DataAnnotationsResources.ResourceManager, /* The generated RESX class in my proj */
      BindingFlags.NonPublic | BindingFlags.Static, null, null);

  var injected = resourceManager.GetValue(null) == DataAnnotationsResources.ResourceManager;
  Debug.Assert(injected);
}
Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632