Unity IOC递归-拦截器的使用
本文关键字:IOC 递归 Unity | 更新日期: 2023-09-27 18:10:22
所以,我正试图解决一个问题,我相信别人已经遇到过。基本上,我希望对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
在创建控制器时使用Unity。这一切都很好。现在,我想做的是,它提供了在层次结构中对任何特定类型进行AB测试的能力。这对于顶层很有效,但对于子元素就不行了,因为它在对象图中递归。
现在,它将选择合适的控制器来解析并给它它的依赖项。然而,我似乎无法拦截对依赖项的单独调用,也无法对这些调用进行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)
通过一些在线文章,听起来我需要使用某种Unity拦截器,但这几乎就像我在编写自己的IoC容器,其中内置了某种测试架构。
希望有人有一个好主意,如何做到这一点之前,我去寻找构造函数,然后递归地解析每个类型的痛苦。
编辑:所以它实际上并没有被证明是可怕的,通过检查每个依赖的构造函数参数递归地创建我自己的注入,但我认为老板的人可能会有点不安,如果我抛弃Unity为我自己的自定义解决方案。
我会尝试在请求期间更换Unity容器。检测到这是您的"V2"条件,然后创建一个新容器,但要注册不同的类型集。在请求的范围内使用它。在生产环境中为每个请求都创建一个新的容器不是一个好主意,但是在测试中应该是一个好主意。
假设您可以用Create
方法编写ABEmailSenderFactory
…
在autoface中,它就像
一样简单builder.RegisterType<ABEmailSenderFactory>().AsSelf();
builder.Register(c => c.Resolve<ABEmailSenderFactory>().Create())
.As<EmailSender>();
我不太了解Unity,但看起来它可能不那么容易。
我想我要做我的自定义解决方案。实际上,我必须破解我正在使用的当前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类实例的公开。 这里有几个选项可以使用。我假设这里是Unity 2.0,这在早期版本中比较困难。
您可以提前预计算各种排列,并在resolve调用中使用解析器覆盖:
container.Resolve(figureOutControllerType(),
new DependencyOverride<SampleType>(figureOutSampleType()));
这将替换SampleType在树中出现的任何地方的已解析对象,但它确实直接需要一个实例。
您可以使用命名注册,并为您进行A/B测试的每个因素组合执行NxM注册集。虽然修改了4次之后会变得很乱。
你可以为你要测试的东西使用注入工厂——工厂可以找出使用哪一个:
container.RegisterType<SimpleType>(
new InjectionFactory((c) => container.Resolve(figureOutWhichType()));
这将根据figureouthtmltype方法的操作进行运行时切换。
第三种选择是一个容器扩展,它添加了一个类型映射策略来执行解析链中的逻辑。
在这些选项中,我可能会从工厂方法开始,如果事情无法控制,则转向自定义扩展。