📜 ⬆️ ⬇️

Overload standard DataAnnotation attributes for use with custom resource provider

Imagine that you have a legacy project Asp.NET MVC version 5, which for many years. It uses samopisny ResourceProvider, which can get a resource from the database and show it on the UI. Depending on various conditions (for example, from where the user came to the site), resources will be shown different. Now it's time to make all tightly hardcoded strings in the Data Annotation attributes, such as:
[Display(Name = "Username")] [Required(ErrorMessage = "Please enter the username")] [StringLength(64, ErrorMessage = "Username cannot exceed 64 characters")] public string Username{ get; set; } 

could also get their values ​​using ResourceProvider. How to do this using a bit of inheritance and customization available in Asp.NET MVC, I will show under the cat.

All the necessary logic to find exactly the resource that needs to be shown in the current situation is encapsulated in the ResourceProvider class and related classes, so we just need to call its following method:
 string resource = ResourceProvider.GetResource(name); 


Display attribute


The DisplayAttribute , which appeared in the .NET Framework 4, unlike its predecessor, DisplayNameAttribute , is marked as sealed , so we cannot use the magic of inheritance in this case. Here we take advantage of the fact that the DisplayName is also available in the System.Web.Mvc.ModelMetadata class. Create a class that inherits from DataAnnotationsModelMetadataProvider , override the CreateMetadata method to replace the DisplayName with that obtained from the ResourceProvider . As an IoC container we use Ninject. To set the name of the resource, create your own DisplayResourceAwareAttribute attribute.
 public class DisplayResourceAwareAttribute : Attribute { public string ResourceName { get; set; } public DisplayResourceAwareAttribute() { } } public class ResourceAwareMetadataProvider : DataAnnotationsModelMetadataProvider { [Inject] public ResourceProvider ResourceProvider { get; set; } protected override ModelMetadata CreateMetadata( IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) { var modelMetadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName); var displayNameAttr = attributes.SingleOrDefault(a => typeof(DisplayResourceAwareAttribute) == a.GetType()) as DisplayResourceAwareAttribute; if (displayNameAttr != null) { modelMetadata.DisplayName = ResourceProvider.GetResource(displayNameAttr.ResourceName); } return modelMetadata; } } 

The above code looks for an attribute of the DisplayResourceAwareAttribute type among the attributes, and if there is one, we will update the DisplayName property with the value obtained using the ResourceProvider.GetResource. Now we have to use the new MetadataProvider instead of the standard one. To do this, in the Application_Start method in the Global.asax.cs file, add the following line:
 protected void Application_Start() { ... ModelMetadataProviders.Current = DependencyResolver.Current.GetService<ResourceAwareMetadataProvider>(); ... } 

Now, in the model, we can eliminate the explicit description of the field by replacing the standard attribute with ours:
 [DisplayResourceAware(ResourceName = "UsernameResource")] [Required(ErrorMessage = "Please enter the username")] [StringLength(64, ErrorMessage = "Username cannot exceed 64 characters")] public string Username{ get; set; } 

No additional changes in views, controllers are required.

Validation Attributes


With validation attributes such as Required , StringLength , etc. simpler, because we can inherit our attributes from them and in the constructor set the ErrorMessage property to the value we need.

')

Source: https://habr.com/ru/post/281222/


All Articles