Autofac - 终身和模块

时间:2012-06-02 18:03:01

标签: oop autofac

问题(摘要)

给定一个注册依赖关系X的模块。依赖关系X在MVC3应用程序(每个HttpRequest的生存期)中具有不同的生命周期,然后在控制台应用程序中(具有名称的每个生命周期的依赖关系)。在何处或如何指定依赖关系X的生命周期?

案例

我已将所有与数据库相关的代码放在一个程序集中,其中包含一个注册所有存储库的模块。现在,ISession(Nhibernate)注册也在模块中。

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

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

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

2 个答案:

答案 0 :(得分: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();

最后,如果您知道它是网络或非网络,您可以创建一个处理交换机的自定义模块

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可能会解决您的问题。

答案 1 :(得分:0)

通常的做法是每个http请求使用一个连接。在这种情况下,将使用.InstansePerLifetimeScope()注册连接。例如,您可能会执行以下操作:

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