4

我有一个项目可以使用Unity完美运行。我尝试切换到使用简单注射器,现在没有任何更改会保存在我的数据库中。我相信它与注册组件的寿命有关。这里是Unity容器注册:IoC Unity和Simple Injector之间的注册区别

private IUnityContainer GetUnityContainer() 
{ 
    IUnityContainer container = new UnityContainer() 
     .RegisterType<IDatabaseFactory, DatabaseFactory>(
      new HttpContextLifetimeManager<IDatabaseFactory>()) 
    .RegisterType<IUnitOfWork, UnitOfWork>(
     new HttpContextLifetimeManager<IUnitOfWork>()) 
    .RegisterType<ICategoryRepository, CategoryRepository>(
     new HttpContextLifetimeManager<ICategoryRepository>()) 
    .RegisterType<ICategoryService, CategoryService>(
     new HttpContextLifetimeManager<ICategoryService>()); 

    return container;   
} 

这里是新的简单注射器注册。我不确定HttpContextLifetimeManager如何进入Simple Injector。 MVC是统一示例的客户端,但我正在更改为WPF项目和Simple Injector。任何建议,非常感谢。谢谢。


@Steven。谢谢你的评论。我刚刚发现,由于我的RepositoryBase和UnitOfWork在其构造函数中注入了一个IDatabaseFactory,所以我需要使用container.RegisterSingle<IDatabaseFactory, DatabaseFactory>()。这解决了一个问题。尽管如此,我仍然有一个问题。由于我使用的应用程序是WPF,因此RegisterPerWebRequest将如何工作?

我的项目有一个DataLayer >> BusinessLayer >> WcfService >> WPF前端。 Simple Injector在WcfService项目上设置,业务层有Boostrapper在其中注册项目。截至目前,我的WPF客户端将GetAllCountries()并显示在网格中。如果我更改名称并尝试更新,则会得到“ObjectStateManager中已存在具有相同键的对象,而ObjectStateManager无法使用同一个键跟踪多个对象。”错误。我做了一些调试,发现在WPF客户端的GetCountries服务调用之后,当我回去尝试更新时,我发现所有国家都通过dbContext.ChangeTracker.Entries()附加到上下文。在这一点上,我应该没有实体被跟踪,因为我的上下文应该在第一个工作单元后处理。

在MVC应用程序中,RegisterPerWebRequest修复了这个问题,但WPF的等价物是什么?我现在要安装扩展程序,并尝试它,但我有一种感觉,它不是我正在寻找的解决方案..或者它是什么?再次感谢您的帮助。


好的。我做了更多的挖掘并找到了可行的解决方案。我只是不确定它是否是正确的。不管怎样,现在在我的BLL那里是登记的东西引导程序,我可以这样注册:

container.RegisterPerWcfOperation<IDatabaseFactory, DatabaseFactory>(); 
container.RegisterPerWcfOperation<IUnitOfWork, UnitOfWork>(); 
container.RegisterPerWcfOperation<ICountryRepository, CountryRepository>(); 

这给了我什么,我一直在寻找。只有一个DatabaseFactory实例被创建,因此我的存储库和工作单元就像他们应该分享它一样。另外,在客户端上的GetCountries()之后,当我再次调用服务来执行和更新时,我检查了dbContext.ChangeTracker.Entries()并查看没有实体被跟踪,这是正确的。我现在可以附加,设置为修改,并调用SaveChanges而不会出现重复键错误。这看起来好吗?谢谢。

回答

5

在Simple Injector中,Register使用瞬态过载注册类型(表示无缓存)。每次请求一个实例(或注入一个实例)时,都会创建一个新实例。在Unity中,这是一样的;默认的生活方式是瞬态

看来,注册这些类型与每个Web请求生活方式是非常重要的。当在IUnitOfWork上执行提交的类获取与实际对IUnitOfWork进行更改的类获取不同的实例时,并不奇怪更改未提交到数据库。

简单的喷油器相当于Unity的HttpContextLifetimeManagerRegisterPerWebRequest方法(和WebRequestLifestyle类)。此方法不是核心库的一部分,但可用as NuGet package并作为default CodePlex download的一部分(只需在项目中包含SimpleInjector.Integration.Web.dll)。

包括你在你的项目在这之后,你可以做以下注册:

container.Options.DefaultScopedLifestyle = new WebRequestLifestyle(); 

container.Register<IDatabaseFactory, DatabaseFactory>(Lifestyle.Scoped); 
container.Register<IUnitOfWork, UnitOfWork>(Lifestyle.Scoped); 
container.Register<ICategoryRepository, CategoryRepository>(Lifestyle.Scoped); 
container.Register<ICategoryService, CategoryService>(Lifestyle.Scoped); 

或等值:

Lifestyle lifestyle = new WebRequestLifestyle(); 

container.Register<IDatabaseFactory, DatabaseFactory>(lifestyle); 
container.Register<IUnitOfWork, UnitOfWork>(lifestyle); 
container.Register<ICategoryRepository, CategoryRepository>(lifestyle); 
container.Register<ICategoryService, CategoryService>(lifestyle); 

WebRequestLifestyle的默认行为是处理创建的实例时网络请求结束。不需要特殊注册(简单注射器在应用程序启动时为您挂接HttpModule)。如果处置不是你想要的,你可以使用可用的过载。


UPDATE:

我的道歉不读你的问题到最后一行。我错过了要使用WPF客户端进行配置的事实。您可能知道,由于您的客户端与WCF服务不同,因此您的系统中将有两个Composition Roots;一个为客户,一个为服务。他们的注册可能会完全不同。对于WCF服务,确实需要RegisterPerWcfOperation或者当您按照SolidServices.CodePlex.com的参考体系结构时,您会发现它易于使用RegisterLifetimeScope并在两个Execute方法中明确定义范围。

我发现在两层应用程序(客户端 - >数据库)的客户端中管理对象的生存期比较困难,因为很难为特定范围定义工作单元。由于您使用的是命令/处理程序+查询/处理程序方法,事情会变得更加容易。客户端不会有任何工作单元。就在服务器上。您的演示者可以只依靠几个IQueryHandler<TQ, TR>ICommandHandler<T>接口,然后就完成了。查询不会更改状态,并且命令应该是原子操作。换句话说,只有在执行命令的边界内才需要一个工作单元。

我希望这会有所帮助。

+0

请参阅我对帖子所做的编辑以回应您的建议。并感谢您的帮助...... PS,一旦我解决这个问题,我将尝试使用命令/查询处理程序设置项目,如您以前所建议的。我只想先充分理解这一点。谢谢。 – BBauer42

+0

@ BBauer42:查看我的更新。 – Steven

相关问题