Autofac创建2个服务实例

时间:2019-04-24 15:18:47

标签: autofac asp.net-core-webapi core

当我希望每个API请求一个实例时,Autofac可以分解为2个单独的实例。

我看到从控制器到命令处理程序的一个实例,以及我的域事件处理程序之一的另一个实例,域处理程序通过属性注入引用调度程序对象。

这里的问题是我没有看到同一个 UserContext 对象实例,在该对象实例中我通过DomainEvent.dispatcher.Raise();调用的Domain事件处理程序中的Web API控制器进行了注入和修改。

我遵循与Autofac文档相同的说明。有人可以给我一些启示吗?

Auotfac registration  
 public class AutofacModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
         builder
            .RegisterType<DomainEventDispatcher>()
            .As<IDomainEventDispatcher>().InstancePerLifetimeScope();

         builder.RegisterType<DomainEvent>().As<IDomainEvent> 
       ().PropertiesAutowired().InstancePerLifetimeScope().AutoActivate();
        builder.RegisterType<UserContext>().As<IApplicationContext> 
        ().InstancePerLifetimeScope();
}

}
My DomainEvent class
--------------------------
internal class DomainEvent:IDomainEvent
{
    public static IDomainEventDispatcher dispatcher { get;  set; }

}

1 个答案:

答案 0 :(得分:0)

由于您没有提供a Minimal, Complete, and Verifiable Example,因此很难弄清楚。该示例以及与您的问题相关的所有代码都应在问题中-如果注释中有任何问题,请使用答案更新您的问题。

也就是说,我可以看到您可能陷入的陷阱。

首先,我看到了:

builder
  .RegisterType<DomainEventDispatcher>()
  .As<IDomainEventDispatcher>()
  .InstancePerLifetimeScope();

在ASP.NET/ASP.NET Core中,这通常等同于“每个请求一个实例”,这似乎是您要遵循的方向。但是,这意味着它只能在请求生存期范围内解决

根容器也是生命周期范围。这就是单例解决的地方。这也是解决单例依赖项的地方。 There's a looooot of documentation on lifetime scopes.

这很重要,因为我看到另外两件事引起了一些关注。

首先...

internal class DomainEvent : IDomainEvent
{
    public static IDomainEventDispatcher dispatcher { get;  set; }
}

static真正有关,因为您已将分派器注册为每个生存期范围的实例...但是static将会出现线程问题。我假设(尽管由于缺少MCVE而无法验证),您正在做某事,以便在收到请求时设置此静态变量。当一次只有一个请求时,这可能在开发中非常有用。当您有两个请求,而调度程序的一个实例踩在另一个实例上时,这将真是个坏消息。

不要混合使用静态和按请求。静态是单例。总是。

第二...

builder
  .RegisterType<DomainEvent>()
  .As<IDomainEvent>()
  .PropertiesAutowired()
  .InstancePerLifetimeScope()
  .AutoActivate();

您看起来应该根据请求创建内容,但是您还有AutoActivate()。这将在容器构建时从根容器中解析这些之一。我不确定发生了什么,也许共享数据连接正在启动或其他什么,但这意味着您正在容器构建时从容器中解决了DomainEvent,实例将被缓存直到容器被丢弃,因为您为每个生存期作用域指定了实例...而且,容器也是一个作用域。如果您要在根级别上请求第二个根(例如对单例的依赖性),则会得到此根,而不是每个请求一个。

我认为以后您还会在某个地方解析DomainEvent个对象。如果它们位于控制器之内,则可能来自请求范围。

因此,您对范围和激活有一系列混杂的要求。

  • IDomainEventDispatcher的静态引用将是整个应用的单例……但IDomainEventDispatcher的每个生命周期范围的注册。
  • IDomainEvent自动激活,这将在根容器处发生...但是由于每个生命周期范围的注册,因此希望按请求进行激活。

这里显然也有一个UserContext在起作用,但是代码中没有任何内容可以解释其消耗方式或作用,因此无法解决部分问题。

我建议:

  • 将对调度程序的static引用切换为实例属性。如果要使调度程序成为单例,请在Autofac中将其注册为SingleInstance
  • 如果可以,请避免注入属性。如果DomainEvent需要调度程序,则将其放入构造函数中。这样可以确保您始终得到一份。如果不能注入属性,则注入属性不会失败。
  • 请勿在每个请求项上使用AutoActivate。如果您具有共享的数据连接或需要在容器构建时启动的内容,请将该逻辑分离到可以注册为单例的某种数据提供程序中。这也将避免对每个请求应做的事情进行根级解析。