虽然我一直在使用Ninject,但我一般都很喜欢DI,所以我是使用Simple Injector的全新品牌。吸引我想要使用Simple Injector的一件事是装饰者的易用性。如何装饰依赖运行时值创建的类
我已经能够在所有正常情况下使用Simple Injector成功使用装饰器,在请求服务时解决了依赖关系。然而,我很难弄清楚在服务必须使用运行时值构建的情况下是否有办法让我的装饰器应用于案例中。
在Ninject中,我可以将ConstructorArgument
传递给kernel.Get<IService>
请求,该请求可以沿着N个装饰器链一路继承到“真正的”实现类。我无法想出一个方法来复制使用简单注射器。
我已经在下面说明了一些非常基本的代码。我想在现实世界中做的事情是将IMyClassFactory
实例传递给我的应用程序中的其他类。那些其他类可以使用它们创建IMyClass
实例,使用它们提供的IRuntimeValue
。他们从IMyClassFactory
得到的IMyClass
实例将被注册的装饰器自动装饰。
我知道我可以在我的IMyClassFactory
或任何Func<IMyClass>
中手动应用我的装饰器,但我希望它能“正常工作”。
我一直在试图抽象出MyClass
构造,但我无法弄清楚如何使用IRuntimeValue
构造函数参数来解决它并进行装饰。
我忽略了一个明显的解决方案吗?
using System;
using SimpleInjector;
using SimpleInjector.Extensions;
public class MyApp
{
[STAThread]
public static void Main()
{
var container = new Container();
container.Register<IMyClassFactory, MyClassFactory>();
container.RegisterDecorator(typeof (IMyClass), typeof (MyClassDecorator));
container.Register<Func<IRuntimeValue, IMyClass>>(
() => r => container.GetInstance<IMyClassFactory>().Create(r));
container.Register<IMyClass>(() => ?????)); // Don't know what to do
container.GetInstance<IMyClass>(); // Expect to get decorated class
}
}
public interface IRuntimeValue
{
}
public interface IMyClass
{
IRuntimeValue RuntimeValue { get; }
}
public interface IMyClassFactory
{
IMyClass Create(IRuntimeValue runtimeValue);
}
public class MyClassFactory : IMyClassFactory
{
public IMyClass Create(IRuntimeValue runtimeValue)
{
return new MyClass(runtimeValue);
}
}
public class MyClass : IMyClass
{
private readonly IRuntimeValue _runtimeValue;
public MyClass(IRuntimeValue runtimeValue)
{
_runtimeValue = runtimeValue;
}
public IRuntimeValue RuntimeValue
{
get
{
return _runtimeValue;
}
}
}
public class MyClassDecorator : IMyClass
{
private readonly IMyClass _inner;
public MyClassDecorator(IMyClass inner)
{
_inner = inner;
}
public IRuntimeValue RuntimeValue
{
get
{
return _inner.RuntimeValue;
}
}
}
编辑1:
好,感谢史蒂芬为伟大的答案。它给了我一些想法。
虽然不是我的情况,但更多的是“经典”。假设我有一个ICustomer
,它是在运行时通过读取数据库或从磁盘反序列化或创建的东西创建的。所以我想这将被认为是一个“新”引用史蒂文连接articles之一。我想创建一个ICustomerViewModel
的实例,以便我可以显示和操作我的ICustomer
。我的具体CustomerViewModel
类在其构造函数中接受了ICustomer
以及可以由容器解析的另一个依赖项。
所以我有一个ICustomerViewModelFactory
有一个.Create(ICustomer customer)
定义方法返回ICustomerViewModel
。我总能得到这个工作之前,我问这个问题,因为我在执行的ICustomerViewModelFactory
我能做到这一点(厂组成根实现):
return new CustomerViewModel(customer, container.GetInstance<IDependency>());
我的问题是,我想我的ICustomerViewModel
由容器进行装饰并且将它推到新的地位。现在我知道如何解决这个限制。
所以我想我的后续问题是:我的设计是否在第一个地方错了?我真的觉得ICustomer
应该被传递到CustomerViewModel
的构造函数中,因为它演示了它的意图,它是必需的,得到验证等。我不想在事后添加它。
此外,如果服务取决于运行时值,那么您已经将一些知识用于服务的实现方面,这是您尝试避免的一点。如果一个实现需要传递数字42,那么这个数字是否与不同的实现有关? 42是什么意思? –
@ LasseV.Karlsen我完全同意你的价值类型和类似的东西。但我试图让我的脑袋里说,运行时的价值是ICustomer刚刚从数据库中读入,我需要将它(以及一些其他容器解析的依赖关系)传递给一个构造函数说一个CustomerViewModel。虽然史蒂文给了我很多想法,但是这是否是正确的设计。 –
这是我消化和思考的很多信息(并且感谢您的链接并帮助我陷入成功之坑)。我将添加一个编辑以作进一步评论。实际上我自己想出了你自己的第二个解决方案,但是有点害怕它,因为1)我从来没有使用ThreadLocal存储(整洁),2)我担心可能存在一些其他的副作用,在上下文中引用对象。但那可能是我走的路。 –