asp.net,mvc,localization,using,database

Mert 8/7/2016 0

ASP.NET MVC Localization using a Database ASP.NET MVC already offers support for localization through ressource files. However, if you need to read texts from a database things get more complicated. Especially if you still want to use DataAnnotations in your model for display text and validation messages.

C#
 public class LoginModel
{
    [Required(ErrorMessage = "User name is required!")]
    [Display(Name = "User name")]
    public string UserName { get; set; }

    [Required(ErrorMessage = "Passwort is required!")]
    [Display(Name = "Password")]
    public string Password { get; set; }
} 
CsHtml
@Html.LabelFor(m => m.UserName)
@Html.TextBoxFor(m => m.UserName)
@Html.ValidationMessageFor(m => m.UserName) 
C#
 [Required(ErrorMessage = "27")]
[Display(Name = "42")]
public string UserName { get; set; }
To replace the text-id of the Display attribute with the text from the database, we need to create our own MetadataProvider:

Hide   Copy Code
public class MetadataProvider : AssociatedMetadataProvider
{
    protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes,
        Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
    {
        var metadata = new ModelMetadata(this, containerType, modelAccessor, modelType, propertyName);
        if (propertyName != null)
        {
            var displayAttribute = attributes.OfType<DisplayAttribute>().FirstOrDefault();
            if (displayAttribute != null)
            {
                int textId;
                if (Int32.TryParse(displayAttribute.Name, out textId))
                {
                    // TODO: get text from database
                    metadata.DisplayName = "DB Text with id "   textId;
                }
            }
        }
        return metadata;
    }
}
This class must then be registered in the Application_Start() method of Global.asax.cs:

Hide   Copy Code
ModelMetadataProviders.Current = new MetadataProvider();
For the validation attributes it is a bit more complicated. The first class we need is a ValidatorProvider, which tells the MVC validation system which class to use to perform model validation. Our implementation in the class LocalizableModelValidatorProvider returns an instance of LocalizableModelValidator.

Hide   Copy Code
public class LocalizableModelValidatorProvider : DataAnnotationsModelValidatorProvider
{
    protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
    {
        var validators = base.GetValidators(metadata, context, attributes);
        return validators.Select(validator => new LocalizableModelValidator(validator, metadata, context)).ToList();
    }
}
Our validation provider also needs to be registered in the Application_Start() method of Global.asax.cs:

Hide   Copy Code
var provider = ModelValidatorProviders.Providers.FirstOrDefault(p => p.GetType() == typeof(DataAnnotationsModelValidatorProvider));
if (provider != null)
{
    ModelValidatorProviders.Providers.Remove(provider);
}
ModelValidatorProviders.Providers.Add(new LocalizableModelValidatorProvider());
As you can see, we remove the existing validator provider that is of type DataAnnotationsModelValidatorProvider and replace it with our own implementation in the LocalizableModelValidatorProvider class.

The second class we need is the actual ModelValidatorProvider, which we implement in the class LocalizableModelValidatorProvider:

Hide   Shrink    Copy Code
public class LocalizableModelValidator : ModelValidator
{
    private readonly ModelValidator innerValidator;

    public LocalizableModelValidator(ModelValidator innerValidator, ModelMetadata metadata, ControllerContext controllerContext)
        : base(metadata, controllerContext)
    {
        this.innerValidator = innerValidator;
    }

    public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
    {
        var rules = innerValidator.GetClientValidationRules();
        var modelClientValidationRules = rules as ModelClientValidationRule[] ?? rules.ToArray();
        foreach (var rule in modelClientValidationRules)
        {
            int textId;
            if (Int32.TryParse(rule.ErrorMessage, out textId))
            {
                // TODO: read text from database
                rule.ErrorMessage = "DB_Text_"   textId;
            }
        }
        return modelClientValidationRules;
    }

    public override IEnumerable<ModelValidationResult> Validate(object container)
    {
        // execute the inner validation which doesn't have localization
        var results = innerValidator.Validate(container);
        // convert the error message (text id) to the localized value
        return results.Select(result =>
        {
            int textId;
            if (Int32.TryParse(result.Message, out textId))
            {
                // TODO: read text from database
                result.Message = "DB text with id "   textId;
            }
            return new ModelValidationResult() { Message = result.Message };
        });
    }
} 

Report Bug

Please Login to Report Bug

Reported Bugs

Comments

Please Login to Comment

Comments