Skip to content

Commit

Permalink
Merge pull request #1834 from totvsnetcore/efcore-sharedtransaction
Browse files Browse the repository at this point in the history
Add EfCoreTransactionStrategy to share transactions
  • Loading branch information
hikalkan committed Mar 3, 2017
2 parents eabf3c8 + 07757c7 commit 817b409
Show file tree
Hide file tree
Showing 7 changed files with 243 additions and 61 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore;
using System.Data.Common;

namespace Abp.EntityFrameworkCore.Configuration
{
Expand All @@ -7,12 +8,20 @@ public class AbpDbContextConfiguration<TDbContext>
{
public string ConnectionString {get; internal set; }

public DbConnection Connection { get; internal set; }

public DbContextOptionsBuilder<TDbContext> DbContextOptions { get; }

public AbpDbContextConfiguration(string connectionString)
{
ConnectionString = connectionString;
DbContextOptions = new DbContextOptionsBuilder<TDbContext>();
}

public AbpDbContextConfiguration(DbConnection connection)
{
Connection = connection;
DbContextOptions = new DbContextOptionsBuilder<TDbContext>();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
using System;
using System.Reflection;
using Abp.Dependency;
using Abp.EntityFramework;
using Abp.EntityFrameworkCore.Configuration;
using Microsoft.EntityFrameworkCore;
using System;
using System.Data.Common;
using System.Reflection;

namespace Abp.EntityFrameworkCore
{
public class DefaultDbContextResolver : IDbContextResolver, ITransientDependency
{
private static readonly MethodInfo CreateOptionsMethod = typeof(DefaultDbContextResolver).GetMethod("CreateOptions", BindingFlags.NonPublic | BindingFlags.Instance);
private static readonly MethodInfo CreateOptionsFromConnectionMethod = typeof(DefaultDbContextResolver).GetMethod("CreateOptionsFromConnection", BindingFlags.NonPublic | BindingFlags.Instance);

private readonly IIocResolver _iocResolver;
private readonly IDbContextTypeMatcher _dbContextTypeMatcher;
Expand Down Expand Up @@ -43,11 +45,37 @@ public TDbContext Resolve<TDbContext>(string connectionString)
});
}

public TDbContext Resolve<TDbContext>(DbConnection existingConnection)
where TDbContext : DbContext
{
var dbContextType = typeof(TDbContext);

if (!dbContextType.IsAbstract)
{
return _iocResolver.Resolve<TDbContext>(new
{
options = CreateOptionsFromConnection<TDbContext>(existingConnection)
});
}

var concreteType = _dbContextTypeMatcher.GetConcreteType(dbContextType);

return (TDbContext)_iocResolver.Resolve(concreteType, new
{
options = CreateOptionsForType(concreteType, existingConnection)
});
}

private object CreateOptionsForType(Type dbContextType, string connectionString)
{
return CreateOptionsMethod.MakeGenericMethod(dbContextType).Invoke(this, new object[] { connectionString });
}

private object CreateOptionsForType(Type dbContextType, DbConnection connection)
{
return CreateOptionsFromConnectionMethod.MakeGenericMethod(dbContextType).Invoke(this, new object[] { connection });
}

protected virtual DbContextOptions<TDbContext> CreateOptions<TDbContext>(string connectionString)
where TDbContext : DbContext
{
Expand All @@ -70,5 +98,28 @@ protected virtual DbContextOptions<TDbContext> CreateOptions<TDbContext>(string

throw new AbpException($"Could not resolve DbContextOptions for {typeof(TDbContext).AssemblyQualifiedName}.");
}

protected virtual DbContextOptions<TDbContext> CreateOptionsFromConnection<TDbContext>(DbConnection existingConnection)
where TDbContext : DbContext
{
if (_iocResolver.IsRegistered<IAbpDbContextConfigurer<TDbContext>>())
{
var configuration = new AbpDbContextConfiguration<TDbContext>(existingConnection);

using (var configurer = _iocResolver.ResolveAsDisposable<IAbpDbContextConfigurer<TDbContext>>())
{
configurer.Object.Configure(configuration);
}

return configuration.DbContextOptions.Options;
}

if (_iocResolver.IsRegistered<DbContextOptions<TDbContext>>())
{
return _iocResolver.Resolve<DbContextOptions<TDbContext>>();
}

throw new AbpException($"Could not resolve DbContextOptions for {typeof(TDbContext).AssemblyQualifiedName}.");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
using Microsoft.EntityFrameworkCore;
using System.Data.Common;

namespace Abp.EntityFrameworkCore
{
public interface IDbContextResolver
{
TDbContext Resolve<TDbContext>(string connectionString)
where TDbContext : DbContext;
}

TDbContext Resolve<TDbContext>(DbConnection existingConnection)
where TDbContext : DbContext;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using System.Collections.Generic;

namespace Abp.EntityFrameworkCore.Uow
{
public class ActiveTransactionInfo
{
public IDbContextTransaction DbContextTransaction { get; }

public DbContext StarterDbContext { get; }

public List<DbContext> AttendedDbContexts { get; }

public ActiveTransactionInfo(IDbContextTransaction dbContextTransaction, DbContext starterDbContext)
{
DbContextTransaction = dbContextTransaction;
StarterDbContext = starterDbContext;

AttendedDbContexts = new List<DbContext>();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using Abp.Collections.Extensions;
using Abp.Dependency;
using Abp.Domain.Uow;
using Abp.EntityFrameworkCore.Extensions;
using Abp.Transactions.Extensions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using System.Collections.Generic;
using System.Transactions;

namespace Abp.EntityFrameworkCore.Uow
{
public class DbContextEfCoreTransactionStrategy : IEfCoreTransactionStrategy, ITransientDependency
{
protected UnitOfWorkOptions Options { get; private set; }

protected IDictionary<string, ActiveTransactionInfo> ActiveTransactions { get; }

public DbContextEfCoreTransactionStrategy()
{
ActiveTransactions = new Dictionary<string, ActiveTransactionInfo>();
}

public void Commit()
{
foreach (var activeTransaction in ActiveTransactions.Values)
{
activeTransaction.DbContextTransaction.Commit();

foreach (var dbContext in activeTransaction.AttendedDbContexts)
{
if (dbContext.HasRelationalTransactionManager())
{
//Relational databases use the SharedTransaction
continue;
}

dbContext.Database.CommitTransaction();
}
}
}

public DbContext CreateDbContext<TDbContext>(string connectionString, IDbContextResolver dbContextResolver) where TDbContext : DbContext
{
DbContext dbContext;

var activeTransaction = ActiveTransactions.GetOrDefault(connectionString);

if (activeTransaction == null)
{
dbContext = dbContextResolver.Resolve<TDbContext>(connectionString);
}
else
{
dbContext = dbContextResolver.Resolve<TDbContext>(activeTransaction.DbContextTransaction.GetDbTransaction().Connection);
}

if (dbContext.HasRelationalTransactionManager())
{
if (activeTransaction == null)
{
var dbtransaction = dbContext.Database.BeginTransaction((Options.IsolationLevel ?? IsolationLevel.ReadUncommitted).ToSystemDataIsolationLevel());
activeTransaction = new ActiveTransactionInfo(dbtransaction, dbContext);
ActiveTransactions[connectionString] = activeTransaction;
}
else
{
dbContext.Database.UseTransaction(activeTransaction.DbContextTransaction.GetDbTransaction());
activeTransaction.AttendedDbContexts.Add(dbContext);
}
}
else
{
dbContext.Database.BeginTransaction();
}

return dbContext;
}

public void Dispose(IIocResolver iocResolver)
{
foreach (var activeTransaction in ActiveTransactions.Values)
{
activeTransaction.DbContextTransaction.Dispose();

foreach (var attendedDbContext in activeTransaction.AttendedDbContexts)
{
iocResolver.Release(attendedDbContext);
}

activeTransaction.StarterDbContext.Dispose();
}

ActiveTransactions.Clear();
}

public void InitOptions(UnitOfWorkOptions options)
{
Options = options;
}
}
}

0 comments on commit 817b409

Please sign in to comment.