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

时间:2015-09-18 18:34:24

标签: c# asp.net-web-api inversion-of-control ioc-container simple-injector

我的应用程序中有一个针对我的DbContexts的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(Transient)。

- [Disposable Transient Component] CustomerContext注册为transient,但实现了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。

有关如何注册的任何想法?

1 个答案:

答案 0 :(得分:6)

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

因为CustomerContext具有默认构造函数,所以Simple Injector能够为您自动装配此依赖项。但是当Simple Injector在第一次调用GetInstance时对此进行注册时,容器除了使用默认生活方式注册此servicetype之外别无选择。然而,Simple Injector的默认生活方式是:Transient

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

要解决此问题,请将CustomerContext的注册添加为:

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

您可以删除警告抑制的配置,因为这些“警告”是实际问题!

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

但最好在容器中注册所有需要的类型,因为容器在这种情况下完全了解所有类,从而能够尽最大努力Verify您的配置。< / p>

这是良好做法的第二个原因是因为每个DI容器都有不同的“默认生活方式”。 Simple Injector默认为Transient,而其他默认值为Singleton。如果您稍后交换DI容器,最终可能会导致应用程序在运行时失败,因为某个组件突然变为Singleton

您收到InvalidOperationException抑制警告的原因是容器未能(还)能够抑制打开的泛型类型的警告。在这种情况下,这是件好事,因为你必须来寻求帮助!

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

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

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

  • 使用queryhandler / repository或任何服务
  • 从数据库中获取记录
  • 您将在另一个服务/命令处理程序中使用一些新信息更新实体,该服务/命令处理程序具有自己对DbContext的依赖关系,因而是另一个实例
  • 你可能在另一个类中调用DbContext.SaveChanges(),再次使用自己的DbContext实例,SaveChangesCommandHandlerDecorator进行检查。由于此DbContext未跟踪任何更改,因此数据库不会更新。

为了完整起见:

警告在CustomerContextUnitOfWork<CustomerContext>上给出,因此您应该已经抑制了此servicetypes注册的警告。幸运的是,您没有CustomerContext的注册,而且您需要做一些工作来抑制UnitOfwork<CustomerContext上的警告!

您收到的警告文档为herehere