如何使用 PostSharp 属性注入属性?
如何编写 PostSharp 方面以将属性应用于类?我正在考虑的场景是需要用 DataContract 属性修饰的 WCF 实体(或域对象)。它还应该有一个 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 属性。手动完成此操作既乏味又不独特。我真的只想编写一个可以应用于我的“域”名称空间的单个方面。然后它会为我应用序列化相关的属性。这样我就可以专注于开发实体对象,而不必担心序列化细节。
我在 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 &&
!property.IsDefined(typeof (NotDataMemberAttribute), false)))
yield return new AspectInstance(property, introduceDataMemberAspect);
}
}
[AttributeUsage(AttributeTargets.Property)]
public sealed class NotDataMemberAttribute : Attribute
{
}
}
How can I write a PostSharp aspect to apply an attribute to a class? The scenario I'm considering is a WCF entity (or domain object) that needs to be decorated with the DataContract
attribute. It should also have a Namespace
property. Like this:
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; }
}
}
}
In the above example you can see what I want the output to look like. It has the DataContract attribute applied to the class. Doing this by hand is tedious and not unique. I'd really just like to write a single aspect that can be applied a my "Domain" namespace. It would then apply the serialization related attributes for me. This way I can just focus on developing entity objects, and not worry about the serialization pluming details.
I have found documentation on PostSharp's website for injecting code before, after, and instead of methods. However what I'm looking for is a way to inject an Attribute onto a type.
Here is the solution!
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 &&
!property.IsDefined(typeof (NotDataMemberAttribute), false)))
yield return new AspectInstance(property, introduceDataMemberAspect);
}
}
[AttributeUsage(AttributeTargets.Property)]
public sealed class NotDataMemberAttribute : Attribute
{
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
请参阅http://www.sharpcrafters.com/blog/post/PostSharp-Principals-Day-12-e28093-Aspect-Providers-e28093-Part-1.aspx
这是一个工作示例。将此方面应用于类会将 XmlIgnore 属性应用于尚未应用 XmlElement 或 XmlAttribute 的任何公共属性。诀窍是使用 Postsharp 内置的 CustomAttributeIntroductioinAspect。您只需实例化一个指定属性类型和构造函数详细信息的实例,然后创建一个提供程序以将其应用到目标。
要使用属性,指定参数,我使用
See http://www.sharpcrafters.com/blog/post/PostSharp-Principals-Day-12-e28093-Aspect-Providers-e28093-Part-1.aspx
Here is a working example. Applying this aspect to a class will apply the XmlIgnore attribute to any public property that does not already have XmlElement or XmlAttribute applied to it. the trick is using the CustomAttributeIntroductioinAspect that is built in to Postsharp. You just need to instantiate an instance specifying the attribute type and contructor details, then create a provider to apply it to the target(s).
To use attributes, specifying parameters, I use