¿Cómo inyectar un atributo usando un atributo PostSharp?

¿Cómo puedo escribir un aspecto PostSharp para aplicar un atributo a una clase? El escenario que estoy considerando es una entidad WCF (u objeto de dominio) que debe decorarse con el atributo DataContract . También debería tener una propiedad de Namespace . Me gusta esto:

 using System.Runtime.Serialization; namespace MWS.Contracts.Search.V1 { namespace Domain { [DataContract(Namespace = XmlNamespaces.SchemaNamespace)] public class PagingContext { [DataMember] public int Page { get; set; } [DataMember] public int ResultsPerPage { get; set; } [DataMember] public int MaxResults { get; set; } } } } 

En el ejemplo anterior, puede ver cómo quiero que se vea la salida. Tiene el atributo DataContract aplicado a la clase. Hacer esto a mano es tedioso y no único. Realmente me gustaría escribir un solo aspecto que se pueda aplicar a mi espacio de nombres “Dominio”. A continuación, aplicaría los atributos relacionados con la serialización para mí. De esta manera, puedo centrarme en desarrollar objetos de entidades y no preocuparme por los detalles de la serialización.

He encontrado documentación en el sitio web de PostSharp para inyectar código antes, después y en lugar de métodos. Sin embargo, lo que estoy buscando es una forma de inyectar un atributo en un tipo.


¡Aquí está la solución!

 using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.Serialization; using PostSharp.Aspects; using PostSharp.Extensibility; using PostSharp.Reflection; namespace MWS.Contracts.Aspects { // We set up multicast inheritance so the aspect is automatically added to children types. [MulticastAttributeUsage(MulticastTargets.Class, Inheritance = MulticastInheritance.Strict)] [Serializable] public sealed class AutoDataContractAttribute : TypeLevelAspect, IAspectProvider { private readonly string xmlNamespace; public AutoDataContractAttribute(string xmlNamespace) { this.xmlNamespace = xmlNamespace; } // This method is called at build time and should just provide other aspects. public IEnumerable ProvideAspects(object targetElement) { var targetType = (Type) targetElement; var introduceDataContractAspect = new CustomAttributeIntroductionAspect( new ObjectConstruction(typeof (DataContractAttribute).GetConstructor(Type.EmptyTypes))); introduceDataContractAspect.CustomAttribute.NamedArguments.Add("Namespace", xmlNamespace); var introduceDataMemberAspect = new CustomAttributeIntroductionAspect( new ObjectConstruction(typeof (DataMemberAttribute).GetConstructor(Type.EmptyTypes))); // Add the DataContract attribute to the type. yield return new AspectInstance(targetType, introduceDataContractAspect); // Add a DataMember attribute to every relevant property.))) foreach (var property in targetType.GetProperties(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance) .Where(property => property.CanWrite && !property.IsDefined(typeof (NotDataMemberAttribute), false))) yield return new AspectInstance(property, introduceDataMemberAspect); } } [AttributeUsage(AttributeTargets.Property)] public sealed class NotDataMemberAttribute : Attribute { } } 

Ver http://www.sharpcrafters.com/blog/post/PostSharp-Principals-Day-12-e28093-Aspect-Providers-e28093-Part-1.aspx

Aquí hay un ejemplo de trabajo. Al aplicar este aspecto a una clase, se aplicará el atributo XmlIgnore a cualquier propiedad pública que aún no tenga aplicado XmlElement o XmlAttribute. el truco es usar CustomAttributeIntroductioinAspect que está incorporado a Postsharp. Solo necesita crear una instancia de una instancia que especifique el tipo de atributo y los detalles del contructor, luego cree un proveedor para aplicarlo al objective (s).

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using PostSharp.Extensibility; using PostSharp.Aspects; using PostSharp.Reflection; using System.Xml.Serialization; namespace ApplyingAttributes { [MulticastAttributeUsage(MulticastTargets.Field | MulticastTargets.Property, TargetMemberAttributes = MulticastAttributes.Public | MulticastAttributes.Instance)] public sealed class AddXmlIgnoreAttribute : LocationLevelAspect, IAspectProvider { private static readonly CustomAttributeIntroductionAspect customAttributeIntroductionAspect = new CustomAttributeIntroductionAspect( new ObjectConstruction(typeof(XmlIgnoreAttribute).GetConstructor(Type.EmptyTypes))); public IEnumerable ProvideAspects(object targetElement) { LocationInfo memberInfo = (LocationInfo)targetElement; if (memberInfo.PropertyInfo.IsDefined(typeof(XmlElementAttribute), false) || memberInfo.PropertyInfo.IsDefined(typeof(XmlAttributeAttribute), false)) yield break; yield return new AspectInstance(memberInfo.PropertyInfo, customAttributeIntroductionAspect); } } } 

Para usar atributos, especificar parámetros, uso

  public class MyAspect : TypeLevelAspect, IAspectProvider { public IEnumerable ProvideAspects(object targetElement) { yield return Create(mi, "Value"); } private AspectInstance Create(T target, string newName) { var x = new CustomAttributeIntroductionAspect( new ObjectConstruction(typeof(NewMethodName).GetConstructor(new Type[] { typeof(string) }), new object[] { newName }) ); return new AspectInstance(target, x); } }