From 36a8efb05d8485be0226cb37ba077bf6c11779cb Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sun, 17 Jul 2022 15:15:55 +0200 Subject: [PATCH] Refactored service provider usage - Remove service provider from WinForms altogether - Moved service provider handling to processors --- I18N Commander/Processor/SectionProcessor.cs | 57 ++++++++++++++----- .../Processor/TextElementProcessor.cs | 10 +++- .../UI WinForms/Components/SectionTree.cs | 28 +++------ .../UI WinForms/Components/TextElements.cs | 18 ++---- I18N Commander/UI WinForms/Program.cs | 12 ++-- 5 files changed, 68 insertions(+), 57 deletions(-) diff --git a/I18N Commander/Processor/SectionProcessor.cs b/I18N Commander/Processor/SectionProcessor.cs index 7dd6799..7b291b7 100644 --- a/I18N Commander/Processor/SectionProcessor.cs +++ b/I18N Commander/Processor/SectionProcessor.cs @@ -1,6 +1,7 @@ using DataModel.Database; using DataModel.Database.Common; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; namespace Processor; @@ -9,16 +10,24 @@ public static class SectionProcessor /// /// Load one layer of the tree by using the specified depth: /// - public static IAsyncEnumerable
LoadLayer(DataContext db, int depth) + public static async Task> LoadLayer(int depth) { - return db.Sections.Where(n => n.Depth == depth).OrderBy(n => n.Id).AsAsyncEnumerable(); + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + var sections = await db.Sections.Where(n => n.Depth == depth).OrderBy(n => n.Id).ToListAsync(); + + // Ensure, that the database loaded the section's parent: + foreach (var section in sections) + await db.Entry(section).Reference(n => n.Parent).LoadAsync(); + + return sections; } /// /// Determine how deep the tree is. /// - public static async ValueTask GetDepth(DataContext db) + public static async ValueTask GetDepth() { + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); if(!await db.Sections.AnyAsync()) { return 0; @@ -30,8 +39,10 @@ public static class SectionProcessor /// /// Compute the new sections key and its depth, then store the section in the database. /// - public static async Task> AddSection(DataContext db, string text, string? parentKey) + public static async Task> AddSection(string text, string? parentKey) { + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + // Remove any whitespaces from the section name, regardless of how many e.g. spaces the user typed: var key = string.Join('_', text.Split(' ', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)).ToUpperInvariant(); @@ -97,24 +108,32 @@ public static class SectionProcessor } } - public static async Task RemoveSection(DataContext db, string selectedKey) + public static async Task RemoveSection(string selectedKey) + { + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + await SectionProcessor.RemoveOneSectionAndItsChildren(db, selectedKey); + await db.SaveChangesAsync(); + } + + private static async Task RemoveOneSectionAndItsChildren(DataContext db, string selectedKey) { // Remove the section from the database: var section2Delete = await db.Sections.FirstOrDefaultAsync(n => n.DataKey == selectedKey); if (section2Delete is null) throw new ArgumentException($"The section with key {selectedKey} does not exist in the database."); - + // Next, remove all children of the section, and the children's children, etc.: var children = await db.Sections.Where(n => n.Parent == section2Delete).ToListAsync(); foreach (var child in children) - await RemoveSection(db, child.DataKey); + await SectionProcessor.RemoveOneSectionAndItsChildren(db, child.DataKey); db.Sections.Remove(section2Delete); - await db.SaveChangesAsync(); } - - public static async Task NumberChildren(DataContext db, string selectedKey) + + public static async Task NumberChildren(string selectedKey) { + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + // Read the section from the database: var section = await db.Sections.FirstOrDefaultAsync(n => n.DataKey == selectedKey); if (section is null) @@ -123,8 +142,10 @@ public static class SectionProcessor return await db.Sections.CountAsync(n => n.Parent == section); } - public static async Task> RenameSection(DataContext db, string selectedNodeKey, string alteredName) + public static async Task> RenameSection(string selectedNodeKey, string alteredName) { + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + // Read the section from the database: var section = await db.Sections.FirstOrDefaultAsync(n => n.DataKey == selectedNodeKey); if (section is null) @@ -158,10 +179,18 @@ public static class SectionProcessor } } - public static Section GetSection(DataContext db, string sectionKey) => db.Sections.First(n => n.DataKey == sectionKey); - - public static async Task GetSectionPath(DataContext db, string sectionKey) + public static async Task
GetSection(string sectionKey) { + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + var section = await db.Sections.FirstAsync(n => n.DataKey == sectionKey); + await db.Entry(section).Reference(n => n.Parent).LoadAsync(); + + return section; + } + + public static async Task GetSectionPath(string sectionKey) + { + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); var section = await db.Sections.FirstAsync(n => n.DataKey == sectionKey); // Ensure, that the database loaded the section's parent: diff --git a/I18N Commander/Processor/TextElementProcessor.cs b/I18N Commander/Processor/TextElementProcessor.cs index 1d31532..82a4718 100644 --- a/I18N Commander/Processor/TextElementProcessor.cs +++ b/I18N Commander/Processor/TextElementProcessor.cs @@ -1,18 +1,22 @@ using DataModel.Database; using DataModel.Database.Common; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; namespace Processor; public static class TextElementProcessor { - public static Task> GetTextElements(DataContext db, Section section) + public static async Task> GetTextElements(Section section) { - return Task.FromResult(db.TextElements.Where(n => n.Section == section).AsAsyncEnumerable()); + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + return await db.TextElements.Where(n => n.Section == section).ToListAsync(); } - public static async Task> AddTextElement(DataContext db, string? currentSectionKey, string elementName) + public static async Task> AddTextElement(string? currentSectionKey, string elementName) { + await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); + if(string.IsNullOrWhiteSpace(currentSectionKey)) throw new ArgumentOutOfRangeException(nameof(currentSectionKey)); diff --git a/I18N Commander/UI WinForms/Components/SectionTree.cs b/I18N Commander/UI WinForms/Components/SectionTree.cs index 41fc3c3..308a38c 100644 --- a/I18N Commander/UI WinForms/Components/SectionTree.cs +++ b/I18N Commander/UI WinForms/Components/SectionTree.cs @@ -1,6 +1,4 @@ -using DataModel.Database.Common; -using Microsoft.Extensions.DependencyInjection; -using Processor; +using Processor; using UI_WinForms.Dialogs; using UI_WinForms.Resources; @@ -8,8 +6,6 @@ namespace UI_WinForms.Components; public partial class SectionTree : UserControl { - private readonly DataContext db; - public SectionTree() { this.InitializeComponent(); @@ -18,12 +14,6 @@ public partial class SectionTree : UserControl if(Program.SERVICE_PROVIDER is null) return; - // Get the DI context from the main form: - this.db = Program.SERVICE_PROVIDER.GetService()!; - - // Dispose of the context when the control is disposed: - this.Disposed += (_, _) => this.db.Dispose(); - // Create an image list from a resource: var imgList = new ImageList(); imgList.ImageSize = new Size(45, 45); @@ -46,7 +36,7 @@ public partial class SectionTree : UserControl var treeNodes = new Dictionary(); // Get the max. depth of the tree: - var maxDepth = await SectionProcessor.GetDepth(this.db); + var maxDepth = await SectionProcessor.GetDepth(); // Store nodes, where we cannot find the parents: var missingParents = new List(); @@ -54,7 +44,7 @@ public partial class SectionTree : UserControl // Populate the tree view out of the database, layer by layer: for (var i = 0; i <= maxDepth; i++) { - await foreach (var section in SectionProcessor.LoadLayer(this.db, i)) + foreach (var section in await SectionProcessor.LoadLayer(i)) { // Create the tree node: var node = new TreeNode @@ -139,7 +129,7 @@ public partial class SectionTree : UserControl var selectedNode = this.treeView.SelectedNode; // Add the new section to the database: - var addedSection = await SectionProcessor.AddSection(this.db, result.Text, addRootNode ? null : selectedNode?.Name); + var addedSection = await SectionProcessor.AddSection(result.Text, addRootNode ? null : selectedNode?.Name); addedSection.ProcessError(); if(!addedSection.Successful) @@ -173,7 +163,7 @@ public partial class SectionTree : UserControl // Get the number of children: // (notice, that the node's name is its key) - var numberChildren = await SectionProcessor.NumberChildren(this.db, selectedNode.Name); + var numberChildren = await SectionProcessor.NumberChildren(selectedNode.Name); // Ask the user, if he really wants to remove the section: if(MessageBox.Show(numberChildren > 0 ? $"Are you sure, you want to remove the section '{selectedNode.Text}', its {numberChildren} children and so on?" : $"Are you sure, you want to remove the section '{selectedNode.Text}'?", "Remove section", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.No) @@ -181,7 +171,7 @@ public partial class SectionTree : UserControl // Remove the section from the database: // (notice, that the node's name is its key) - await SectionProcessor.RemoveSection(this.db, selectedNode.Name); + await SectionProcessor.RemoveSection(selectedNode.Name); // Remove all nodes from the tree control: this.treeView.Nodes.Clear(); @@ -190,7 +180,7 @@ public partial class SectionTree : UserControl this.LoadNodes(this, EventArgs.Empty); } - private void treeView_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) + private async void treeView_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) { if(this.DesignMode) return; @@ -205,7 +195,7 @@ public partial class SectionTree : UserControl if (selectedNode is not null) { // Get the section from the database: - var section = SectionProcessor.GetSection(this.db, selectedNode.Name); + var section = await SectionProcessor.GetSection(selectedNode.Name); AppEvents.SectionChanged(section); } } @@ -235,7 +225,7 @@ public partial class SectionTree : UserControl return; // Rename the section: - var alteredSection = await SectionProcessor.RenameSection(this.db, selectedNode.Name, result.Text); + var alteredSection = await SectionProcessor.RenameSection(selectedNode.Name, result.Text); alteredSection.ProcessError(); if(!alteredSection.Successful) diff --git a/I18N Commander/UI WinForms/Components/TextElements.cs b/I18N Commander/UI WinForms/Components/TextElements.cs index 24ad44f..081fb1f 100644 --- a/I18N Commander/UI WinForms/Components/TextElements.cs +++ b/I18N Commander/UI WinForms/Components/TextElements.cs @@ -1,6 +1,4 @@ using DataModel.Database; -using DataModel.Database.Common; -using Microsoft.Extensions.DependencyInjection; using Processor; using UI_WinForms.Dialogs; @@ -8,8 +6,6 @@ namespace UI_WinForms.Components; public partial class TextElements : UserControl { - private readonly DataContext db; - private Section? currentSection; public TextElements() @@ -20,12 +16,6 @@ public partial class TextElements : UserControl if(Program.SERVICE_PROVIDER is null) return; - // Get the DI context from the main form: - this.db = Program.SERVICE_PROVIDER.GetService()!; - - // Dispose of the context when the control is disposed: - this.Disposed += (_, _) => this.db.Dispose(); - // When the section is changed, update this component: AppEvents.WhenSectionChanged += async (sender, section) => { @@ -36,7 +26,7 @@ public partial class TextElements : UserControl if (this.currentSection is null) return; - this.labelSectionPath.Text = await SectionProcessor.GetSectionPath(this.db, this.currentSection.DataKey); + this.labelSectionPath.Text = await SectionProcessor.GetSectionPath(this.currentSection.DataKey); this.LoadTextElements(); }; } @@ -48,11 +38,11 @@ public partial class TextElements : UserControl return; // Load the text elements: - var textElements = await TextElementProcessor.GetTextElements(this.db, this.currentSection); + var textElements = await TextElementProcessor.GetTextElements(this.currentSection); // Update the list: this.listTextElements.Items.Clear(); - await foreach (var textElement in textElements) + foreach (var textElement in textElements) { var item = new ListViewItem(textElement.Name) { @@ -79,7 +69,7 @@ public partial class TextElements : UserControl return; // Add the text element to the database into the current section: - var newTextElement = await TextElementProcessor.AddTextElement(this.db, this.currentSection?.DataKey, result.Text); + var newTextElement = await TextElementProcessor.AddTextElement(this.currentSection?.DataKey, result.Text); newTextElement.ProcessError(); if(!newTextElement.Successful) diff --git a/I18N Commander/UI WinForms/Program.cs b/I18N Commander/UI WinForms/Program.cs index a26755f..e871b6e 100644 --- a/I18N Commander/UI WinForms/Program.cs +++ b/I18N Commander/UI WinForms/Program.cs @@ -2,6 +2,7 @@ using DataModel; using DataModel.Database.Common; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Processor; namespace UI_WinForms; @@ -34,9 +35,6 @@ internal static class Program // builder.ConfigureServices((hostContext, serviceCollection) => { - // The main form: - serviceCollection.AddSingleton
(); - // The database: serviceCollection.AddDatabase(loader.DataFile, true); }); @@ -50,15 +48,15 @@ internal static class Program // Get a service provider: SERVICE_PROVIDER = scope.ServiceProvider; + // Set the service provider to the processor: + ProcessorMeta.ServiceProvider = SERVICE_PROVIDER; + // Apply database migrations: using (var database = SERVICE_PROVIDER.GetRequiredService()) Setup.PerformDataMigration(database).Wait(); - // Create the main window: - var mainWindow = SERVICE_PROVIDER.GetService
(); - // Start the app: - Application.Run(mainWindow); + Application.Run(new Main()); } } } \ No newline at end of file