2

I found the following question/answer in my search for more information on data validation attributes and the ErrorMessageResourceName and ErrorMessageResourceType:

How to use DataAnnotations ErrorMessageResourceName with custom Resource Solution

Unfortunately it does not have the information I need. In the answer, a presumed class of CustomResourceManager is mentioned:

[Required(
    ErrorMessageResourceType = typeof(CustomResourceManager), 
    ErrorMessageResourceName = "ResourceKey")]
public string Username { get; set; }

But I cannot find the documentation on what this class would look like, what interface it implements, etc. I need to provide custom error messages for validation failures, but I cannot find the documentation I need to do so.

Community
  • 1
  • 1
Peter Howe
  • 429
  • 6
  • 19
  • Old and such, but here is how it was done in the [ValidationAttribute](https://github.com/Microsoft/referencesource/blob/master/System.ComponentModel.DataAnnotations/DataAnnotations/ValidationAttribute.cs) – Artur Jul 03 '20 at 17:48

1 Answers1

3

I had the same problem today, as I tried to make globalization happen for a wpf app with entity framework and validation attributes.

I tried different solutions and, for now, I ended up creating that CustomResourceManager based on the sample that Dave Sexton describes in this post: http://www.pcreview.co.uk/forums/load-resources-string-table-database-t2892227.html

Basically I have implemented my DatabaseResourceManager and DatabaseResourceSet derived from ResourceManager and ResourceSet respectively like so:

The DatabaseResourceManager Implementation:

public class DatabaseResourceManager : ResourceManager
{
    #region Singleton pattern http://msdn.microsoft.com/en-us/library/ff650316.aspx
    private static volatile DatabaseResourceManager instance;
    private static object syncRoot = new Object();

    private DatabaseResourceManager() : base() { }

    public static DatabaseResourceManager Instance
    {
        get
        {
            if (instance == null)
            {
                lock (syncRoot)
                {
                    if (instance == null)
                        instance = new DatabaseResourceManager();
                }
            }

            return instance;
        }
    }
    #endregion

    protected override ResourceSet InternalGetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents)
    {
        if (culture == null)
            culture = CultureInfo.InvariantCulture;

        return new DatabaseResourceSet(culture);
    }
}

And the DatabaseResourceSet implementation:

public class DatabaseResourceSet : ResourceSet
{
    private readonly CultureInfo culture;
    private static readonly Dictionary<string, Hashtable> cachedResources = new Dictionary<string, Hashtable>();

    public DatabaseResourceSet(CultureInfo culture)
    {
        if (culture == null)
            throw new ArgumentNullException("culture");

        this.culture = culture;

        ReadResources();
    }

    protected override void ReadResources()
    {
        if (cachedResources.ContainsKey(culture.Name))
        // retrieve cached resource set
        {
            Table = cachedResources[culture.Name];
            return;
        }

        using (MyDatabaseContext db = new MyDatabaseContext())
        {
            var translations = from t in db.Translations
                       where t.CultureIso == culture.Name
                       select t;
            foreach (var translation in translations)
            {
                Table.Add(translation.Chave, translation.Valor);
            }
        }

        cachedResources[culture.Name] = Table;
    }
}

Since my DataBaseResourceManager has the singletone pattern implemented I can easily access it and get the data like so:

public class LocalizedRequiredAttribute : RequiredAttribute
{
    public LocalizedRequiredAttribute() : base()
    {
    }

    public override string FormatErrorMessage(string name)
    {
        string localErrorMessage = DatabaseResourceManager.Instance.GetString(this.ErrorMessageResourceName) ?? ErrorMessage ?? "{0} is required"; //probably DataAnnotationsResources.RequiredAttribute_ValidationError would is a better option
        return string.Format(System.Globalization.CultureInfo.CurrentCulture, localErrorMessage, new object[] { name });
    }
}

Now the drawbacks:

Like said in the pcreview post, winforms doesn't accept this DataBaseResourceManager, but in my case since I'm using wpf I didn't have this problem (but had another small memory problem because of the duplication of the ResourceManager and a DictionaryResource) . Don't know if you can inject the resource manager in the winforms, but that goes beyond your question. Another drawback, that you already know, is that we must derived all ValidationAttribute used so that the FormatErrorMessage is used. StringLengthAttribute, RequiredAttribute, RegularExpressionAttribute, and so on. Which is prone to errors and clumsy. Unfortunately, Microsoft didn't gave us much room here.

Hope I helped, probably I'll review this answer in the future to share my findings on this.

Regards

saamorim
  • 3,855
  • 17
  • 22