Настройка приложения с контейнером 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:- Маппинги из этого проекта
- Фабрику сессий
- Сервис пользователя
IOrmConfig
ISessionProvider
-
AddDesktop
Является объединением необходимых регистраций из проекта QS.Project.Gtk:IGuiDispatcher
-
AddGuiInteracive
Регистрирует сервисы диалогов пользователя с GUI:IInteractiveMessage
IInteractiveQuestion
IInteractiveService
-
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);