2022. 8. 19. 00:00ㆍMAUI
dotnet dependency injection 시스템을 사용하면 아주 간단하게 의존성을 주입하고 활용할 수 있다.
그러나 이런 시스템을 사용해도 각각의 class 들을 등록해야 하는 과정은 필요하다.
class 들이 얼마 없는 간단한 program (app) 이라면 괜찮겠지만 엄청나게 많은 class 들의 존재하는
아주 복잡하고 큰 program 이라면 class 를 등록해야 하는 과정 자체가 하나의 일이 된다.
이런 과정을 도와주는 library 로 Scrutor 이 있다.
Scrutor 는 autoscan 을 통해 필요한 class 들을 자동으로 등록할 수 있다.
Scrutor 를 MAUI 에서 활용하는 방법에 대해 알아보자
Visual Studio 에서 개발자 명령 프롬프트로 이동 한후. {프로젝트명}.csproj 파일이 있는 폴더로 이동한다.
아래 명령 실행
dotnet add package Scrutor
InjectableServices 폴더 추가 --> Injectables.cs 파일 추가
namespace MauiApp1.InjectableServices;
public interface IInjectableService { }
public interface ITransientService : IInjectableService { }
public interface IScopedService : IInjectableService { }
public interface ISingletonService : IInjectableService { }
빈 interface 를 만들어 LifeTime 별로 구분 할 수 있도록 3가지로 분류한다.
MauiProgram.cs
using MauiApp1.InjectableServices;
namespace MauiApp1;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
//builder.Services.AddTransient<IHelloWorldClass, HelloWorldClass>();
//builder.Services.AddTransient<IncrementCounterViewModel>();
//builder.Services.AddTransient<IncrementCounterPage>();
builder.Services.Scan(scan => scan
.FromAssemblyOf<ITransientService>()
.AddClasses(classes => classes.AssignableTo<ITransientService>())
.AsSelfWithInterfaces()
.WithTransientLifetime()
.AddClasses(classes => classes.AssignableTo<IScopedService>())
.AsSelfWithInterfaces()
.WithScopedLifetime()
.AddClasses(classes => classes.AssignableTo<ISingletonService>())
.AsSelfWithInterfaces()
.WithSingletonLifetime()
);
return builder.Build();
}
}
ITransientService 를 정의한 Assembly를 자동으로 찾는다.
ITransientService 를 상속 받은 모든 type 은 Transient LifeTime 을 갖는다.
또한 Dependency Injection 시스템에 자동으로 등록 되어 어디서든 사용가능 하게된다.
// 이형태와
services.AddTransient<ITransientService, TestService>();
// 이형태가 둘다 등록된다.
services.AddTransient<TestService>();
IScopedService 은 Scoped LifeTime, ISingletonService 는 Sigleton LifeTime 을 갖는다.
이제 이전 강좌에서 사용했던 IncrementCounterPage 및 ViewModel 을 Scrutor 를 이용하여 수정해보자
HelloWorldClass.cs
using MauiApp1.InjectableServices;
namespace MauiApp1.MVVM.AutoScan;
public interface IHelloWorldClass:ITransientService
{
string Execute();
}
public class HelloWorldClass : IHelloWorldClass
{
public string Execute() => $"{DateTime.Now} Hello World!";
}
IHelloWorldClass 가 ITransientService 를 상속받도록 수정 한다.
IncrementCounterPage.xaml.cs
using MauiApp1.InjectableServices;
namespace MauiApp1.MVVM.AutoScan;
public partial class IncrementCounterPage : ContentPage, ITransientService
{
public IncrementCounterPage(IncrementCounterViewModel incrementCounterViewModel)
{
InitializeComponent();
BindingContext = incrementCounterViewModel;
}
}
IncrementCounterPage가 ITransientService 를 상속받도록 수정 한다.
IncrementCounterViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using MauiApp1.InjectableServices;
namespace MauiApp1.MVVM.AutoScan;
public partial class IncrementCounterViewModel : ObservableObject, ITransientService
{
public IncrementCounterViewModel(IHelloWorldClass helloWorld)
{
_helloWorld = helloWorld;
}
[ObservableProperty]
private int _counter;
[ObservableProperty]
private string _message;
private readonly IHelloWorldClass _helloWorld;
[RelayCommand]
private void IncrementCounter() => Counter++;
[RelayCommand]
private void ChangeMessage()
{
Message = _helloWorld.Execute();
}
}
IncrementCounterViewModel이 ITransientService 를 상속받도록 수정 한다.
또한 위에 Scrutor.Scan 한 부분에서 Assembly 를 자동으로 scan 하여 처리 하는 방법도 있다.
dll 이 저장되는 위치를 모두 scan 한다.
scan 한 dll 을 모두 load 한다.
load 된 dll 에서 ITransientService,IScopedService,ISingletonService 를 찾아 등록한다.
이럴경우 아래와 같이 사용한다.
Helper/AssemblyHelper.cs
using System.Reflection;
using System.Runtime.Loader;
namespace MauiApp1.Helper;
public class AssemblyHelper
{
public static List<Assembly> GetAllAssemblies
(SearchOption searchOption = SearchOption.TopDirectoryOnly)
{
List<Assembly> assemblies = new List<Assembly>();
var baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
var assemblyFiles = Directory.GetFiles(baseDirectory
, "*.dll"
, searchOption);
var path = Directory.GetFiles(baseDirectory);
foreach (string assemblyPath in assemblyFiles)
{
try
{
var assembly = AssemblyLoadContext
.Default
.LoadFromAssemblyPath(assemblyPath);
if (assemblies.Find(a => a == assembly) != null)
continue;
assemblies.Add(assembly);
}
catch (Exception ex)
{
//Debug.WriteLine(ex.ToString());
}
}
return assemblies;
}
}
MauiProgram.cs
using MauiApp1.InjectableServices;
namespace MauiApp1;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
builder.Services.Scan(scan => scan
//.FromAssemblyOf<ITransientService>()
.FromAssemblies(Helper.AssemblyHelper.GetAllAssemblies(SearchOption.AllDirectories))
.AddClasses(classes => classes.AssignableTo<ITransientService>())
.AsSelfWithInterfaces()
.WithTransientLifetime()
.AddClasses(classes => classes.AssignableTo<IScopedService>())
.AsSelfWithInterfaces()
.WithScopedLifetime()
.AddClasses(classes => classes.AssignableTo<ISingletonService>())
.AsSelfWithInterfaces()
.WithSingletonLifetime()
);
return builder.Build();
}
}
FromAssemblies (AssemblyHelper.GetAllAssemblies(모든폴더확인)) 을 통해 처리 한다.
그러나 문제는 dll 을 load 하는 초기에 좀 오랜 시간이 걸리므로 이부분 참고하자
관련영상
'MAUI' 카테고리의 다른 글
.NET MAUI - CommunityToolkit.Maui and Alerts (0) | 2022.08.23 |
---|---|
.NET MAUI - Xaml Compilation (for HighPerformance) (0) | 2022.08.22 |
.NET MAUI - MVVM Dependency Injection 1/2 (0) | 2022.08.18 |
.NET MAUI - MVVM SourceGenerator (0) | 2022.08.17 |
.NET MAUI - MVVM Messenger (0) | 2022.08.16 |