2012-06-02 24 views
9

问题(摘要)Autofac - 终身和模块

鉴于哪些寄存器依赖性X的依赖性X具有在MVC3应用程式(每HttpRequest的寿命)不同寿命的模块然后在控制台应用程序(依赖每带名称的lifetimescope)。在哪里或如何指定依赖项X的生存期?

案例

我已经把我所有的数据库相关的代码放在一个组件,它的模块,其注册的所有存储库。现在ISession(Nhibernate)注册也在模块中。

ISession是依赖项X(在给定的问题情况下)。 ISession在MVC3应用程序中有不同的生命周期(每个请求的生命期),然后在控制台应用程序中定义一个命名的生命周期。

ISession的注册应该在模块之外吗?会很奇怪,因为它是一个实现细节。

这里最好的情况是什么?设计缺陷还是有这个:)的智能建筑?

+0

你有关于连接的具体生命时间吗?如果是这样,每个应用程序的生命周期是不同的,还是不管应用程序的类型如何都是相同的?怎么样汇集?你是否在某些应用中游泳,但不是其他人? –

+0

就像你说的那样:“MVC扩展使用一个命名范围来实现一次一次请求机制。”当您在控制台应用程序中使用模块时,名为范围的Http不存在。目前我正在研究工作单元的内容,并将其封装在一个范围内。 –

回答

7

鉴于您的用例描述,我会说你有几个选项。

首先,您可以让每个应用程序注册自己的一组依赖关系,包括生命周期范围。在这方面有一两个“重复”的代码片段并不是什么大问题,因为考虑到应用程序与注册似乎很小的事实之间的差异。

二,您可以将公共部分(减去生命周期范围)包装到ContainerBuilder扩展方法中,该方法可以在每个应用程序中使用。它仍然意味着每个应用程序都有一个“重复代码”,但通用逻辑将被包装在一个简单的扩展中。

public static IRegistrationBuilder<TLimit, ScanningActivatorData, DynamicRegistrationStyle> 
    RegisterConnection<TLimit, ScanningActivatorData, DynamicRegistrationStyle>(this ContainerBuilder builder) 
{ 
    // Put the common logic here: 
    builder.Register(...).AsImplementedInterfaces(); 
} 

消费中的每个应用这样的扩展将是这样的:

builder.RegisterConnection().InstancePerHttpRequest(); 
// or 
builder.RegisterConnection().InstancePerLifetimeScope(); 

最后,如果你知道它的Web或非Web,你可以做一个处理开关自定义模块

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 application: 
builder.RegisterModule(new ConnectionModule(true)); 

// Non-web application: 
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 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")); 

我会建议不要简单地在Web应用程序中使用InstancePerLifetimeScope除非这其实你想要什么。正如在其他回答/评论中指出的那样,InstancePerHttpRequest使用特定的命名生命周期范围,以便创建子周期范围是安全的;使用InstancePerLifetimeScope没有这样的限制,所以实际上你会得到每个子范围的一个连接,而不是请求的一个连接。我个人不认为其他开发人员不会使用子女生命期范围(which is a recommended practice),所以在我的应用程序中我非常具体。如果您完全控制您的应用程序,并且可以确保您没有创建额外的子范围,或者您确实希望每个范围都有一个连接,那么也许InstancePerLifetimeScope将解决您的问题。

+0

感谢您的广泛答复!我想出了这个:https://gist.github.com/2883596你可以调用ConfigureUnitOfWork来配置隐藏实现细节的依赖项IUnitOfWork。此解决方案也不依赖于MVC3扩展。这个解决方案唯一的缺点是,你可以配置所有的东西,而你只希望允许一生,可以被抽象出来,但是如果现在就切割了:) –

0

通常的做法是每个http请求使用一个连接。既然如此,连接将使用.InstansePerLifetimeScope()注册。例如,你可能会这样做:

builder 
    .Register(c => { 
         var conn = new SqlConnection(GetConnectionString()); 
         conn.Open(); 
         return conn; 
        }) 
    .AsImplementedInterfaces() 
    .InstancePerLifetimeScope(); 
+0

是的,我知道你可以做到这一点。我的问题是在哪里做到这一点?这是“数据”模块应负责的事情。但是,似乎没有办法在模块内注册连接,并在模块外设置生命周期。 –

+0

我会注册连接并设置数据模块内的生命周期。 –

+0

这就是MVC应用程序的生命周期中的问题与控制台应用程序中的问题不同,因为在没有诸如Http –