refactor(P5a): App DI 컴포지션 루트 + MainWindow 생성자 주입 (서비스로케이터 제거)
MVVM(P5b)의 선행조건 — 엔진/설정을 생성자 주입으로 가시화하고 서비스로케이터 결합을 제거. - App.xaml.cs: new DpapiSettingsStore()+CreateDefault() 직접 생성 → ServiceCollection.AddEverything2Everything() (P2 DI 재사용) 컴포지션 루트. Settings/Engine은 컨테이너 단일 싱글턴에서 해소(공유 보존). Generic Host 대신 ServiceProvider 사용 — Environment.Exit 진입점과 라이프사이클 충돌 회피(적대적 리뷰 반영). - MainWindow: ((App)Application.Current).Engine/.Settings 서비스로케이터 8곳 → 생성자 주입 _engine/_settings. App.ShowMainWindow/ShowConvertDialog가 주입. 헤드리스 테스트 가능성 토대. 70개 테스트 전부 그린, 빌드 0경고/0오류.
This commit is contained in:
parent
6ce4c429fe
commit
12bda2dcbf
2 changed files with 27 additions and 16 deletions
|
|
@ -2,19 +2,27 @@ using System.Windows;
|
|||
using Everything2Everything.App.Cli;
|
||||
using Everything2Everything.App.Views;
|
||||
using Everything2Everything.Core;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Everything2Everything.App;
|
||||
|
||||
public partial class App : Application
|
||||
{
|
||||
/// <summary>App·LlmProvider가 공유하는 설정 저장소 (키 저장 즉시 변환에 반영).</summary>
|
||||
public ISettingsStore Settings { get; } = new DpapiSettingsStore();
|
||||
private readonly IServiceProvider _services;
|
||||
|
||||
/// <summary>App·LlmProvider가 공유하는 설정 저장소 (키 저장 즉시 변환에 반영). DI 컨테이너 단일 싱글턴.</summary>
|
||||
public ISettingsStore Settings { get; }
|
||||
|
||||
public ConversionEngine Engine { get; }
|
||||
|
||||
public App()
|
||||
{
|
||||
Engine = Everything2EverythingBootstrap.CreateDefault(Settings);
|
||||
// 컴포지션 루트 — Core의 DI 확장(Scrutor 자동등록)으로 Provider/Registry/Engine/Settings를 구성.
|
||||
var services = new ServiceCollection();
|
||||
services.AddEverything2Everything();
|
||||
_services = services.BuildServiceProvider();
|
||||
Settings = _services.GetRequiredService<ISettingsStore>();
|
||||
Engine = _services.GetRequiredService<ConversionEngine>();
|
||||
}
|
||||
|
||||
protected override async void OnStartup(StartupEventArgs e)
|
||||
|
|
@ -68,14 +76,14 @@ public partial class App : Application
|
|||
|
||||
private void ShowMainWindow()
|
||||
{
|
||||
var window = new MainWindow();
|
||||
var window = new MainWindow(Engine, Settings);
|
||||
MainWindow = window;
|
||||
window.Show();
|
||||
}
|
||||
|
||||
private void ShowConvertDialog(IReadOnlyList<string> files)
|
||||
{
|
||||
var window = new Views.MainWindow(files);
|
||||
var window = new Views.MainWindow(Engine, Settings, files);
|
||||
MainWindow = window;
|
||||
window.Show();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ namespace Everything2Everything.App.Views;
|
|||
|
||||
public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
||||
{
|
||||
private readonly ConversionEngine _engine;
|
||||
private readonly ISettingsStore _settings;
|
||||
private readonly ObservableCollection<QueueItem> _activeQueue = new();
|
||||
private readonly ObservableCollection<DateGroup> _pastResults = new();
|
||||
private CancellationTokenSource? _cts;
|
||||
|
|
@ -28,10 +30,11 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
|||
public ICommand CloseCommand { get; }
|
||||
public ICommand RefreshCommand { get; }
|
||||
|
||||
public MainWindow() : this(null) { }
|
||||
|
||||
public MainWindow(IReadOnlyList<string>? initialFiles)
|
||||
public MainWindow(ConversionEngine engine, ISettingsStore settings, IReadOnlyList<string>? initialFiles = null)
|
||||
{
|
||||
_engine = engine;
|
||||
_settings = settings;
|
||||
|
||||
AddFilesCommand = new RelayCommand(_ => PickAndAddFiles());
|
||||
ProcessQueueCommand = new RelayCommand(_ => OnProcessQueueClick(this, new RoutedEventArgs()),
|
||||
_ => _activeQueue.Count > 0 && _cts is null);
|
||||
|
|
@ -66,7 +69,7 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
|||
|
||||
private async Task RefreshCapabilityStatusAsync()
|
||||
{
|
||||
var engine = ((App)Application.Current).Engine;
|
||||
var engine = _engine;
|
||||
var notReady = new List<string>();
|
||||
foreach (var p in engine.Providers.All)
|
||||
{
|
||||
|
|
@ -90,7 +93,7 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
|||
|
||||
private void OnSettingsClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var win = new SettingsWindow(((App)Application.Current).Settings) { Owner = this };
|
||||
var win = new SettingsWindow(_settings) { Owner = this };
|
||||
win.ShowDialog();
|
||||
_ = RefreshCapabilityStatusAsync();
|
||||
RefreshAvailableOutputFormats();
|
||||
|
|
@ -304,7 +307,7 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
|||
var targetLang = AiTargetLangBox?.Text?.Trim();
|
||||
opts.Ai.TargetLanguage = string.IsNullOrEmpty(targetLang) ? null : targetLang;
|
||||
|
||||
opts.VideoPreferGpu = ((App)Application.Current).Settings.Get("video.gpu") != "false";
|
||||
opts.VideoPreferGpu = _settings.Get("video.gpu") != "false";
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
|
@ -563,7 +566,7 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
|||
UpdateProcessQueueButton();
|
||||
ShowProcessingProgress(snapshot.Count);
|
||||
|
||||
var engine = ((App)Application.Current).Engine;
|
||||
var engine = _engine;
|
||||
var options = BuildOptions();
|
||||
|
||||
var reporter = new Progress<ConvertProgress>(p =>
|
||||
|
|
@ -780,7 +783,7 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
|||
{
|
||||
try
|
||||
{
|
||||
ContextMenuRegistrar.Register(((App)Application.Current).Engine);
|
||||
ContextMenuRegistrar.Register(_engine);
|
||||
MessageBox.Show(this,
|
||||
"컨텍스트 메뉴를 등록했습니다.\n파일 우클릭 → \"추가 옵션 표시\" 또는 \"JPEG로 빠른 변환/변환…\".",
|
||||
"Everything2Everything", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
|
|
@ -794,7 +797,7 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
|||
|
||||
private void OnDiagnoseClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var window = new DiagnoseWindow(((App)Application.Current).Engine) { Owner = this };
|
||||
var window = new DiagnoseWindow(_engine) { Owner = this };
|
||||
window.ShowDialog();
|
||||
}
|
||||
|
||||
|
|
@ -934,7 +937,7 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
|||
private LossClass? WorstLossForQueue(string outputExt)
|
||||
{
|
||||
if (_activeQueue.Count == 0) return null;
|
||||
var graph = ((App)Application.Current).Engine.Providers.Graph;
|
||||
var graph = _engine.Providers.Graph;
|
||||
LossClass? worst = null;
|
||||
foreach (var item in _activeQueue)
|
||||
{
|
||||
|
|
@ -971,7 +974,7 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
|||
{
|
||||
if (OutputFormatCombo is null) return;
|
||||
|
||||
var engine = ((App)Application.Current).Engine;
|
||||
var engine = _engine;
|
||||
IReadOnlyCollection<string> available;
|
||||
|
||||
if (_activeQueue.Count == 0)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue