diff --git a/src/Persistence/EntityFramework/EntityFrameworkContextBase.cs b/src/Persistence/EntityFramework/EntityFrameworkContextBase.cs index 50f92c7c4..ae3848467 100644 --- a/src/Persistence/EntityFramework/EntityFrameworkContextBase.cs +++ b/src/Persistence/EntityFramework/EntityFrameworkContextBase.cs @@ -2,6 +2,8 @@ // Licensed under the MIT License. See LICENSE file in the project root for full license information. // +using System.Threading; + namespace MUnique.OpenMU.Persistence.EntityFramework; using System.Collections; @@ -61,57 +63,43 @@ protected EntityFrameworkContextBase(DbContext context, IContextAwareRepositoryP protected IContextAwareRepositoryProvider RepositoryProvider { get; } /// - public bool SaveChanges() + public async ValueTask SaveChangesAsync(CancellationToken cancellationToken = default) { - using var l = this._lock.Lock(); + using var l = await this._lock.LockAsync(); // when we have a change publisher attached, we want to get the changed entries before accepting them. // Otherwise, we can accept them. var acceptChanges = true; + object? sender = null; + SavedChangesEventArgs? args = null; if (this._changeListener is { }) { - this.Context.SavedChanges += this.OnSavedChanges; + this.Context.SavedChanges += OnSavedChanges; acceptChanges = false; } try { - this.Context.SaveChanges(acceptChanges); + await this.Context.SaveChangesAsync(acceptChanges, cancellationToken).ConfigureAwait(false); + + if (args is not null) + { + await this.OnSavedChangesAsync(sender, args).ConfigureAwait(false); + } } finally { - this.Context.SavedChanges -= this.OnSavedChanges; + this.Context.SavedChanges -= OnSavedChanges; } return true; - } - - /// - public async ValueTask SaveChangesAsync() - { - using var l = await this._lock.LockAsync(); - - // when we have a change publisher attached, we want to get the changed entries before accepting them. - // Otherwise, we can accept them. - var acceptChanges = true; - if (this._changeListener is { }) + void OnSavedChanges(object? s, SavedChangesEventArgs e) { - this.Context.SavedChanges += this.OnSavedChanges; - acceptChanges = false; + sender = s; + args = e; } - - try - { - await this.Context.SaveChangesAsync(acceptChanges).ConfigureAwait(false); - } - finally - { - this.Context.SavedChanges -= this.OnSavedChanges; - } - - return true; } /// @@ -270,7 +258,6 @@ protected virtual void Dispose(bool dispose) return; } - this.Context.SavedChanges -= this.OnSavedChanges; this.Context.Dispose(); } @@ -319,7 +306,7 @@ private void ForEachAggregate(object obj, Action action) } [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD100:Avoid async void methods", Justification = "Catching all Exceptions.")] - private async void OnSavedChanges(object? sender, SavedChangesEventArgs e) + private async ValueTask OnSavedChangesAsync(object? sender, SavedChangesEventArgs e) { try { diff --git a/src/Persistence/IContext.cs b/src/Persistence/IContext.cs index 15a7658f9..58386b67d 100644 --- a/src/Persistence/IContext.cs +++ b/src/Persistence/IContext.cs @@ -5,6 +5,7 @@ namespace MUnique.OpenMU.Persistence; using System.Collections; +using System.Threading; /// /// The context for repository actions. @@ -19,14 +20,11 @@ public interface IContext : IDisposable /// /// Saves the changes of the context. /// - /// True, if the saving was successful; false, otherwise. - bool SaveChanges(); - - /// - /// Saves the changes of the context. - /// - /// True, if the saving was successful; false, otherwise. - ValueTask SaveChangesAsync(); + /// The cancellation token. + /// + /// True, if the saving was successful; false, otherwise. + /// + ValueTask SaveChangesAsync(CancellationToken cancellationToken = default); /// /// Detaches the specified item from the context, if required. @@ -35,7 +33,7 @@ public interface IContext : IDisposable /// The item which should be detached. /// true, if the object was persisted. /// - /// When calling this method, be sure to clear a back reference property before. Otherwise you might detach more than you intended. + /// When calling this method, be sure to clear a back reference property before. Otherwise, you might detach more than you intended. /// bool Detach(object item); @@ -45,7 +43,7 @@ public interface IContext : IDisposable /// /// The item which should be attached. /// - /// When calling this method, be sure to clear a previous back reference property before. Otherwise you might attach more than you intended. + /// When calling this method, be sure to clear a previous back reference property before. Otherwise, you might attach more than you intended. /// void Attach(object item); diff --git a/src/Persistence/InMemory/InMemoryContext.cs b/src/Persistence/InMemory/InMemoryContext.cs index 4665375a1..7670c55f6 100644 --- a/src/Persistence/InMemory/InMemoryContext.cs +++ b/src/Persistence/InMemory/InMemoryContext.cs @@ -5,6 +5,7 @@ namespace MUnique.OpenMU.Persistence.InMemory; using System.Collections; +using System.Threading; using Nito.AsyncEx.Synchronous; /// @@ -55,7 +56,7 @@ public bool SaveChanges() } /// - public async ValueTask SaveChangesAsync() + public async ValueTask SaveChangesAsync(CancellationToken cancellationToken = default) { var result = this.SaveChanges(); if (result) diff --git a/src/Startup/Program.cs b/src/Startup/Program.cs index 1dbe7b468..93c1067c9 100644 --- a/src/Startup/Program.cs +++ b/src/Startup/Program.cs @@ -340,7 +340,7 @@ private ICollection PlugInConfigurationsFactory(IServicePro if (typesWithMissingCustomConfigs.Any()) { typesWithMissingCustomConfigs.ForEach(c => this.CreateDefaultPlugInConfiguration(typesWithCustomConfig[c.TypeId]!, c, referenceHandler)); - context.SaveChanges(); + _ = context.SaveChangesAsync().AsTask().WaitAndUnwrapException(); } var typesWithMissingConfigs = pluginManager.KnownPlugInTypes.Where(t => configs.All(c => c.TypeId != t.GUID)).ToList(); @@ -378,7 +378,7 @@ private IEnumerable CreateMissingPlugInConfigurations(IEnum yield return plugInConfiguration; } - saveContext.SaveChanges(); + _ = saveContext.SaveChangesAsync().AsTask().WaitAndUnwrapException(); } private void CreateDefaultPlugInConfiguration(Type plugInType, PlugInConfiguration plugInConfiguration, ReferenceHandler referenceHandler)