DataTypeAnnotations MetadataType not working as expected

525 views Asked by At

I try to implement Metadatatype, in order to seperate Validation attributes from my Acquisitiecode class, into the AcquisitiecodeAnnotations class.

Now when I add attributes (like Required, StringLength and so on) to the Acquisitiecode class, validation works as expected. When I move these attributes to the AcquisitiecodeAnnotations class and bind this class using the MetadataType attribute, I does not work.

Please find the code examples below (I've stripped them down for readability). Also, the project is an ASP.NET Core 3.0 web application. All code, including the examples are also running in.NET Core 3.0 projects.

Snippet 1:

using System;
using System.ComponentModel.DataAnnotations;

namespace Shared.Entities
{
    [MetadataType(typeof(AcquisitiecodeAnnotations))]
    public partial class Acquisitiecode
    { }

    public partial class AcquisitiecodeAnnotations
    {
        [StringLength(4, ErrorMessage = "The value cannot exceed 4 characters. ")]
        public string Acquisitiecode1 { get; set; }
    }
}

Snippet 2:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace Shared.Entities
{
    public partial class Acquisitiecode
    {
        public Acquisitiecode()
        {
            Lidmaatschap = new HashSet<Lidmaatschap>();
        }

        public string Acquisitiecode1 { get; set; }

        public virtual Lid Lid { get; set; }
        public virtual ICollection<Lidmaatschap> Lidmaatschap { get; set; }
    }
}
2

There are 2 answers

0
nAviD On

As of October 2020 the current version of Blazor does not support Metadatatype. For more information please read this issue.

0
Peter Morris On

I've just written a workaround for this.

You can add this helper class to your Blazor project and call MetadataValidationHelper.Scan at app start (and also if you dynamically load assemblies at runtime).

public static class MetadataValidationHelper
{
    private readonly static ConcurrentDictionary<Assembly, bool> ScannedAssemblies = new();

    public static void Scan(Assembly assembly, params Assembly[] additionalAssemblies)
    {
        ArgumentNullException.ThrowIfNull(assembly);
        Assembly[] allAssemblies = (additionalAssemblies ?? Array.Empty<Assembly>()).Append(assembly).ToArray();
        foreach (var currentAssembly in allAssemblies)
        {
            ScannedAssemblies.GetOrAdd(
                key: assembly,
                valueFactory: assembly =>
                {
                    ScanAssembly(assembly);
                    return true;
                });
        }
    }

    private static void ScanAssembly(Assembly assembly)
    {
        foreach(Type currentClass in assembly.GetTypes().Where(x => x.IsClass))
        {
            var metaAttributes = currentClass.GetCustomAttributes<MetadataTypeAttribute>(inherit: false);
            foreach (var metaAttribute in metaAttributes)
                RegisterMetadataClass(currentClass, metaAttribute);
        }
    }

    private static void RegisterMetadataClass(Type currentClass, MetadataTypeAttribute metaAttribute)
    {
        TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(currentClass, metaAttribute.MetadataClassType), currentClass);
    }
}