Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ignore / Override AttributeUsage Restrictions

I need to be able to apply DisplayAttribute to classes but its AttributeUsage doesn't allow that in the current .NET / .NET Core release. It looks like this has been remedied for .NET Core vNext, but if there's some workaround to be able to somehow ignore or override this restriction until this change makes its way into a .NET release that would be extremely helpful. The only option I can see is reimplementing the whole thing (including localization) but I don't really want to have to support and test that just to deprecate it as soon as .NET vNext comes out.

Any clever ideas/hacks?

Do AttributeUsage restrictions get verified at runtime by the CLR or are they just compile time restrictions? If they are only compile time checked then is there a clever way to change the metadata used by the compiler to "trick" it into allowing the usage or somehow modifying the system assembly so my dev machines allows the usage?

*I can't seem to edit the bounty description so just to clarify, solution for the bounty has to work for the .NET Framework, bonus points for .NET Core as well.

like image 303
Mike Marynowski Avatar asked Jun 29 '18 06:06

Mike Marynowski


1 Answers

While you should not change an existing .NET Assembly - due to signing and the GAC (trouble awaits) it is possible to add the attribute onto an existing class after compiling and it works without problems. The AttributeUsage does not seem to be enforced at runtime.

So I created a little Fody add-in that rewrites a certain attribute to the DisplayAttribute:

First our little dummy attribute that will be rewritten through Fody:

[AttributeUsage (AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Class)]
public class DisplayPatchAttribute : Attribute
{
  public DisplayPatchAttribute()
  {
  }
}

And a little dummy program that tests whether the DisplayAttribute is applied to a test class. When ran without the Fody-addin it will always print "no" (Note that the test class uses our dummy attribute instead of the real one):

internal static class Program
{
  private static void Main (string[] args)
  {
    var attr = Attribute.GetCustomAttribute (typeof(Test), typeof(DisplayAttribute)) as DisplayAttribute;
    Console.WriteLine (attr == null ? "no" : "yes");
  }
}

[DisplayPatch]
internal class Test
{
}

And now we add a little Fody weaver that rewrites the attribute to the real one (hacky code incoming):

public class DisplayAttributeWeaver : BaseModuleWeaver
{
  public override void Execute()
  {
    var dataAnnotationAssembly = ModuleDefinition.AssemblyReferences.First (e => e.Name.Contains ("DataAnnotation"));
    var resolvedDataAnnotationAssembly = ModuleDefinition.AssemblyResolver.Resolve (dataAnnotationAssembly);
    var displayAttribute = resolvedDataAnnotationAssembly.Modules.First().GetType ("System.ComponentModel.DataAnnotations.DisplayAttribute");
    var displayAttributeConstructor = ModuleDefinition.ImportReference(displayAttribute.GetConstructors().First());

    foreach (var type in ModuleDefinition.Types)
    {
      var targetAttribute = type.CustomAttributes.FirstOrDefault (e => e.AttributeType.Name == "DisplayPatchAttribute");
      if (targetAttribute == null)
        continue;

      type.CustomAttributes.Remove (targetAttribute);

      var newAttr = new CustomAttribute (displayAttributeConstructor);
      type.CustomAttributes.Add (newAttr);
    }
  }

  public override IEnumerable<string> GetAssembliesForScanning()
  {
    yield return "mscorlib";
    yield return "System";
  }
}

It converts the DisplayPatchAttribute into a DisplayAttribute and so the program outputs "yes".

The DisplayPatchAttribute would then look like the normal DisplayAttribute and have its properties copied over to the new attribute.

Not tested for .NET Core but as Fody supports net core and the fix is on IL level it should work without problems.

like image 92
a-ctor Avatar answered Nov 20 '22 14:11

a-ctor