【问题标题】:Autofac - Lifetime and modulesAutofac - 生命周期和模块
【发布时间】:2012-06-06 00:53:25
【问题描述】:

问题(摘要)

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

案例

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

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

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

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

【问题讨论】:

  • 您是否考虑过连接的特定生命周期?如果是这样,每个应用程序的生命周期是否不同,或者无论应用程序类型如何,它们总是相同的?池化呢?您是否集中使用某些应用程序而不使用其他应用程序?
  • 就像你说的“MVC 扩展使用命名范围来实现一次请求机制。”。当您在控制台应用程序中使用模块时,Http 命名范围不存在。目前我正在研究工作单元是什么,并且将被封装在一个范围内。

标签: oop autofac


【解决方案1】:

鉴于您的用例描述,我想说您有几个选择。

首先,您可以让每个应用程序注册自己的一组依赖项,包括生命周期范围。考虑到应用程序之间的差异以及注册看起来相当小的事实,在这方面拥有一两段“重复”代码并不是什么大问题。

其次,您可以将公共部分(减去生命周期范围)包装到可在每个应用程序中使用的 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,除非这确实是您想要的。 正如其他答案/cmets 中所述,InstancePerHttpRequest 使用特定的命名生命周期范围,因此它是安全地创建子生命周期范围;使用InstancePerLifetimeScope 没有这样的限制,所以实际上每个子作用域只有一个连接,而不是请求的一个连接。我个人并不认为其他开发人员不会使用子生命周期范围 (which is a recommended practice),所以在我的应用程序中我非常具体。如果您可以完全控制您的应用程序,并且可以确保您没有创建额外的子作用域,或者您确实希望每个作用域有一个连接,那么InstancePerLifetimeScope 可能会解决您的问题。

【讨论】:

  • 感谢您的广泛回答!我想出了这个:gist.github.com/2883596 你调用 ConfigureUnitOfWork 来配置依赖项 IUnitOfWork,它隐藏了实现细节。此外,此解决方案不依赖于 MVC3 扩展。此解决方案的唯一缺点是您可以配置所有内容,而您只想允许生命周期,可以被抽象掉,但如果现在就删掉:)
【解决方案2】:

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

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

【讨论】:

  • 是的,我知道你可以做到。我的问题是在哪里做这个?这是“数据”模块应该负责的事情。但是似乎没有办法在模块内注册连接并在模块外设置生命周期。
  • 我会注册连接并在数据模块中设置生命周期。
  • 这就是 MVC 应用程序中的问题,生命周期与控制台应用程序中的不同,其中没有诸如 Http 之类的上下文
  • InstancePerLifetimeScope() 是不可知的。在控制台应用程序中,可能只有一个生命周期范围。在 asp.net 应用程序中,autofac 会为您将生命周期范围绑定到 httpcontext。其实挺聪明的。
  • Simply InstancePerLifetimeScope 不能保证每个 HTTP 请求只有一个。如果您在单个请求期间创建任何嵌套的生命周期范围,您也会为每个嵌套范围获得一个。 MVC 扩展使用命名范围来实现一次请求机制。
猜你喜欢
  • 2012-11-24
  • 1970-01-01
  • 2023-04-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-13
相关资源
最近更新 更多