按属性划分C#unity拦截

时间:2013-06-13 15:04:41

标签: c# unity-container interception

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

2 个答案:

答案 0 :(得分: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!

显示第一种方法是截获的,而第二种方法则没有。

答案 1 :(得分: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>());