7

有没有办法在 C# unity 中使用属性拦截并将对象注册代码保留在 XML 文件中(如 app.config)?如果是的话,你能给我提供代码吗?这样的注册应该是什么样子?我做了很多解决方法,但没有找到解决这个问题的有效解决方案。

4

2 回答 2

14

我假设您的意思是使用自定义属性来指示要拦截的方法。您可以使用策略注入来实现使用 XML 配置的拦截。

首先,让我们定义一个自定义属性:

[AttributeUsage(AttributeTargets.Method)]
public class MyInterceptionAttribute : Attribute
{
}

接下来我们可以创建一个 ICallHandler 来做一些拦截工作。这个实现只会在方法之前和之后做一个 Console.WriteLine :

public class MyLoggingCallHandler : ICallHandler
{
    IMethodReturn ICallHandler.Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
    {
        Console.WriteLine("Invoking " + input.MethodBase.Name);
        IMethodReturn result = getNext()(input, getNext);
        Console.WriteLine("Done Invoke");
        return result;
    }

    int ICallHandler.Order { get; set; }
}

接下来让我们假设我们有一些接口和一个实现:

public interface IMyClass
{
    void Do();
    void DoAgain();
}

public class MyClass : IMyClass 
{
    [MyInterception]
    public void Do()
    {
        Console.WriteLine("Do!");
    }

    public void DoAgain()
    {
        Console.WriteLine("Do Again!");
    }
}

请注意,我仅将自定义属性 MyInterception 应用于 Do 方法,而不是 DoAgain 方法。我们将拦截对 Do 方法的所有调用。

接下来我们创建配置以定义策略,配置匹配规则并注册类型以及拦截器:

<?xml version="1.0"?>
<configuration>
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
  </configSections>
  <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
    <namespace name="UnityCallHandlerConfig" />
    <assembly name="UnityCallHandlerConfig"  />
    <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration"/>
    <container>
      <extension type="Interception"/>
      <interception>
        <policy name="Policy">
          <matchingRule name="Match" type="Microsoft.Practices.Unity.InterceptionExtension.CustomAttributeMatchingRule, Microsoft.Practices.Unity.Interception">
            <constructor>
              <param name="attributeType" value="UnityCallHandlerConfig.MyInterceptionAttribute, UnityCallHandlerConfig" typeConverter="AssemblyQualifiedTypeNameConverter" />
              <param name="inherited">
                <value value="false"/>
              </param>
            </constructor>
          </matchingRule>
          <callHandler name="MyLogging" type="MyLoggingCallHandler">
            <lifetime type="singleton"/>
          </callHandler>
        </policy>
      </interception>
      <register type="IMyClass" mapTo="MyClass">
        <interceptor type="InterfaceInterceptor"/>
        <interceptionBehavior type="PolicyInjectionBehavior"/>
      </register>
    </container>
  </unity>

  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
  </startup>
</configuration>

我们还需要一个类型转换器来将自定义属性的字符串表示形式转换为正确的类型:

public class AssemblyQualifiedTypeNameConverter : ConfigurationConverterBase
{
    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
    {
        if (value != null)
        {
            Type typeValue = value as Type;
            if (typeValue == null)
            {
                throw new ArgumentException("Cannot convert type", typeof(Type).Name);
            }

            if (typeValue != null) return (typeValue).AssemblyQualifiedName;
        }
        return null;
    }

    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        string stringValue = (string)value;
        if (!string.IsNullOrEmpty(stringValue))
        {
            Type result = Type.GetType(stringValue, false);
            if (result == null)
            {
                throw new ArgumentException("Invalid type", "value");
            }

            return result;
        }
        return null;
    }
}

完成所有设置后,我们可以创建一个容器并加载配置:

var container = new UnityContainer().LoadConfiguration();

var myClass = container.Resolve<IMyClass>();
myClass.Do();
myClass.DoAgain();

输出将是:

Invoking Do
Do!
Done Invoke
Do Again!

表明第一种方法被拦截,而第二种方法没有。

于 2013-06-17T21:59:04.290 回答
2

这是一个老问题,但对我来说真的很有用,所以我将添加统一配置的 C# 版本。

container.Configure<Interception>()
            .AddPolicy("LoggingPolicy")
            .AddMatchingRule(new CustomAttributeMatchingRule(typeof(MyInterceptionAttribute), false))
            .AddCallHandler<MyLoggingCallHandler>();
container.RegisterType<IMyClass , MyClass>(new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<PolicyInjectionBehavior>());
于 2018-03-13T22:55:28.233 回答