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> интерфейсы и все готово. Запрос не меняет состояние, и команда должна быть атомарной операцией. Другими словами, единица работы требуется только в пределах границ исполняемой команды.

Другие вопросы по тегам