向 IoC 注册通用 UnitOfWork<> 时出错

Error registering generic UnitOfWork<> with IoC

我的应用程序中的 DbContext 有一个 UnitOfWork 的通用实现,其工作方式如下:

 public class UnitOfWork<TContext> : IDisposable, IUnitOfWork<TContext>
        where TContext : DbContext
 {
     private readonly TContext _context;

     [..UoW default implementations..]

     public void Dispose()
     {
        _context.Dispose();
     }
  }

并注册...:

public static class SimpleInjectorWebApiInitializer
    {
        /// <summary>Initialize the container and register it as Web API Dependency Resolver.</summary>
        public static void Initialize()
        {
            var container = new Container();
            container.Options.DefaultScopedLifestyle = new WebApiRequestLifestyle();

            InitializeContainer(container);

            container.RegisterWebApiControllers(GlobalConfiguration.Configuration);

            container.Verify();

            GlobalConfiguration.Configuration.DependencyResolver =
                new SimpleInjectorWebApiDependencyResolver(container);
        }

        private static void InitializeContainer(Container container)
        {

            // For instance:
            container.Register(typeof(IUnitOfWork<>), typeof(UnitOfWork<>), Lifestyle.Scoped);
            container.RegisterWebApiRequest<IBankRepository, BankRepository>();
            container.RegisterWebApiRequest<IBankService, BankService>();

        }
    }

当我尝试注册此类型和其他服务时,我收到这些警告:

-[生活方式不匹配] UnitOfWork<CustomerContext>(Web API 请求)取决于 CustomerContext(瞬态)。

-[一次性瞬态组件] CustomerContext 注册为瞬态,但实现了 IDisposable。

我的应用程序架构使用默认的 WebAPI 实现:

[RoutePrefix("customer/banks")]
    public class BankController : ApiController
    {
        private readonly IBankService _bankService;

        public BankController(IBankService bankService)
        {
            _bankService = bankService;
        }

        [Route]
        public IEnumerable<BancoModel> Get()
        {
            var result = _bankService.GetBanks();
            [...mappings and return...]            
        }
    }

我已经尝试抑制这些警告:

Registration registration = container.GetRegistration(typeof(IUnitOfWork<>)).Registration;
            registration.SuppressDiagnosticWarning(DiagnosticType.DisposableTransientComponent, "Dispose called by application code");

..但我收到 InvalidOperationException。

关于如何注册这个有什么想法吗?

您遇到的问题是您没有为 CustomerContext 添加注册。

因为CustomerContext有一个默认的构造函数,Simple Injector 能够为你自动装配这个依赖。但是,当 Simple Injector 在第一次调用 GetInstance 时为此进行注册时,容器除了使用默认生活方式注册此服务类型外别无选择。然而,Simple Injector 的默认生活方式是:Transient.

换句话说,您收到的诊断警告是正确的,因为 CustomerContext 实际上注册为 Transient

要解决此问题,请为 CustomerContext 添加一个注册为:

container.Register<CustomerContext>(Lifestyle.Scoped);

你可以去掉警告抑制的配置,因为这些'warnings'是真实存在的问题!

此时实际上有第三个诊断警告可用。您可以阅读 Container-Registered Types 警告 here。此诊断警告通知您对象图中需要的类型不属于您的配置。这是一个信息性警告,因此在验证容器时不会作为异常抛出。信息类别的原因是,对于可以是 Transient 的简单 class,这根本不是问题,并且存在您无法注册所有 class 的有效用例es 在编译时。

但最好在容器中注册所有需要的类型,因为在这种情况下容器完全了解所有 classes,因此能够尽其所能 Verify 你的配置。

这是良好做法的第二个原因是每个 DI 容器都有不同的 'default lifestyle'。简单注入器默认为 Transient,而其他注入器默认为 Singleton。如果您以后要交换 DI 容器,您可能会得到一个在运行时失败的应用程序,因为某些组件突然变成 Singleton.

您在抑制警告时收到 InvalidOperationException 的原因是容器(尚)无法抑制开放泛型类型的警告。在这种情况下这是一件好事,因为你必须来寻求帮助!

如果您成功地抑制了这些警告,您很快就会发现自己在调试奇怪的问题。

因为如果你的 DbContextTransient 会发生什么?

每个 service/commandhandler/queryhandler/etc。依赖于此 DbContext 的将得到一个不同的!!这可能会导致这种情况:

  • 你使用 queryhandler/repository 或任何服务从数据库中获取一条记录
  • 您将在另一个 service/commandhandler 中用一些新信息更新实体,它对 DbContext 有自己的依赖性,因此是一个不同的实例
  • 您可能在另一个 class 中调用 DbContext.SaveChanges(),同样使用它自己的 DbContext 实例,例如 SaveChangesCommandHandlerDecorator。因为这个 DbContext 没有跟踪任何更改,所以数据库没有更新。

为了完整起见:

警告是在 CustomerContextUnitOfWork<CustomerContext> 上给出的,因此您应该抑制注册此服务类型时的警告。幸运的是,您没有注册 CustomerContext,您必须做更多的工作来抑制 UnitOfwork<CustomerContext!

上的警告

您收到的警告的文档是 here and here