IoC Регистрация различий между Unity и Simple Injector
У меня есть один проект, который отлично работает с использованием Unity. Я пытаюсь вместо этого использовать Simple Injector, и теперь в моей базе данных не сохраняются никакие изменения. Я считаю, что это связано с временем жизни зарегистрированных компонентов. Вот регистрация контейнера Unity:
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;
}
А вот и новая регистрация Simple Injector.
container.Register<IDatabaseFactory, DatabaseFactory>();
container.Register<IUnitOfWork, UnitOfWork>();
container.Register<ICategoryRepository, CategoryRepository>();
container.Register<ICategoryService, CategoryService>();
Я не уверен, как HttpContextLifetimeManager
вступает в игру с простым инжектором. MVC является клиентом для примера Unity, но я перехожу на проект WPF и Simple Injector. Любые предложения очень ценятся. Благодарю.
@Steven. Спасибо за ваш комментарий. Я только что обнаружил, что так как мой RepositoryBase и мой UnitOfWork внедряют IDatabaseFactory в свои конструкторы, которые мне нужно было использовать container.RegisterSingle<IDatabaseFactory, DatabaseFactory>()
, Это решило одну проблему. У меня все еще есть проблема со временем жизни. Поскольку моим приложением-потребителем является WPF, как будет работать RegisterPerWebRequest?
У моего проекта есть DataLayer >> BusinessLayer >> WcfService >> WPF Front end. Простой Инжектор установлен в проекте WcfService, и на бизнес-уровне есть Boostrapper для регистрации там элементов. На данный момент мой клиент WPF будет GetAllCountries() и отображаться в виде сетки. Если я изменяю имя одного и пытаюсь обновить, я получаю "Объект с таким же ключом уже существует в ObjectStateManager. ObjectStateManager не может отслеживать несколько объектов с одним и тем же ключом". ошибка. Я выполнил некоторую отладку и обнаружил, что после вызова службы GetCountries в клиенте WPF, когда я возвращаюсь, чтобы попытаться обновить, я вижу, что ВСЕ страны подключены к контексту через 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 без получения ошибки дубликата ключа. Кажется ли это нормально? Благодарю.
1 ответ
Register
перегружает типы регистров Simple Injector, используя переходный образ жизни (что означает отсутствие кэширования). Каждый раз, когда вы запрашиваете экземпляр (или внедряете экземпляр), создается новый экземпляр. В Unity это то же самое; образ жизни по умолчанию является переходным.
Кажется, что регистрация этих типов в стиле Web Per Request весьма важна. Не удивительно, что изменения не фиксируются в базе данных, когда класс, который выполняет эти коммиты в IUnitOfWork
получает другой экземпляр, чем класс, который на самом деле вносит изменения в IUnitOfWork
,
Простой Инжектор эквивалентен Unity's HttpContextLifetimeManager
такое Web RequestLifestyle. Этот стиль жизни не является частью базовой библиотеки, но доступен в виде пакета NuGet.
После того, как вы включили это в свой проект, вы можете сделать следующую регистрацию:
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
заключается в удалении созданных экземпляров по окончании веб-запроса. Специальной регистрации для этого не требуется (Simple Injector подключает HttpModule
для вас, когда приложение запускается).
ОБНОВИТЬ:
Приношу свои извинения за то, что не прочитал ваш вопрос до последней строки. Я упустил тот факт, что вы хотите настроить его с помощью клиента WPF.
Как вы, вероятно, знаете, поскольку ваш клиент - это приложение, отличное от службы WCF, в вашей системе будет два Composition Roots; один для клиента, один для службы. Их регистрация, вероятно, будет совсем другой. Начиная с Simple Injector v4.1, для службы WCF вам действительно потребуется AsyncScopedLifestyle или, если вы будете следовать эталонной архитектуре в https://github.com/dotnetjunkie/solidservices, вы обнаружите, что использовать ThreadScopedLifestyle так же просто, как и определить область видимости в ваших двух. Execute
методы.
Я считаю, что управлять временем жизни объектов в клиентах двухуровневых приложений (клиент -> база данных) довольно сложно, поскольку трудно определить единицу работы для определенной области. Поскольку вы используете подход команда / обработчик + запрос / обработчик, все станет намного проще. Там не будет никакой единицы работы на клиенте. Просто на сервере. Ваш докладчик может зависеть только от нескольких IQueryHandler<TQuery, TResult>
а также ICommandHandler<TCommand>
интерфейсы и все готово. Запрос не меняет состояние, и команда должна быть атомарной операцией. Другими словами, единица работы требуется только в пределах границ исполняемой команды.