问题描述
给定一个注册依赖关系X的模块。依赖关系X在MVC3应用程序中具有不同的生命周期(生命周期)每个HttpRequest),然后在一个控制台应用程序(依赖每个lifetimescope与一个名称)。在哪里或如何指定依赖关系X的生命周期?
案例
将我所有的数据库相关代码放在一个程序集中,其中包含注册所有存储库的模块。现在ISession(Nhibernate)注册也在模块中。
ISession是依赖X(在给定的问题情况下)。 ISession在MVC3应用程序(每个请求的生命周期)中具有不同的生命周期,然后在控制台应用程序中定义一个命名的lifetimescope。
是否应该在模块之外注册ISession?这将是一个奇怪的,因为它是一个实现细节。
这里最好的情况是什么?设计缺陷或者有这样的智能结构:)?
根据您的用例描述,我会说你有
首先,您可以让每个应用程序注册自己的一组依赖关系,包括生命周期范围。考虑到应用程序和注册表现相当小的事实之间的差异,在这方面有一个或两个重复的代码片段并不是那么大的一个。
第二,您可以将公共部分(负寿命范围)包装到可在每个应用程序中使用的ContainerBuilder扩展方法中。这仍然意味着每个应用程序都有一些重复的代码,但常见的逻辑将被包装在一个简单的扩展。
public静态IRegistrationBuilder< TLimit,ScanningActivatorData,DynamicRegistrationStyle>
RegisterConnection< TLimit,ScanningActivatorData,DynamicRegistrationStyle>(此ContainerBuilder构建器)
{
//将常用逻辑放在此处:
builder.Register(...)AsImplementedInterfaces() ;
}
在每个应用程序中使用这样的扩展名将如下所示:
builder.RegisterConnection()。InstancePerHttpRequest();
//或
builder.RegisterConnection()。InstancePerLifetimeScope();
最后,如果你知道它是网络或非网络,你可以做一个自定义处理开关的模块:
public class ConnectionModule:Autofac.Module
{
bool _isWeb;
public ConnectionModule(bool isWeb)
{
this._isWeb = isWeb;
}
protected override void Load(ContainerBuilder builder)
{
var reg = builder.Register(...)AsImplementedInterfaces();
if(this._isWeb)
{
reg.InstancePerHttpRequest();
}
else
{
reg.InstancePerLifetimeScope();
}
}
}
在每个应用程序中,你可以然后注册模块:
// Web应用程序:
builder.RegisterModule(new ConnectionModule(true));
//非Web应用程序:
builder.RegisterModule(new ConnectionModule(false));
或者,您提到您的其他应用程序的生命周期范围有一个名称。 您可以使您的模块取名为
public class ConnectionModule:Autofac.Module
{
object _scopeTag;
public ConnectionModule(object scopeTag)
{
this._scopeTag = scopeTag;
}
protected override void Load(ContainerBuilder builder)
{
var reg = builder.Register(...)
.AsImplementedInterfaces()
.InstancePerMatchingLifetimeScope(this._scopeTag);
}
}
消费类似:
// Web应用程序(使用通常提供的标准标签):
builder.RegisterModule(new ConnectionModule(httpRequest));
//非Web应用程序(使用您的自定义作用域名称):
builder.RegisterModule(new ConnectionModule(yourOtherScopeName));
我建议不要使用 InstancePerLifetimeScope
在其他答案/评论中指出, InstancePerHttpRequest
使用特定的命名生命周期范围,以便安全创造孩子一生的范围;使用 InstancePerLifetimeScope
没有这样的限制,所以你实际上可以为每个子范围获取一个连接,而不是请求的一个连接。我个人并不认为其他开发人员不会使用小孩生命范围(),所以在我的应用程序中,我非常具体。如果您完全控制了您的应用程序,并且您可以确保您不会创建其他子范围,或者实际上每个作用域需要一个连接,那么可能 InstancePerLifetimeScope
将解决您的问题。
Problem (abstract)
Given a module which registers dependency X. The dependency X has a different lifetime in a MVC3 app (lifetime per HttpRequest) then in a console application (dependency per lifetimescope with a name). Where or how to specify the lifetime of dependency X?
Case
I've put all my database related code in a assembly with a module in it which registers all repositories. Now the ISession (Nhibernate) registration is also in the module.
ISession is dependency X (in the given problem case). ISession has different lifetime in a MVC3 app (lifetime per request) then in a console app where I define a named lifetimescope.
Should the registration of ISession be outside the module? Would be strange since it's an implementation detail.
What is the best case to do here? Design flaw or are there smart constructions for this :) ?
Given your use case description, I'd say you have a few of options.
First, you could just have each application register their own set of dependencies including lifetime scope. Having one or two "duplicate" pieces of code in this respect isn't that big of a deal considering the differences between the application and the fact that the registrations appear fairly small.
Second, you could wrap the common part (minus lifetime scope) into a ContainerBuilder extension method that could be used in each application. It would still mean each app has a little "duplicate code" but the common logic would be wrapped in a simple extension.
public static IRegistrationBuilder<TLimit, ScanningActivatorData, DynamicRegistrationStyle>
RegisterConnection<TLimit, ScanningActivatorData, DynamicRegistrationStyle>(this ContainerBuilder builder)
{
// Put the common logic here:
builder.Register(...).AsImplementedInterfaces();
}
Consuming such an extension in each app would look like:
builder.RegisterConnection().InstancePerHttpRequest();
// or
builder.RegisterConnection().InstancePerLifetimeScope();
Finally, if you know it's either web or non-web, you could make a custom module that handles the switch:
public class ConnectionModule : Autofac.Module
{
bool _isWeb;
public ConnectionModule(bool isWeb)
{
this._isWeb = isWeb;
}
protected override void Load(ContainerBuilder builder)
{
var reg = builder.Register(...).AsImplementedInterfaces();
if(this._isWeb)
{
reg.InstancePerHttpRequest();
}
else
{
reg.InstancePerLifetimeScope();
}
}
}
In each application, you could then register the module:
// Web application:
builder.RegisterModule(new ConnectionModule(true));
// Non-web application:
builder.RegisterModule(new ConnectionModule(false));
Alternatively, you mentioned your lifetime scope in your other apps has a name. You could make your module take the name:
public class ConnectionModule : Autofac.Module
{
object _scopeTag;
public ConnectionModule(object scopeTag)
{
this._scopeTag = scopeTag;
}
protected override void Load(ContainerBuilder builder)
{
var reg = builder.Register(...)
.AsImplementedInterfaces()
.InstancePerMatchingLifetimeScope(this._scopeTag);
}
}
Consumption is similar:
// Web application (using the standard tag normally provided):
builder.RegisterModule(new ConnectionModule("httpRequest"));
// Non-web application (using your custom scope name):
builder.RegisterModule(new ConnectionModule("yourOtherScopeName"));
I would recommend against simply using InstancePerLifetimeScope
in a web application unless that's actually what you intend. As noted in other answers/comments, InstancePerHttpRequest
uses a specific named lifetime scope so that it's safe to create child lifetime scopes; using InstancePerLifetimeScope
doesn't have such a restriction so you'll actually get one connection per child scope rather than one connection for the request. I, personally, don't assume that other developers won't make use of child lifetime scopes (which is a recommended practice), so in my applications I'm very specific. If you're in total control of your application and you can assure that you aren't creating additional child scopes or that you actually do want one connection per scope, then maybe InstancePerLifetimeScope
will solve your problem.
这篇关于Autofac - 终身和模块的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!