Skip to content

Commit

Permalink
Rolled back UnitOfWork Async operations and simplified the Transactio…
Browse files Browse the repository at this point in the history
…nScope wrapper. TransactionScope does not support using async so we must keep it relatively simple
  • Loading branch information
jasonmwebb-lv committed Jun 3, 2024
1 parent 4390a44 commit 980ddc0
Show file tree
Hide file tree
Showing 48 changed files with 285 additions and 784 deletions.
2 changes: 1 addition & 1 deletion Build/Build.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ protected override void OnBuildInitialized()
{
Log.Information("Generating NuGet packages for projects in solution");
int commitNum = 0;
string NuGetVersionCustom = "2.0.0.880";
string NuGetVersionCustom = "2.0.0.881";


//if it's not a tagged release - append the commit number to the package version
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ public abstract class AuditableDbContext : RCommonDbContext
private readonly ICurrentUser _currentUser;
private readonly ISystemTime _systemTime;

public AuditableDbContext(DbContextOptions options, ICurrentUser currentUser, ISystemTime systemTime,
IEntityEventTracker eventTracker)
: base(options, eventTracker)
public AuditableDbContext(DbContextOptions options, ICurrentUser currentUser, ISystemTime systemTime)
: base(options)
{
_currentUser = currentUser;
this._systemTime = systemTime;
Expand Down Expand Up @@ -51,10 +50,5 @@ public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, Cance

return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}

public override async Task PersistChangesAsync()
{
await base.PersistChangesAsync();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@ namespace HR.LeaveManagement.Persistence
public class LeaveManagementDbContext : AuditableDbContext
{

public LeaveManagementDbContext(DbContextOptions<LeaveManagementDbContext> options, ICurrentUser currentUser, ISystemTime systemTime,
IEntityEventTracker eventTracker)
: base(options, currentUser, systemTime, eventTracker)
public LeaveManagementDbContext(DbContextOptions<LeaveManagementDbContext> options, ICurrentUser currentUser, ISystemTime systemTime)
: base(options, currentUser, systemTime)
{
}

Expand Down
26 changes: 3 additions & 23 deletions Src/RCommon.Dapper/Crud/DapperRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ public class DapperRepository<TEntity> : SqlRepositoryBase<TEntity>
where TEntity : class, IBusinessEntity
{

public DapperRepository(IDataStoreRegistry dataStoreRegistry, IDataStoreEnlistmentProvider dataStoreEnlistmentProvider,
ILoggerFactory logger, IUnitOfWorkManager unitOfWorkManager, IEntityEventTracker eventTracker,
public DapperRepository(IDataStoreFactory dataStoreFactory,
ILoggerFactory logger, IEntityEventTracker eventTracker,
IOptions<DefaultDataStoreOptions> defaultDataStoreOptions)
: base(dataStoreRegistry, dataStoreEnlistmentProvider, logger, unitOfWorkManager, eventTracker, defaultDataStoreOptions)
: base(dataStoreFactory, logger, eventTracker, defaultDataStoreOptions)
{
Logger = logger.CreateLogger(GetType().Name);
}
Expand All @@ -48,7 +48,6 @@ public override async Task AddAsync(TEntity entity, CancellationToken token = de
await db.InsertAsync(entity, cancellationToken: token);
entity.AddLocalEvent(new EntityCreatedEvent<TEntity>(entity));
EventTracker.AddEntity(entity);
await DispatchEvents();

}
catch (ApplicationException exception)
Expand Down Expand Up @@ -82,7 +81,6 @@ public override async Task DeleteAsync(TEntity entity, CancellationToken token =
await db.DeleteAsync(entity, cancellationToken: token);
entity.AddLocalEvent(new EntityDeletedEvent<TEntity>(entity));
EventTracker.AddEntity(entity);
await DispatchEvents();
}
catch (ApplicationException exception)
{
Expand Down Expand Up @@ -117,7 +115,6 @@ public override async Task UpdateAsync(TEntity entity, CancellationToken token =
await db.UpdateAsync(entity, cancellationToken: token);
entity.AddLocalEvent(new EntityUpdatedEvent<TEntity>(entity));
EventTracker.AddEntity(entity);
await DispatchEvents();
}
catch (ApplicationException exception)
{
Expand Down Expand Up @@ -316,23 +313,6 @@ public override async Task<bool> AnyAsync(ISpecification<TEntity> specification,
return await AnyAsync(specification.Predicate, token);
}

protected async Task DispatchEvents()
{
try
{
if (!UnitOfWorkManager.IsUnitOfWorkActive)
{
Guard.Against<NullReferenceException>(DataStore == null, "DataStore is null");
await DataStore.PersistChangesAsync(); // This dispatches the events
}
}
catch (ApplicationException exception)
{
Logger.LogError(exception, "Error in {0}.DispatchEvents while executing on the Context.", GetType().FullName);
throw;
}
}


}
}
15 changes: 9 additions & 6 deletions Src/RCommon.Dapper/DapperPersistenceBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Microsoft.Extensions.DependencyInjection;
using RCommon.Persistence.Dapper.Crud;
using RCommon.Persistence.Crud;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace RCommon
{
Expand Down Expand Up @@ -37,13 +38,15 @@ public IDapperBuilder AddDbConnection<TDbConnection>(string dataStoreName, Actio
Guard.Against<UnsupportedDataStoreException>(dataStoreName.IsNullOrEmpty(), "You must set a name for the Data Store");
Guard.Against<RDbConnectionException>(options == null, "You must configure the options for the RDbConnection for it to be useful");

if (!StaticDataStore.DataStores.TryAdd(dataStoreName, typeof(TDbConnection)))
{
throw new UnsupportedDataStoreException($"The StaticDataStore refused to add the new DataStore name: {dataStoreName} of type: {typeof(TDbConnection).AssemblyQualifiedName}");
}

var dbContext = typeof(TDbConnection).AssemblyQualifiedName;
this._services.AddTransient(Type.GetType(dbContext), Type.GetType(dbContext));
//this._services.AddTransient(Type.GetType(dbContext), Type.GetType(dbContext));

this._services.TryAddTransient<IDataStoreFactory, DataStoreFactory>();
this._services.TryAddTransient(Type.GetType(dbContext));
this._services.Configure<DataStoreFactoryOptions>(options => options.Register<TDbConnection>(dataStoreName));

//var dbContext = typeof(TDbConnection).AssemblyQualifiedName;
//this._services.AddTransient(Type.GetType(dbContext), Type.GetType(dbContext));
this._services.Configure(options);

return this;
Expand Down
36 changes: 25 additions & 11 deletions Src/RCommon.EfCore/Crud/EFCoreRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class EFCoreRepository<TEntity> : GraphRepositoryBase<TEntity>
private IQueryable<TEntity> _repositoryQuery;
private bool _tracking;
private IIncludableQueryable<TEntity, object> _includableQueryable;
private readonly IDataStoreFactory _dataStoreFactory;



Expand All @@ -44,13 +45,29 @@ public class EFCoreRepository<TEntity> : GraphRepositoryBase<TEntity>
/// <param name="dbContext">The <see cref="TDataStore"/> is injected with scoped lifetime so it will always return the same instance of the <see cref="DbContext"/>
/// througout the HTTP request or the scope of the thread.</param>
/// <param name="logger">Logger used throughout the application.</param>
public EFCoreRepository(IDataStoreRegistry dataStoreRegistry, IDataStoreEnlistmentProvider dataStoreEnlistmentProvider,
ILoggerFactory logger, IUnitOfWorkManager unitOfWorkManager, IEntityEventTracker eventTracker,
public EFCoreRepository(IDataStoreFactory dataStoreFactory,
ILoggerFactory logger, IEntityEventTracker eventTracker,
IOptions<DefaultDataStoreOptions> defaultDataStoreOptions)
: base(dataStoreRegistry, dataStoreEnlistmentProvider, unitOfWorkManager, eventTracker, defaultDataStoreOptions)
: base(dataStoreFactory, eventTracker, defaultDataStoreOptions)
{
if (logger is null)
{
throw new ArgumentNullException(nameof(logger));
}

if (eventTracker is null)
{
throw new ArgumentNullException(nameof(eventTracker));
}

if (defaultDataStoreOptions is null)
{
throw new ArgumentNullException(nameof(defaultDataStoreOptions));
}

Logger = logger.CreateLogger(GetType().Name);
_tracking = true;
_dataStoreFactory = dataStoreFactory ?? throw new ArgumentNullException(nameof(dataStoreFactory));
}

protected DbSet<TEntity> ObjectSet
Expand Down Expand Up @@ -231,7 +248,7 @@ protected internal RCommonDbContext ObjectContext
{
get
{
return DataStoreRegistry.GetDataStore<RCommonDbContext>(DataStoreName);
return this._dataStoreFactory.Resolve<RCommonDbContext>(this.DataStoreName);
}
}

Expand All @@ -240,15 +257,12 @@ private async Task<int> SaveAsync(CancellationToken token = default)
int affected = 0;
try
{
if (UnitOfWorkManager.CurrentUnitOfWork == null)
{
affected = await ObjectContext.SaveChangesAsync(true, token); // This will dispatch the local events
}
affected = await ObjectContext.SaveChangesAsync(true, token);
}
catch (ApplicationException exception)
catch (ApplicationException ex)
{
Logger.LogError(exception, "Error in {0}.SaveAsync while executing on the Context.", GetType().FullName);
throw;
var persistEx = new PersistenceException($"Error in {this.GetGenericTypeName()}.SaveAsync while executing on the Context.", ex);
throw persistEx;
}

return affected;
Expand Down
12 changes: 7 additions & 5 deletions Src/RCommon.EfCore/EFCorePerisistenceBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using RCommon.Persistence;
using RCommon.Persistence.Crud;
using RCommon.Persistence.EFCore;
Expand Down Expand Up @@ -34,11 +37,10 @@ public IEFCorePersistenceBuilder AddDbContext<TDbContext>(string dataStoreName,
where TDbContext : RCommonDbContext
{
Guard.Against<UnsupportedDataStoreException>(dataStoreName.IsNullOrEmpty(), "You must set a name for the Data Store");

if (!StaticDataStore.DataStores.TryAdd(dataStoreName, typeof(TDbContext)))
{
throw new UnsupportedDataStoreException($"The StaticDataStore refused to add the new DataStore name: {dataStoreName} of type: {typeof(TDbContext).AssemblyQualifiedName}");
}

this._services.TryAddTransient<IDataStoreFactory, DataStoreFactory>();
//this._services.TryAddTransient<TDbContext>();
this._services.Configure<DataStoreFactoryOptions>(options => options.Register<TDbContext>(dataStoreName));
this._services.AddDbContext<TDbContext>(options, ServiceLifetime.Scoped);

return this;
Expand Down
26 changes: 1 addition & 25 deletions Src/RCommon.EfCore/RCommonDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,46 +15,22 @@ namespace RCommon.Persistence.EFCore
{
public abstract class RCommonDbContext : DbContext, IDataStore
{
private readonly IEntityEventTracker _entityEventTracker;


public RCommonDbContext(DbContextOptions options, IEntityEventTracker entityEventTracker)
public RCommonDbContext(DbContextOptions options)
: base(options)
{
if (options is null)
{
throw new ArgumentNullException(nameof(options));
}

this._entityEventTracker = entityEventTracker ?? throw new ArgumentNullException(nameof(entityEventTracker));
}

public RCommonDbContext(DbContextOptions options)
: base(options)
{
if (options is null)
{
throw new ArgumentNullException(nameof(options));
}
}



public DbConnection GetDbConnection()
{
return base.Database.GetDbConnection();
}

public virtual async Task PersistChangesAsync()
{
await this.SaveChangesAsync(true);
}


public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
{
await this._entityEventTracker.EmitTransactionalEventsAsync();
return await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
}
}
42 changes: 21 additions & 21 deletions Src/RCommon.Linq2Db/Crud/Linq2DbRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,40 @@ public class Linq2DbRepository<TEntity> : LinqRepositoryBase<TEntity>
{
private IQueryable<TEntity> _repositoryQuery;
private ILoadWithQueryable<TEntity, object> _includableQueryable;
private readonly IDataStoreFactory _dataStoreFactory;

public Linq2DbRepository(IDataStoreRegistry dataStoreRegistry, IDataStoreEnlistmentProvider dataStoreEnlistmentProvider,
ILoggerFactory logger, IUnitOfWorkManager unitOfWorkManager, IEntityEventTracker eventTracker,
public Linq2DbRepository(IDataStoreFactory dataStoreFactory,
ILoggerFactory logger, IEntityEventTracker eventTracker,
IOptions<DefaultDataStoreOptions> defaultDataStoreOptions)
: base(dataStoreRegistry, dataStoreEnlistmentProvider, unitOfWorkManager, eventTracker, defaultDataStoreOptions)
: base(dataStoreFactory, eventTracker, defaultDataStoreOptions)
{
if (logger is null)
{
throw new ArgumentNullException(nameof(logger));
}

if (eventTracker is null)
{
throw new ArgumentNullException(nameof(eventTracker));
}

if (defaultDataStoreOptions is null)
{
throw new ArgumentNullException(nameof(defaultDataStoreOptions));
}

Logger = logger.CreateLogger(GetType().Name);
_repositoryQuery = null;
_includableQueryable = null;
_dataStoreFactory = dataStoreFactory ?? throw new ArgumentNullException(nameof(dataStoreFactory));
}


protected internal RCommonDataConnection DataConnection
{
get
{
return DataStoreRegistry.GetDataStore<RCommonDataConnection>(DataStoreName);
return this._dataStoreFactory.Resolve<RCommonDataConnection>(this.DataStoreName);
}
}

Expand Down Expand Up @@ -214,22 +231,5 @@ public async override Task UpdateAsync(TEntity entity, CancellationToken token =
entity.AddLocalEvent(new EntityUpdatedEvent<TEntity>(entity));
EventTracker.AddEntity(entity);
}

protected async Task DispatchEvents()
{
try
{
if (!UnitOfWorkManager.IsUnitOfWorkActive)
{
Guard.Against<NullReferenceException>(DataConnection == null, "DataConnection is null");
await DataConnection.PersistChangesAsync(); // This dispatches the events
}
}
catch (ApplicationException exception)
{
Logger.LogError(exception, "Error in {0}.DispatchEvents while executing on the Context.", GetType().FullName);
throw;
}
}
}
}
8 changes: 4 additions & 4 deletions Src/RCommon.Linq2Db/Linq2DbPersistenceBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using System.Threading.Tasks;
using RCommon.Persistence.Linq2Db.Crud;
using RCommon.Persistence.Crud;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace RCommon.Persistence.Linq2Db
{
Expand All @@ -37,10 +38,9 @@ public ILinq2DbPersistenceBuilder AddDataConnection<TDataConnection>(string data
Guard.Against<UnsupportedDataStoreException>(dataStoreName.IsNullOrEmpty(), "You must set a name for the Data Store");
Guard.Against<UnsupportedDataStoreException>(options == null, "You must set options to a value in order for them to be useful");

if (!StaticDataStore.DataStores.TryAdd(dataStoreName, typeof(TDataConnection)))
{
throw new UnsupportedDataStoreException($"The StaticDataStore refused to add the new DataStore name: {dataStoreName} of type: {typeof(TDataConnection).AssemblyQualifiedName}");
}
this._services.TryAddTransient<IDataStoreFactory, DataStoreFactory>();
this._services.TryAddTransient<TDataConnection>();
this._services.Configure<DataStoreFactoryOptions>(options => options.Register<TDataConnection>(dataStoreName));

_services.AddLinqToDBContext<TDataConnection>(options);
return this;
Expand Down
7 changes: 0 additions & 7 deletions Src/RCommon.Linq2Db/RCommonDataConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ public class RCommonDataConnection : DataConnection, IDataStore
public RCommonDataConnection(IEntityEventTracker eventTracker, DataOptions linq2DbOptions)
:base(linq2DbOptions)
{
var options = linq2DbOptions ?? throw new ArgumentNullException(nameof(linq2DbOptions));
_eventTracker = eventTracker ?? throw new ArgumentNullException(nameof(eventTracker));

}

Expand All @@ -32,10 +30,5 @@ public DbConnection GetDbConnection()
{
return this.Connection;
}

public async Task PersistChangesAsync()
{
await this._eventTracker.EmitTransactionalEventsAsync();
}
}
}
Loading

0 comments on commit 980ddc0

Please sign in to comment.