#c# #.net #postsharp #aop
#c# #.net #postsharp #aop
Вопрос:
Как я могу написать аспект PostSharp, чтобы применить атрибут к классу? Сценарий, который я рассматриваю, — это объект WCF (или объект домена), который необходимо украсить DataContract
атрибутом. У него также должно быть Namespace
свойство. Вот так:
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; }
}
}
}
В приведенном выше примере вы можете видеть, как я хочу, чтобы результат выглядел. К классу применяется атрибут DataContract. Выполнение этого вручную утомительно и не уникально. Я бы просто хотел написать один аспект, который можно применить к моему пространству имен «Домен». Затем он применит атрибуты, связанные с сериализацией, для меня. Таким образом, я могу просто сосредоточиться на разработке объектов entity и не беспокоиться о деталях сериализации.
Я нашел документацию на веб-сайте PostSharp для внедрения кода до, после и вместо методов. Однако то, что я ищу, — это способ ввести атрибут в тип.
Вот решение!
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<AspectInstance> 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 amp;amp;
!property.IsDefined(typeof (NotDataMemberAttribute), false)))
yield return new AspectInstance(property, introduceDataMemberAspect);
}
}
[AttributeUsage(AttributeTargets.Property)]
public sealed class NotDataMemberAttribute : Attribute
{
}
}
Комментарии:
1. Нашел решение здесь: doc.sharpcrafters.com/postsharp-2.1 /…
2. CopyCustomAttribute не выполняет то, что вы хотите. Он применяет пользовательские атрибуты только к элементам, которые вы вводите в целевой тип. Он не будет применять атрибуты к существующим членам.
3. @DustinDavis Я удалил ссылку на CopyCustomAttribute, чтобы мы никого не путали.
4. Обязательно ли это делать с помощью PostSharp? это просто сделать с помощью Mono Cecil.
Ответ №1:
Вот рабочий пример. Применение этого аспекта к классу приведет к применению атрибута XmlIgnore к любому общедоступному свойству, к которому еще не применен XmlElement или XmlAttribute. хитрость заключается в использовании CustomAttributeIntroductioinAspect, который встроен в Postsharp. Вам просто нужно создать экземпляр экземпляра, указав тип атрибута и детали конструктора, а затем создать поставщика, чтобы применить его к цели (целям).
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<AspectInstance> 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);
}
}
}
Чтобы использовать атрибуты, указывая параметры, я использую
public class MyAspect : TypeLevelAspect, IAspectProvider
{
public IEnumerable<AspectInstance> ProvideAspects(object targetElement)
{
yield return Create<MethodInfo>(mi, "Value");
}
private AspectInstance Create<T>(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);
}
}
Комментарии:
1. Спасибо за пример. Как мне задать свойства для CustomAttributeIntroductionAspect ? Например, в DataContractAttribute есть свойство с именем «Namespace», которое я хотел бы установить.
2. У меня есть пример этого, нужно найти его очень быстро
3. @Paul Я обновил свой ответ примером, который я использую для применения атрибутов с параметрами конструктора. Я знаю, что не все атрибуты используют конструкторы, а вместо этого используют свойства, но это только начало. Попробуйте
4. DataContractAttribute имеет только один конструктор и принимает нулевые параметры. Поэтому я не могу установить параметры как часть параметров конструктора. Мне интересно, если API PostSharp просто не имеет возможности устанавливать свойства атрибута из customattributeintroductionaspectтипа?
5. @DistinDavis Это сработало! Я только что опубликовал решение в качестве обновления моего первоначального вопроса. Спасибо за помощь, очень признателен.