Настройка приложения с контейнером DI - QualitySolution/QSProjects GitHub Wiki
-
Необходимо заменить все вызовы в UoW:
TryDelete -> Delete
TrySave -> Save -
Все вызовы методов статического класса
UnitOfWorkFactoryнеобходимо заменить на вызовы в переданный как зависимость объектIUnitOfWorkFactory, если это невозможно сделать в одном из легаси проектов, то можно вызвать вServicesConfig.UnitOfWorkFactory -
Все вызовы методов статического класса
OrmConfigнадо заменять на вызовы экземпляраIOrmConfig
-
В ServicesConfig собраны глобальные зависимости, которые все еще используются в легаси проектах. Эти зависимости резолвятся из Scope, который устанавливаается при первом подключении к бд. Чтобы он установился при подключении к бд необходимо вызвать метод AddStaticServicesConfig при регистрации зависимостей в контейнере
-
Сейчас доступны следующие статические регистрации в контейнере:
AddStaticServicesConfig()- глобальные сервисы для легаси проектов
AddStaticHistoryTracker()- включение мониторинга изменений, заменаHistoryMain.Enable() -
Такой подход с регистраций статических вызовов через контейнер является вынужденной мерой, так как в некоторых проектах которые работают полностью на контейрее, просто нет места где-бы можно было вызывать инициализацию старой статики
Специальный класс для помощи в регистрации действий со статикой в момент первого подключения к бд. Использовать только в легаси проектах в которых есть сложности с заменой на контейнер. Как использовать:
public static IServiceCollection AddSomethingStatic(this IServiceCollection services) {
services.AddSingleton<OnDatabaseInitialization>((provider) => {
//Получение зависимостей по необходимости
var someDependency = provider.GetRequiredService<SomeDependency>();
//Настройка статики
//ВАЖНО! В методе Config ниже, не должно быть обращений к бд!
StaticClass.Config(someDependency);
return new OnDatabaseInitialization();
});
return services;
}Реализованы некоторые методы для настройки приложения с помошью контейнера (описаны ниже).
Их можно заменить на свои реализации частично, или по примеру написать свои.
Если реализовывать свои методы на замену существующих, то необходимо учитывать какой класс регистрируется в контейнере, скорее всего этот класс запрашивается далее в других настройках.
Часто используемые методы можно объединить в один сервис, например:
//Объединение часто используемых настроек для подключения к бд
public static IServiceCollection AddDatabaseConnection(this IServiceCollection services)
{
services
//Свой проект с маппингами
.AddCoreDataNHibernate()
.AddDatabaseConnectionSettings()
.AddDatabaseConnectionString()
//Своя реализация настройки SQL
.AddSpatialSqlConfiguration()
.AddNHibernateConfiguration()
.AddDatabaseInfo()
.AddDatabaseSingletonSettings()
;
services.AddStaticServicesConfig();
return services;
}Ниже указаны обязательные методы для настройки подключения в бд используя контейнер.
-
Указания сборок с мапингами для Nhibernate
Можно вызвать в исполняемом проекте методservices.AddMappingAssemblies(), передав в него все сборки с мапингами Но логичнее в каждой сборке создать метод который регистрирует зависимости этого проекта и в нем уже вызватьservices.AddMappingAssemblies(Assembly.GetExecutingAssembly())
Регистрируется какIMappingAssembliesProvider -
Настройки подключения
Настройки для подключения организованы вIDatabaseConnectionSettingsи настроен биндинг из конфигурации на секцию DatabaseConnectionSettings (но можно указать свое имя). Эта настройка вызывается методомservices.AddDatabaseConnectionSettings() -
Строка подключения
Методservices.AddDatabaseConnectionString()берет настройки подключения изIDatabaseConnectionSettingsи регистрируетMySqlConnectionStringBuilder -
Настройка SQL
Методservices.AddSqlConfiguration()берет строку подключения изMySqlConnectionStringBuilder, указывает драйвер, диалект и регистрируетMySQLConfiguration -
Донастройка Nhibernate (ExposeConfiguration)
Методservices.AddDatabaseConfigurationExposer()указывает функцию для изменения конфигурации NHibernate в момент сборки конфигурации Nhibernate.
Это способ вызвать настройкуfluentConfig.ExposeConfiguration()
Регистрируется какIDatabaseConfigurationExposer -
Сборка конфигурации Nhibernate
Методservices.AddNHibernateConfiguration()делает:- добавляет все сборки с маппингами из зарегистрированных объектов
IMappingAssembliesProvider - использует SQL конфигурацию из
MySQLConfiguration - (опционально) применяет зарегистрированные конвенции
IConvention - (опционально) применяет метод донастройки конфигурации из
IDatabaseConfigurationExposer - (опционально) включает трекер изменений из
GlobalUowEventsTracker
И по итогу собирает конфигурацию Nhibernate в
Configuration - добавляет все сборки с маппингами из зарегистрированных объектов
-
Выбор фабрики UoW
Доступны 3 варианта фабрик UoW в зависимости от трекера изменений:-
services.AddNotTrackedUoW()- без отслеживания изменений -
services.AddTrackedUoW()- с отслеживанием изменений для проектов без GUI -
services.AddGuiTrackedUoW()- с отслеживанием изменений для проектов с GUI
Регистрации с трекерами так же регистрируют и трекеры:
GlobalUowEventsTracker,SingleUowEventsTrackerиITrackerActionInvokerдля вызова кода в GUI
В итоге регистрируется фабрикаIUnitOfWorkFactory -
-
Фабрика сессий Nhibernate
Методservices.AddSessionFactory()получает конфигурациюConfigurationи собирает из нее фабрику сессий регистрируя ее какISessionFactory.
В момент когда в первый раз будет запрошенISessionFactoryпроисходит первое подключение к БД, и до возвратаISessionFactoryбудут вызваны все регистрацииOnDatabaseInitialization, вызывая действия прописанные при их регистрации.
ВАЖНО!OnDatabaseInitializationне должны содержать код который обращается к бд, иначе будет зацикливание и StackOverflowException
-
AddDatabaseInfo
РегистрируетIDatabaseInfo, информацию о базе получает изMySqlConnectionStringBuilder -
AddUserService
РегистрируетIUserService, информацию загружает из бд используя логин изIDatabaseConnectionSettings -
AddCore
Является объединением необходимых регистраций из проекта QS.Project.Core:- Маппинги из этого проекта
- Фабрику сессий
- Сервис пользователя
IOrmConfigISessionProvider
-
AddDesktop
Является объединением необходимых регистраций из проекта QS.Project.Gtk:IGuiDispatcher
-
AddGuiInteracive
Регистрирует сервисы диалогов пользователя с GUI:IInteractiveMessageIInteractiveQuestionIInteractiveService
-
AddObjectValidatorWithGui
РегистрируетIValidatorс выводом GUI результатов валидации
Так как проекты с GUI подписываются на отслеживание изменений для обновления данных в GUI, необъодимо реализовать выполнение подписок на зменения в главном потоке приложения.
Чтобы можно было отслеживать изменения в проектах с GUI и при этом чтобы сам трекер не имел зависимостей от GUI, функция выполнения уведомлений об изменениях (ITrackerActionInvoker) вынесена в листенеры например IIUowPostUpdateEventListener (с реализацией в UowTracker), чтобы выбор того как выполнять требуемый код лежал на том классе который ждет эти изменения и знает как их правильно выполнять. Сам же глобальный трекер не лезет в сравнение потоков и UI.
В IUnitOfWork и его реализации добавлены асинхронные методы для работы с бд, методы Try* были удалены.
В валидатор ObjectValidator добавлена возможность установить ServiceProvider для того, чтобы можно было резолвить глобальные зависимости в методе валидации.
Устанавливается методом services.AddObjectValidatorWithGui(), либо самостоятельно в свойство ObjectValidator.ServiceProvider.
В каждом (по необходимости) проекте создавать статический класс DependencyInjection,
в котором делать методы добавления в контейнер того или иного функционала из этого проекта, например:
public static class DependencyInjection
{
public static IServiceCollection AddSomeService(this IServiceCollection services)
{
services.AddSingleton<ISingletonService, SingletonService>();
services.AddScoped<IScopedService, ScopedService>();
services.AddTransient<IServicePerDependency, ServicePerDependency>();
return services;
}
}Такие регистрации можно будет использовать в Autofac, но не наоборот. Регистрировать с помощью Autofac можно будет так:
var services = new ServiceCollection();
services.AddSomeService();
builder.Populate(services);