如何使用PostSharp属性注入属性?

7
我该如何编写一个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属性。手动执行此操作很繁琐且不唯一。我真的只想编写一个可以应用于我的“Domain”命名空间的单个方面。然后它会为我应用序列化相关的属性。这样我就可以专注于开发实体对象,而不必担心序列化细节。
我在PostSharp网站上找到了在方法之前、之后和代替注入代码的文档。然而,我正在寻找一种将Attribute注入到类型中的方法。
以下是解决方案!
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
    {
    }
}

在这里找到了解决方案:http://doc.sharpcrafters.com/postsharp-2.1/Default.aspx##PostSharp-2.1.chm/html/fa546b6c-9107-4d5f-b921-b93ead7c42a7.htm - Paul Fryer
CopyCustomAttribute并不能实现你想要的功能。它只能将自定义属性应用于你引入到目标类型中的成员,而不会将属性应用于现有成员。 - Dustin Davis
@DustinDavis 我已经删除了对 CopyCustomAttribute 的引用,这样我们就不会让任何人感到困惑了。 - Paul Fryer
必须使用PostSharp吗?使用Mono Cecil也很简单。 - Simon
1个回答

2
请参见http://www.sharpcrafters.com/blog/post/PostSharp-Principals-Day-12-e28093-Aspect-Providers-e28093-Part-1.aspx,这里有一个工作示例。将此方面应用于类将在任何未应用XmlElement或XmlAttribute的公共属性上应用XmlIgnore属性。关键是使用Postsharp内置的CustomAttributeIntroductioinAspect。您只需要实例化一个指定属性类型和构造函数详细信息的实例,然后创建一个提供程序将其应用于目标。请注意,保留HTML标记。
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);
    }
}

谢谢提供示例。我如何设置CustomAttributeIntroductionAspect的属性?例如,在DataContractAttribute上有一个名为“Namespace”的属性,我想设置它。 - Paul Fryer
我有一个例子,得快速找到它。 - Dustin Davis
@Paul 我已经更新了我的答案,并提供了一个我用于应用构造函数参数的属性示例。我知道并非所有属性都使用构造函数,而是使用属性,但这是一个开始。试一试吧。 - Dustin Davis
DataContractAttribute只有一个构造函数,它不需要任何参数。因此,我无法将参数设置为构造函数的一部分。我想知道PostSharp API是否根本没有能力从CustomAttributeIntroductionAspect类型设置属性属性? - Paul Fryer
好的,我想我找到了我要找的东西。有一个名为NamedArguments的属性。这是您可以提供名称/值对并设置属性的地方,按名称设置。仍在测试中... - Paul Fryer
1
@DistinDavis 它起作用了!我将解决方案作为原始问题的更新发布了。感谢你在这里的帮助,非常感激。 - Paul Fryer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接