Search code examples
c#custom-attributesmetadatatype

C# Custom Attributes not being applied


I'm trying to apply attributes to fields using the MetadataType attribute class. I haven't been able to apply a custom attribute to a field in a partial class. Some of the examples I have been following are here and here.

My end game is to try and flag all the fields in a class that I am required to "do some work with".

In the example below, I want the field "Name" to have the FooAttribute applied. In real life, I'm dealing with generated code....

In my very contrived example, I have a partial class - Cow, which is generated code;

namespace Models
{
    public partial class Cow
    {
        public string Name;
        public string Colour;
    }
}

I need the Name field to use my FooAttribute, so I have done this;

using System;
using System.ComponentModel.DataAnnotations;

namespace Models
{
    public class FooAttribute : Attribute { }

    public class CowMetaData
    {
        [Foo]
        public string Name;
    }

    [MetadataType(typeof(CowMetaData))]
    public partial class Cow
    {
        [Foo]
        public int Weight;

        public string NoAttributeHere;
    }

}

This works great for the Weight field, which has the FooAttribute applied - but I would expect that because it is in the partial class. The Name field does not pick up the attribute from the metadata, and this is what I really need.

What am I missing, or have I got it all wrong?

Update: This is how I am searching for fields with the FooAttribute ;

public static void ShowAllFieldsWithFooAttribute(Cow cow)
{
    var myFields = cow.GetType().GetFields().ToList();
    foreach (var f in myFields)
    {
        if (Attribute.IsDefined(f, typeof(FooAttribute)))
        {
            Console.WriteLine("{0}", f.Name);
        }
    }
}

The result of this is:
Weight

But I am Expecting:
Name
Weight


Solution

  • Attributes are part of metadata and they are not affects the compiled results. Setting MetadataType attribute to the class doesn't spread all metadata to the properties/fields of the class. So you have to read the MetadataType attribute in code and use metadata from type defined in MetadataType attribute instead of initial class (or together as it is in your case)

    Check the sample:

        var fooFields = new Dictionary<FieldInfo, FooAttribute>();
    
        var cowType = typeof (Cow);
        var metadataType = cowType.GetCustomAttribute<MetadataTypeAttribute>();
        var metaFields = metadataType?.MetadataClassType.GetFields() ?? new FieldInfo[0];
    
        foreach (var fieldInfo in cowType.GetFields())
        {
            var metaField = metaFields.FirstOrDefault(f => f.Name == fieldInfo.Name);
            var foo = metaField?.GetCustomAttribute<FooAttribute>() 
                               ?? fieldInfo.GetCustomAttribute<FooAttribute>();
            if (foo != null)
            {
                fooFields[fieldInfo] = foo;
            }
        }