Open sandboxFocusImprove this doc

LA0073: Copied Custom Attribute Already Exists on the Target

Severity Error
Reported by PostSharp
Message Cannot copy custom attribute "<AttributeType>" to "<Target>" because "<Target>" already has a custom attribute of the same type. Set the OverrideAction property of [CopyCustomAttributes] to ignore, add or merge the attribute instead.

Cause

An aspect member annotated with IntroduceMemberAttribute and CopyCustomAttributesAttribute copies the custom attributes of the given types from the aspect member to the introduced member. When the target type already defines the member and the introduction overrides it, for example with OverrideOrFail, PostSharp copies the attributes to the existing member, which can already have an attribute of the same type.

PostSharp then follows the OverrideAction of the copy. Its default value, Fail, reports LA0073 instead of adding a second attribute, which most attribute types do not allow, or replacing the attribute that you wrote.

<Target> is the member that already has the attribute. PostSharp reports the error on this member.

In the following example, the [Auditable] aspect introduces a ModifiedBy property with a [Description] attribute, and overrides the ModifiedBy property of Customer, which has its own [Description] attribute.

using System;
using System.ComponentModel;
using System.Reflection;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Serialization;

namespace Errors.LA0073_Error;

[PSerializable]
public class AuditableAttribute : InstanceLevelAspect
{
    [IntroduceMember( OverrideAction = MemberOverrideAction.OverrideOrFail )]
    [CopyCustomAttributes( typeof(DescriptionAttribute) )]
    [Description( "User who last modified the object" )]
    public string? ModifiedBy { get; set; }
}

[Auditable]
public class Customer
{
    // The property already has a [Description] attribute, so the aspect cannot copy its own.
    [Description( "Account manager who last modified the customer" )]
    public string? ModifiedBy { get; set; }
}

[Auditable]
public class Supplier
{
}

public static class Program
{
    public static void Main()
    {
        foreach ( var type in new[] { typeof(Customer), typeof(Supplier) } )
        {
            var property = type.GetProperty( "ModifiedBy" )!;
            Console.WriteLine( $"{type.Name}.ModifiedBy: {property.GetCustomAttribute<DescriptionAttribute>()!.Description}" );
        }
    }
}
error LA0073: LA0073_Error.cs(24,20): Cannot copy custom attribute "System.ComponentModel.DescriptionAttribute" to "Errors.LA0073_Error.Customer.ModifiedBy" because "Errors.LA0073_Error.Customer.ModifiedBy" already has a custom attribute of the same type. Set the OverrideAction property of [CopyCustomAttributes] to ignore, add or merge the attribute instead.

Resolution

Choose what PostSharp does when the attribute already exists, with the OverrideAction property of CopyCustomAttributesAttribute:

  • Ignore keeps the existing attribute and does not copy the attribute of the aspect.
  • MergeAddProperty adds the named properties and fields of the copied attribute to the existing one, and keeps those that the existing attribute already sets.
  • MergeReplaceProperty also merges the two attributes, but the values of the copied attribute replace those of the existing one. Both merge actions ignore the constructor arguments of the copied attribute.
  • Add adds the copied attribute as a second one. Use it only for an attribute type that allows multiple instances.

Otherwise, remove the attribute from the existing member, or exclude its type from the types that the aspect copies.

In the following example, Customer.ModifiedBy keeps its own description, and the property introduced into Supplier receives the description of the aspect.

using System;
using System.ComponentModel;
using System.Reflection;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Serialization;

namespace Errors.LA0073_Fixed;

[PSerializable]
public class AuditableAttribute : InstanceLevelAspect
{
    [IntroduceMember( OverrideAction = MemberOverrideAction.OverrideOrFail )]
    // The attribute is not copied to a property that already has one.
    [CopyCustomAttributes( typeof(DescriptionAttribute), OverrideAction = CustomAttributeOverrideAction.Ignore )]
    [Description( "User who last modified the object" )]
    public string? ModifiedBy { get; set; }
}

[Auditable]
public class Customer
{
    [Description( "Account manager who last modified the customer" )]
    public string? ModifiedBy { get; set; }
}

[Auditable]
public class Supplier
{
}

public static class Program
{
    public static void Main()
    {
        foreach ( var type in new[] { typeof(Customer), typeof(Supplier) } )
        {
            var property = type.GetProperty( "ModifiedBy" )!;
            Console.WriteLine( $"{type.Name}.ModifiedBy: {property.GetCustomAttribute<DescriptionAttribute>()!.Description}" );
        }
    }
}
Customer.ModifiedBy: Account manager who last modified the customer
Supplier.ModifiedBy: User who last modified the object

See Also

Reference

CopyCustomAttributesAttribute
CustomAttributeOverrideAction
IntroduceMemberAttribute

Other Resources

Introducing Interfaces, Methods, Properties and Events into Existing Classes