diff --git a/src/eQuantic.Core.Data.EntityFramework.MongoDb/Repository/UnitOfWork.cs b/src/eQuantic.Core.Data.EntityFramework.MongoDb/Repository/UnitOfWork.cs index 8e38967..9da4ad4 100644 --- a/src/eQuantic.Core.Data.EntityFramework.MongoDb/Repository/UnitOfWork.cs +++ b/src/eQuantic.Core.Data.EntityFramework.MongoDb/Repository/UnitOfWork.cs @@ -1,243 +1,19 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading; -using System.Threading.Tasks; +using eQuantic.Core.Data.EntityFramework.Repository; using eQuantic.Core.Data.Repository; -using eQuantic.Core.Data.Repository.Options; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; namespace eQuantic.Core.Data.EntityFramework.MongoDb.Repository; -public abstract class UnitOfWork : IQueryableUnitOfWork +/// +/// The MongoDB unit of work. All of the store-agnostic behaviour lives in +/// ; this only supplies the MongoDB . +/// +public abstract class UnitOfWork(IServiceProvider serviceProvider, DbContext context) + : EntityFrameworkUnitOfWork(serviceProvider, context) { - protected readonly IServiceProvider ServiceProvider; - /// - /// The context - /// - protected readonly DbContext Context; - - /// - /// The disposed - /// - protected bool Disposed; - - /// - /// Initializes a new instance of the class - /// - /// - /// The context - protected UnitOfWork(IServiceProvider serviceProvider, DbContext context) - { - ServiceProvider = serviceProvider; - Context = context; - } - - public int Commit() - { - return Context.SaveChanges(); - } - - public int CommitAndRefreshChanges() - { - var changes = 0; - var saveFailed = false; - - do - { - try - { - changes = Context.SaveChanges(); - - saveFailed = false; - } - catch (DbUpdateConcurrencyException ex) - { - saveFailed = true; - - ex.Entries.ToList() - .ForEach(entry => entry.OriginalValues.SetValues(entry.GetDatabaseValues()!)); - } - } while (saveFailed); - - return changes; - } - - public async Task CommitAndRefreshChangesAsync(CancellationToken cancellationToken = default) - { - var changes = 0; - var saveFailed = false; - - do - { - try - { - changes = await Context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); - - saveFailed = false; - } - catch (DbUpdateConcurrencyException ex) - { - saveFailed = true; - - ex.Entries.ToList() - .ForEach(entry => entry.OriginalValues.SetValues(entry.GetDatabaseValues()!)); - } - } while (saveFailed); - - return changes; - } - - public async Task CommitAsync(CancellationToken cancellationToken = default) - { - return await Context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); - } - - public int Commit(Action options) - { - return Commit(); - } - - public int CommitAndRefreshChanges(Action options) - { - return CommitAndRefreshChanges(); - } - - public Task CommitAndRefreshChangesAsync(Action options, CancellationToken cancellationToken = default) - { - return CommitAndRefreshChangesAsync(cancellationToken); - } - - public Task CommitAsync(Action options, CancellationToken cancellationToken = default) - { - return CommitAsync(cancellationToken); - } - - public void ApplyCurrentValues(TEntity original, TEntity current) where TEntity : class, IEntity - { - ((Set)InternalCreateSet()).ApplyCurrentValues(original, current); - } - - public void Attach(TEntity item) where TEntity : class, IEntity - { - ((Set)InternalCreateSet()).Attach(item); - } - - public void LoadCollection(TEntity item, - Expression>> navigationProperty, - Expression>? filter = null) where TEntity : class where TElement : class - { - if (filter != null) - { - Context.Entry(item).Collection(navigationProperty).Query().Where(filter).Load(); - } - else - { - Context.Entry(item).Collection(navigationProperty).Load(); - } - } - - public async Task LoadCollectionAsync(TEntity item, - Expression>> navigationProperty, - Expression>? filter = null) where TEntity : class where TElement : class - { - if (filter != null) - { - await Context.Entry(item).Collection(navigationProperty).Query().Where(filter).LoadAsync().ConfigureAwait(false); - } - else - { - await Context.Entry(item).Collection(navigationProperty).LoadAsync().ConfigureAwait(false); - } - } - - public void Reload(TEntity item) where TEntity : class - { - var entry = Context.Entry(item); - entry.CurrentValues.SetValues(entry.OriginalValues); - entry.Reload(); - } - - public void RollbackChanges() - { - // set all entities in change tracker - // as 'unchanged state' - Context?.ChangeTracker.Entries() - .ToList() - .ForEach(entry => entry.State = EntityState.Unchanged); - } - - public void SetModified(TEntity item) where TEntity : class - { - //this operation also attach item in object state manager - Context.Entry(item).State = EntityState.Modified; - } - - public virtual SaveOptions GetSaveOptions() - { - return new SaveOptions(); - } - - public virtual IRepository GetRepository() - where TEntity : class, IEntity - { - return ServiceProvider.GetRequiredService>(); - } - - public IAsyncRepository GetAsyncRepository() - where TEntity : class, IEntity - { - return ServiceProvider.GetRequiredService>(); - } - - public Data.Repository.ISet CreateSet() where TEntity : class, IEntity => InternalCreateSet(); - - public IQueryableRepository GetQueryableRepository() - where TEntity : class, IEntity - { - return ServiceProvider.GetRequiredService>(); - } - - public IAsyncQueryableRepository GetAsyncQueryableRepository() - where TEntity : class, IEntity - { - return ServiceProvider.GetRequiredService>(); - } - - internal DbContext GetDbContext() => Context; - - internal Data.Repository.ISet InternalCreateSet() where TEntity : class, IEntity => + protected override Data.Repository.ISet CreateSetCore() => new Set(ServiceProvider, Context); - - /// - /// Disposes this instance - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Disposes the disposing - /// - /// The disposing - protected virtual void Dispose(bool disposing) - { - if (Disposed) - { - return; - } - - if (disposing) - { - Context?.Dispose(); - } - - Disposed = true; - } } public abstract class UnitOfWork(IServiceProvider serviceProvider, TDbContext context) diff --git a/src/eQuantic.Core.Data.EntityFramework.MongoDb/eQuantic.Core.Data.EntityFramework.MongoDb.Net10.csproj b/src/eQuantic.Core.Data.EntityFramework.MongoDb/eQuantic.Core.Data.EntityFramework.MongoDb.Net10.csproj index 2a64b88..6fb0a41 100644 --- a/src/eQuantic.Core.Data.EntityFramework.MongoDb/eQuantic.Core.Data.EntityFramework.MongoDb.Net10.csproj +++ b/src/eQuantic.Core.Data.EntityFramework.MongoDb/eQuantic.Core.Data.EntityFramework.MongoDb.Net10.csproj @@ -4,7 +4,7 @@ Core Data library for Entity Framework and Mongo DB eQuantic.Core.Data.EntityFramework.MongoDb - 10.1.0.0 + 10.2.0.0 net10.0 eQuantic.Core.Data.EntityFramework.MongoDb eQuantic.Core.Data.EntityFramework.MongoDb @@ -12,8 +12,8 @@ Entity ignorant persistance with Repository Pattern for Entity Framework - 10.1.0.0 - 10.1.0.0 + 10.2.0.0 + 10.2.0.0 enable enable diff --git a/src/eQuantic.Core.Data.EntityFramework.MongoDb/eQuantic.Core.Data.EntityFramework.MongoDb.Net8.csproj b/src/eQuantic.Core.Data.EntityFramework.MongoDb/eQuantic.Core.Data.EntityFramework.MongoDb.Net8.csproj index 76c7c37..8f03163 100644 --- a/src/eQuantic.Core.Data.EntityFramework.MongoDb/eQuantic.Core.Data.EntityFramework.MongoDb.Net8.csproj +++ b/src/eQuantic.Core.Data.EntityFramework.MongoDb/eQuantic.Core.Data.EntityFramework.MongoDb.Net8.csproj @@ -4,7 +4,7 @@ Core Data library for Entity Framework and Mongo DB eQuantic.Core.Data.EntityFramework.MongoDb - 8.2.0.0 + 8.3.0.0 net8.0 eQuantic.Core.Data.EntityFramework.MongoDb eQuantic.Core.Data.EntityFramework.MongoDb @@ -12,8 +12,8 @@ Entity ignorant persistance with Repository Pattern for Entity Framework - 8.2.0.0 - 8.2.0.0 + 8.3.0.0 + 8.3.0.0 enable enable diff --git a/src/eQuantic.Core.Data.EntityFramework/Repository/EntityFrameworkUnitOfWork.cs b/src/eQuantic.Core.Data.EntityFramework/Repository/EntityFrameworkUnitOfWork.cs new file mode 100644 index 0000000..eaf8ecf --- /dev/null +++ b/src/eQuantic.Core.Data.EntityFramework/Repository/EntityFrameworkUnitOfWork.cs @@ -0,0 +1,249 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using eQuantic.Core.Data.Repository; +using eQuantic.Core.Data.Repository.Options; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace eQuantic.Core.Data.EntityFramework.Repository; + +/// +/// The shared, provider-agnostic implementation for the document +/// (non-relational) EF Core providers. It carries every unit-of-work member that does not depend on the +/// store; each provider supplies only its own entity set by overriding . +/// The relational providers keep their own unit of work (it also is a raw-SQL executor). +/// +public abstract class EntityFrameworkUnitOfWork : IQueryableUnitOfWork +{ + /// The service provider used to resolve repositories. + protected readonly IServiceProvider ServiceProvider; + + /// The underlying . + protected readonly DbContext Context; + + /// Whether this instance has been disposed. + protected bool Disposed; + + /// Initializes a new instance of the class. + /// The service provider. + /// The context. + protected EntityFrameworkUnitOfWork(IServiceProvider serviceProvider, DbContext context) + { + ServiceProvider = serviceProvider; + Context = context; + } + + /// Creates the provider-specific entity set for . + /// The entity type. + /// The provider's (a ). + protected abstract Data.Repository.ISet CreateSetCore() where TEntity : class, IEntity; + + public int Commit() + { + return Context.SaveChanges(); + } + + public int CommitAndRefreshChanges() + { + var changes = 0; + var saveFailed = false; + + do + { + try + { + changes = Context.SaveChanges(); + + saveFailed = false; + } + catch (DbUpdateConcurrencyException ex) + { + saveFailed = true; + + ex.Entries.ToList() + .ForEach(entry => entry.OriginalValues.SetValues(entry.GetDatabaseValues())); + } + } while (saveFailed); + + return changes; + } + + public async Task CommitAndRefreshChangesAsync(CancellationToken cancellationToken = default) + { + var changes = 0; + var saveFailed = false; + + do + { + try + { + changes = await Context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + + saveFailed = false; + } + catch (DbUpdateConcurrencyException ex) + { + saveFailed = true; + + ex.Entries.ToList() + .ForEach(entry => entry.OriginalValues.SetValues(entry.GetDatabaseValues())); + } + } while (saveFailed); + + return changes; + } + + public async Task CommitAsync(CancellationToken cancellationToken = default) + { + return await Context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + } + + public int Commit(Action options) + { + return Commit(); + } + + public int CommitAndRefreshChanges(Action options) + { + return CommitAndRefreshChanges(); + } + + public Task CommitAndRefreshChangesAsync(Action options, CancellationToken cancellationToken = default) + { + return CommitAndRefreshChangesAsync(cancellationToken); + } + + public Task CommitAsync(Action options, CancellationToken cancellationToken = default) + { + return CommitAsync(cancellationToken); + } + + public void ApplyCurrentValues(TEntity original, TEntity current) where TEntity : class, IEntity + { + ((SetBase)CreateSetCore()).ApplyCurrentValues(original, current); + } + + public void Attach(TEntity item) where TEntity : class, IEntity + { + ((SetBase)CreateSetCore()).Attach(item); + } + + public void LoadCollection(TEntity item, + Expression>> navigationProperty, + Expression> filter = null) where TEntity : class where TElement : class + { + if (filter != null) + { + Context.Entry(item).Collection(navigationProperty).Query().Where(filter).Load(); + } + else + { + Context.Entry(item).Collection(navigationProperty).Load(); + } + } + + public async Task LoadCollectionAsync(TEntity item, + Expression>> navigationProperty, + Expression> filter = null) where TEntity : class where TElement : class + { + if (filter != null) + { + await Context.Entry(item).Collection(navigationProperty).Query().Where(filter).LoadAsync().ConfigureAwait(false); + } + else + { + await Context.Entry(item).Collection(navigationProperty).LoadAsync().ConfigureAwait(false); + } + } + + public void Reload(TEntity item) where TEntity : class + { + var entry = Context.Entry(item); + entry.CurrentValues.SetValues(entry.OriginalValues); + entry.Reload(); + } + + public void RollbackChanges() + { + // set all entities in change tracker as 'unchanged state' + Context?.ChangeTracker.Entries() + .ToList() + .ForEach(entry => entry.State = EntityState.Unchanged); + } + + public void SetModified(TEntity item) where TEntity : class + { + // this operation also attaches item in the object state manager + Context.Entry(item).State = EntityState.Modified; + } + + public virtual SaveOptions GetSaveOptions() + { + return new SaveOptions(); + } + + public virtual IRepository GetRepository() + where TEntity : class, IEntity + { + return ServiceProvider.GetRequiredService>(); + } + + public IAsyncRepository GetAsyncRepository() + where TEntity : class, IEntity + { + return ServiceProvider.GetRequiredService>(); + } + + public Data.Repository.ISet CreateSet() where TEntity : class, IEntity => CreateSetCore(); + + public IQueryableRepository GetQueryableRepository() + where TEntity : class, IEntity + { + return ServiceProvider.GetRequiredService>(); + } + + public IAsyncQueryableRepository GetAsyncQueryableRepository() + where TEntity : class, IEntity + { + return ServiceProvider.GetRequiredService>(); + } + + internal DbContext GetDbContext() => Context; + + /// Disposes this instance. + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// Disposes the managed resources. + /// Whether to dispose managed resources. + protected virtual void Dispose(bool disposing) + { + if (Disposed) + { + return; + } + + if (disposing) + { + Context?.Dispose(); + } + + Disposed = true; + } +} + +/// +/// The strongly-typed that a document provider's unit of work +/// derives from over its concrete . +/// +/// The context type. +public abstract class EntityFrameworkUnitOfWork(IServiceProvider serviceProvider, TDbContext context) + : EntityFrameworkUnitOfWork(serviceProvider, context) + where TDbContext : DbContext; diff --git a/src/eQuantic.Core.Data.EntityFramework/eQuantic.Core.Data.EntityFramework.Net10.csproj b/src/eQuantic.Core.Data.EntityFramework/eQuantic.Core.Data.EntityFramework.Net10.csproj index ab20736..4c4e58c 100644 --- a/src/eQuantic.Core.Data.EntityFramework/eQuantic.Core.Data.EntityFramework.Net10.csproj +++ b/src/eQuantic.Core.Data.EntityFramework/eQuantic.Core.Data.EntityFramework.Net10.csproj @@ -4,7 +4,7 @@ Core Data library for Entity Framework eQuantic.Core.Data.EntityFramework - 10.1.0.0 + 10.2.0.0 net10.0 eQuantic.Core.Data.EntityFramework eQuantic.Core.Data.EntityFramework @@ -12,8 +12,8 @@ Entity ignorant persistance with Repository Pattern for Entity Framework - 10.1.0.0 - 10.1.0.0 + 10.2.0.0 + 10.2.0.0 diff --git a/src/eQuantic.Core.Data.EntityFramework/eQuantic.Core.Data.EntityFramework.Net8.csproj b/src/eQuantic.Core.Data.EntityFramework/eQuantic.Core.Data.EntityFramework.Net8.csproj index ac1500b..d6d78fc 100644 --- a/src/eQuantic.Core.Data.EntityFramework/eQuantic.Core.Data.EntityFramework.Net8.csproj +++ b/src/eQuantic.Core.Data.EntityFramework/eQuantic.Core.Data.EntityFramework.Net8.csproj @@ -4,7 +4,7 @@ Core Data library for Entity Framework eQuantic.Core.Data.EntityFramework - 8.2.0.0 + 8.3.0.0 net8.0 eQuantic.Core.Data.EntityFramework eQuantic.Core.Data.EntityFramework @@ -12,8 +12,8 @@ Entity ignorant persistance with Repository Pattern for Entity Framework - 8.2.0.0 - 8.2.0.0 + 8.3.0.0 + 8.3.0.0 diff --git a/src/eQuantic.Core.Data.EntityFramework/eQuantic.Core.Data.EntityFramework.csproj b/src/eQuantic.Core.Data.EntityFramework/eQuantic.Core.Data.EntityFramework.csproj index 776afc9..fe4806a 100644 --- a/src/eQuantic.Core.Data.EntityFramework/eQuantic.Core.Data.EntityFramework.csproj +++ b/src/eQuantic.Core.Data.EntityFramework/eQuantic.Core.Data.EntityFramework.csproj @@ -4,15 +4,15 @@ Core Data library for Entity Framework eQuantic.Core.Data.EntityFramework - 4.5.0.0 + 4.6.0.0 net8.0;net10.0 eQuantic.Core.Data.EntityFramework eQuantic.Core.Data.EntityFramework eQuantic;Core;Data;Library;Repository;Pattern Entity ignorant persistance with Repository Pattern for Entity Framework - 4.5.0.0 - 4.5.0.0 + 4.6.0.0 + 4.6.0.0