2011-10-04 56 views
1

所以,我试图解决一个我确定别人已经遇到的问题。基本上,我希望对IoC容器的调用递归解决依赖关系,但也可能执行一些自定义代码,以根据一组预定义条件更改结果。这是模糊的,所以让我举一个例子:Unity IOC递归 - 拦截器的使用?

说我有一个控制器,像这样:

public class SampleController : Controller 
{ 
     protected SampleType _sampleType = null; 

     public SampleController(SampleType sampleType) 
     { 
      _sampleType = sampleType; 
     } 
} 

我也得到了该控制器的一些测试版本(比如我重构它,我想使确保它不被AB可怕打破督促测试它的曝光):

public class SampleController_V2 : SampleController 
{ 
     protected SampleType _sampleType = null; 
     protected AnotherType _anotherType = null; 

     public SampleController_V2(SampleType sampleType, AnotherType anotherType) 
     { 
      _sampleType = sampleType; 
      _anotherType = anotherType; 
     } 
} 

我已经延长了DefaultControllerFactory创建控制器时使用统一。这一切工作正常。现在,我想要做什么,它提供了如果需要解决的问题,可以在层次结构中测试任何特定类型的功能。这适用于顶层,但不适用于通过对象图递归的子元素。

现在,它会选择适当的控制器来解决并赋予其依赖关系。然而,我似乎无法拦截个别调用依赖关系,也AB测试这些。我可以通过数据库配置定义一个测试,然后让IOC容器根据标准解决它。例如:

SessionIds that start with the letter 'a': SampleController_V2 
Everyone Else       : SampleController 
UserIds ending in 9      : SampleType_V2 
Everyone Else       : SampleType 

这一切都适用于顶级项目。但是,拨打_unityContainer.Resolve(type)似乎不是一个递归调用;我希望能够注入代码到任意点时,它试图解决型:

-> Attempt to Resolve SampleController 
    -> Test Code Tells Us to use _V2 if possible. 
    -> Attempt to Resolve SampleType 
     -> Test Code tells us to use the _V1 if possible. 
    -> Resolves SampleType_V1 
    -> Attempt to Resolve AnotherType 
     -> No Test Defined, Use the Default 
    -> Resolves AnotherType 
-> Resolves SampleController_V2 (passing SampleType_V1 as the dependency and then AnotherType as the other dependency) 

通过一些网上的文章看,这听起来像我需要使用某种形式的统一拦截,但它的几乎就像我正在编写我自己的IoC容器,并且内置了某种测试体系结构。

希望有人对如何在找到构造函数并解决问题之前要做到这一点有个好主意每种类型递归。

编辑:所以它实际上并没有证明这是可怕的创建我自己的注入通过递归地检查每个依赖项的构造函数参数,但我认为如果我为我自己抛出Unity,老板们可能会有点不安。定制解决方案

+0

我可能会误解。您想要根据输入的测试数据解析不同的对象图吗?我不确定你为什么要这样做 - 当我用容器测试时,我尽量保持对象图尽可能接近生产。 –

+0

这是为特定对象提供AB测试。例如:假设我有一个EmailSender类。它确实是X.但是,我想测试一个重写的EmailSender类。我有国际奥委会容器解决一个EmailSender_V2类在一组情况下会做Y.说它可能是性能密集型的;我想慢慢介绍一下它是否会对网站产生负面影响。然后,我可以上升到100%,并将代码转移到基本版本,而无需修改控制版本。大多数用户获得相同的体验;一些用户在测试时获得增强的体验。 – Tejs

回答

0

我想我只是走上了制定自定义解决方案的道路。实际上,我不得不破解当前使用的IoC来获得我想要的效果,这不仅仅是制作我自己的解决方案。除了依赖关系的简单解决方案之外,我甚至没有将IoC用于大多数功能,所以我不确定我会错过使用它。我结束了以下方法:

public virtual object Resolve(Type requestedType, Session session = null, RequestContext requestContext = null) 
{ 
    ConstructorInfo constructor = requestedType.GetConstructors().First(); 
    object[] dependencies = null; 
    Type resultType = requestedType; 

    if (session == null || requestContext == null) 
    { 
     dependencies = constructor.GetParameters().Select(parameter => Resolve(parameter.ParameterType)).ToArray(); 
    } 
    else 
    { 
     InitializeTestingArchitecture(); 

     var testVersion = _testingProvider.GetTestVersionFor(requestedType, requestContext, session); 

     if(testVersion == null) 
      return Resolve(requestedType); 

     resultType = _testTypeLoader.LoadTypeForTestVersion(requestedType, testVersion); 
     constructor = resultType.GetConstructors().First(); 

     dependencies = constructor.GetParameters().Select(parameter => Resolve(parameter.ParameterType, session, requestContext)).ToArray(); 
    } 

    return Activator.CreateInstance(resultType, dependencies); 
} 

这样,我可以通过数据库记录控制暴露的AB类实例。

1

我会尝试在请求期间换出Unity容器。检测到它是你的“V2”条件,然后创建一个新的容器,但注册了不同的一组类型。在该请求的范围内使用它。在生产的每一个请求中都创建一个新的容器并不是一个好主意,但它对于测试应该是一个好主意。

+0

所以你是说做一个临时容器,为那个特定的请求做'Register ()'? – Tejs

+0

是的,没错。 – RandomEngy

+0

唯一的问题是可能每个请求都有一个临时统一容器。是不是很昂贵的建设? – Tejs

1

假设你可以写一个Create方法的ABEmailSenderFactory ...

在Autofac这将是那么容易,因为

builder.RegisterType<ABEmailSenderFactory>().AsSelf(); 

builder.Register(c => c.Resolve<ABEmailSenderFactory>().Create()) 
    .As<EmailSender>(); 

我不知道很多关于团结,但it looks like it may not be as easy

0

有几个选项可以在这里使用。我在这里假设Unity 2.0,这在早期版本中更加困难。

你可以预先计算的时间提前了各种排列和决心通话使用解析器覆盖:

container.Resolve(figureOutControllerType(), 
    new DependencyOverride<SampleType>(figureOutSampleType())); 

这将取代SampleType解析的对象无论它出现在树中,但它确实需要一个实例直接。

您可以使用已命名的注册并针对您正在进行A/B测试的每个因素组合执行NxM套注册。虽然在大约4次更改后会变得非常混乱。

你可以使用你对测试的东西的injectionfactory - 工厂可以找出哪一个使用方法:

container.RegisterType<SimpleType>(
    new InjectionFactory((c) => container.Resolve(figureOutWhichType())); 

这会基于无论figureOutWhichType方法做了运行时的开关。

第三个选项是一个容器扩展,它添加了一个类型映射策略来执行解析链中的逻辑。

在这些选项中,我可能会先从工厂方法开始,如果情况无法实现,则转到自定义扩展。