17

我正在尝试学习 Unity 拦截器,但我很难做到。

假设我有一个这样的界面:

public interface IMyInterface
{
   void SomeMethod();
}

而且我有未知数量的类实现了这样的接口:

public class SpecificClass1 : IMyInterface
{
   public void SomeMethod()
   {
       Console.WriteLine("Method Called");
   }
}

我正在寻找一种说法,“对于 IMyInterface 的所有实例(我不想枚举它们),当调用 SomeMethod 时运行我的拦截器。

给我带来麻烦的是类的非枚举。(如果您可以枚举所有类,则有很多示例。)

我已经阅读了类型拦截,但我似乎无法确定它是否会满足我的要求。

那里有任何 Unity 专家知道如何做我正在寻找的东西吗?

4

3 回答 3

20

您可以创建InterceptionBehavior然后在特定类上注册它。Invoke请注意,您可以通过过滤执行方法IMethodInvocation input

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.InterceptionExtension;
using NUnit.Framework;

namespace UnitTests
{
    [TestFixture]
    public class ForTest
    {
        [Test]
        public void Test()
        {
            IUnityContainer container = new UnityContainer().AddNewExtension<Interception>();
            container.RegisterType<IMyInterface, SpecificClass1>(
                new Interceptor<InterfaceInterceptor>(),
                new InterceptionBehavior<MyInterceptionBehavior>());
            var myInterface = container.Resolve<IMyInterface>();
            myInterface.SomeMethod();
        }
    }

    public interface IMyInterface
    {
        void SomeMethod();
    }

    public class SpecificClass1 : IMyInterface
    {
        #region IMyInterface

        public void SomeMethod()
        {
            Console.WriteLine("Method Called");
        }

        #endregion
    }

    public class MyInterceptionBehavior : IInterceptionBehavior
    {
        public bool WillExecute
        {
            get { return true; }
        }

        #region IInterceptionBehavior

        public IEnumerable<Type> GetRequiredInterfaces()
        {
            return Enumerable.Empty<Type>();
        }

        public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
        {
            IMethodReturn result = getNext()(input, getNext);
            Console.WriteLine("Interception Called");
            return result;
        }

        #endregion
    }
}

控制台输出

Method Called
Interception Called

有关使用 Unity 进行拦截的更多信息

于 2012-08-10T21:50:21.090 回答
9

@GSerjo,概述了运行良好的 Unity 拦截方法。如果您想自动配置拦截,您可以使用 UnityContainerExtension 自动连接所有接口拦截以及行为。如果您想进行更具体的拦截(方法名称、签名、返回值等),那么您可能需要查看 Policy Injection(使用与 CallHandlers 匹配的规则)。

所以在这种情况下,容器扩展看起来像:

public class UnityInterfaceInterceptionRegisterer : UnityContainerExtension
{
    private List<Type> interfaces = new List<Type>();
    private List<IInterceptionBehavior> behaviors = 
        new List<IInterceptionBehavior>();

    public UnityInterfaceInterceptionRegisterer(Type interfaceType, 
        IInterceptionBehavior interceptionBehavior)
    {
        interfaces.Add(interfaceType);
        behaviors.Add(interceptionBehavior);
    }

    public UnityInterfaceInterceptionRegisterer(Type[] interfaces, 
        IInterceptionBehavior[] interceptionBehaviors)
    {            
        this.interfaces.AddRange(interfaces);
        this.behaviors.AddRange(interceptionBehaviors);

        ValidateInterfaces(this.interfaces);
    }

    protected override void Initialize()
    {
        base.Container.AddNewExtension<Interception>();

        base.Context.Registering += 
            new EventHandler<RegisterEventArgs>(this.OnRegister);
    }

    private void ValidateInterfaces(List<Type> interfaces)
    {
        interfaces.ForEach((i) =>
        {
            if (!i.IsInterface)
                throw new ArgumentException("Only interface types may be configured for interface interceptors");
        }
        );
    }

    private bool ShouldIntercept(RegisterEventArgs e)
    {
        return e != null && e.TypeFrom != null && 
               e.TypeFrom.IsInterface && interfaces.Contains(e.TypeFrom);
    }

    private void OnRegister(object sender, RegisterEventArgs e)
    {
        if (ShouldIntercept(e))
        {
            IUnityContainer container = sender as IUnityContainer;

            var i = new Interceptor<InterfaceInterceptor>();
            i.AddPolicies(e.TypeFrom, e.TypeTo, e.Name, Context.Policies);

            behaviors.ForEach( (b) =>
                {
                    var ib = new InterceptionBehavior(b);
                    ib.AddPolicies(e.TypeFrom, e.TypeTo, e.Name, Context.Policies);
                }
            );
        }
    }
}

然后你可以像这样使用它:

IUnityContainer container = new UnityContainer()
    .AddExtension(new UnityInterfaceInterceptionRegisterer(
        new Type[] { typeof(IMyInterface), 
                     typeof(IMyOtherInterface) }, 
        new IInterceptionBehavior[] { new MyInterceptionBehavior(), 
                                      new AnotherInterceptionBehavior() }
        ));

container.RegisterType<IMyInterface, SpecificClass1>();

var myInterface = container.Resolve<IMyInterface>();
myInterface.SomeMethod();

现在,当接口注册时,相应的拦截策略也将添加到容器中。因此,在这种情况下,如果注册的接口是 IMyInterface 或 IMyOtherInterface 类型,则将为接口拦截设置策略,并且还将添加拦截行为 MyInterceptionBehavior 和 AnotherInterceptionBehavior。

请注意,Unity 3(在此问题/答案之后发布)添加了按约定注册功能,可以执行此扩展的功能(无需编写任何自定义代码)。使用 Unity 进行依赖注入的开发人员指南中的一个示例:

var container = new UnityContainer();

container.AddNewExtension<Interception>();
container.RegisterTypes(
    AllClasses.FromLoadedAssemblies().Where(
      t => t.Namespace == "OtherUnitySamples"),
    WithMappings.MatchingInterface,
    getInjectionMembers: t => new InjectionMember[]
    {
      new Interceptor<VirtualMethodInterceptor>(),
      new InterceptionBehavior<LoggingInterceptionBehavior>()
    });
于 2012-08-12T07:29:33.443 回答
0

设置拦截需要多个操作,包括。拦截类型、策略和处理程序的配置。

首先请参阅在应用程序中使用拦截以了解有关支持拦截的情况类型的一般详细信息(例如,使用或不使用 DI 容器)。然后查看类型拦截以获取有关支持的类型拦截器的更多详细信息。特别注意哪些拦截器可以与您的类的类型一起使用(否则处理程序将永远不会触发)。

当您决定使用什么拦截器后,根据上面的链接对其进行配置并创建一个足够的调用处理程序。如果此时您仍有问题,请发布更详细的问题。如果您已经这样做了,请将配置和代码发布为“类的非枚举”根本不会给出您实际要求的任何提示。您是否有任何机会意味着您分配了属性驱动的策略并且没有它就无法实现您想要的“枚举”?

于 2012-08-10T21:30:27.953 回答