我们有一个ASP。NET REST服务由Nancy(和TinyIoc)实现,它与一个相当大的遗留应用程序接口。我们希望过渡到完全依赖注入。目前,我们的大多数模块直接实例化控制器,并采用一个context类,该类使用NancyContext Context
属性保存请求信息。像这样:
public FooModule(ILegacyService legacyService) {
Get["/"] = parameters => new FooController(new RequestContext(Context), legacyService).GetFoo();
}
我们希望直接在模块中注入控制器,并由super-duper-happy-path处理:)
我们的问题都源于我们需要NancyContext
的信息。url, header等。因此,我们尝试了各种方法来达到依赖注入的天堂。
尝试注入控制器失败,因为它们在应用范围内实例化,所以任何对RequestContext
的依赖都不会有当前上下文信息。即使在ConfigureRequestContainer
中注册RequestContext
也不会波及到所有依赖项,并且它们将保留对过期RequestContext
的引用。
我们已经尝试使用IRequestStartup
注入上下文的属性,这似乎是成功的,直到我们遇到并发问题。不同线程中的同时请求将覆盖应用程序作用域RequestContext
。
我们发现我们可以在ConfigureRequestContainer
中调用container.AutoRegister()
,但这会导致严重的延迟,因为注册需要几秒钟的时间。
在ConfigureApplicationContainer
中使用AsPerRequestSingleton()
似乎会注册一次,然后每个请求实例化,但似乎没有办法让自动注册遵守这一点。
似乎我们需要手动注册所有类型,并手动保持此配置最新。是这样吗?我们真的希望有某种类型的自动注册每个请求的生命周期。
我创建了一个小的测试项目(https://github.com/Rassi/NancyIocTest),我在其中尝试了一些解决方案。
使用DefaultNancyAspNetBootstrapper可以创建自己的自动注册,如下所示:
public class Bootstrapper : DefaultNancyAspNetBootstrapper
{
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
var assemblyClasses = Assembly.GetExecutingAssembly().GetTypes().Where(type => type.IsClass);
foreach (var assemblyClass in assemblyClasses)
{
var interfaces = assemblyClass.GetInterfaces();
if (interfaces.Count() == 1)
{
container.Register(interfaces[0], assemblyClass).AsPerRequestSingleton();
}
}
}
然后每个请求实例化所有内容,并且您可以使用RequestStartup()
:
protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context)
{
base.RequestStartup(container, pipelines, context);
var requestUrl = container.Resolve<IRequestUrl>();
requestUrl.Context = context;
}
}
这是一个简单的概念证明,它将找到实现一个接口的类,并将其注册到该接口。可能需要处理几个问题,例如:注册实现多个接口的类(可能使用将Name注册到IName的约定)。并处理多个类注册相同的接口