Skip to content

Commit c2a639f

Browse files
committed
revoked TransactionScope feature
2 parents d490f4f + caea5f4 commit c2a639f

File tree

8 files changed

+161
-101
lines changed

8 files changed

+161
-101
lines changed

src/SampleDotnet.RepositoryFactory/Extensions/DbContextExtensions.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,17 @@ internal static async Task RollbackChangesAsync(this DbContext context, bool ove
4545
}
4646
}
4747
}
48+
49+
internal static void SilentDbContextDispose(this DbContext dbContext, bool acceptAllChangesBeforeDisposing = false)
50+
{
51+
if (acceptAllChangesBeforeDisposing)
52+
53+
try
54+
{
55+
//dbContext.Dispose();
56+
}
57+
catch (Exception e)
58+
{
59+
}
60+
}
4861
}

src/SampleDotnet.RepositoryFactory/Interfaces/IUnitOfWork.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ public interface IUnitOfWork : IDisposable
66

77
SaveChangesExceptionDetail? SaveChangesException { get; }
88

9-
IRepository<TDbContext> CreateRepository<TDbContext>(TransactionScopeOption transactionScopeOption = TransactionScopeOption.Required, System.Transactions.IsolationLevel isolationLevel = System.Transactions.IsolationLevel.ReadCommitted) where TDbContext : DbContext;
9+
IRepository<TDbContext> CreateRepository<TDbContext>(/*TransactionScopeOption transactionScopeOption = TransactionScopeOption.RequiresNew, System.Transactions.IsolationLevel isolationLevel = System.Transactions.IsolationLevel.ReadCommitted*/) where TDbContext : DbContext;
1010

1111
bool SaveChanges();
1212

src/SampleDotnet.RepositoryFactory/Repository.cs

Lines changed: 27 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,14 @@ internal class Repository<TDbContext> : RepositoryBase
1111
return entity;
1212
});
1313

14-
private readonly TransactionOptions _transactionOptions;
15-
private readonly TransactionScopeOption _transactionScopeOption;
16-
private readonly IDbContextFactory<TDbContext> _dbContextFactory;
14+
//private readonly TransactionOptions transactionOptions;
15+
//private readonly TransactionScopeOption transactionScopeOption;
1716

18-
public Repository(IDbContextFactory<TDbContext> dbContextFactory
19-
, TransactionScopeOption transactionScopeOption
20-
, System.Transactions.IsolationLevel isolationLevel)
21-
: base(dbContextFactory.CreateDbContext())
17+
public Repository(TDbContext dbContext/*, TransactionScopeOption transactionScopeOption, System.Transactions.IsolationLevel isolationLevel*/)
18+
: base(dbContext)
2219
{
23-
this._transactionScopeOption = transactionScopeOption;
24-
this._dbContextFactory = dbContextFactory;
25-
this._transactionOptions = new() { IsolationLevel = isolationLevel };
20+
//this.transactionScopeOption = transactionScopeOption;
21+
//this.transactionOptions = new() { IsolationLevel = isolationLevel };
2622
}
2723

2824
public IQueryable<T> AsQueryable<T>() where T : class
@@ -32,7 +28,7 @@ public IQueryable<T> AsQueryable<T>() where T : class
3228

3329
private DbSet<T> CachedContextSet<T>() where T : class
3430
{
35-
return (DbSet<T>)_cachedDbSets.GetOrAdd(typeof(T).FullName, DbContext.Set<T>());
31+
return (DbSet<T>)_cachedDbSets.GetOrAdd(typeof(T).FullName, CurrentDbContext.Set<T>());
3632
}
3733

3834
public void Delete<T>(T entity) where T : class
@@ -60,44 +56,44 @@ public void Delete<T>(IEnumerable<T> entities) where T : class
6056
{
6157
ArgumentNullException.ThrowIfNull(keyValues, nameof(keyValues));
6258

63-
using (CreateTransactionScope())
64-
return CachedContextSet<T>().Find(keyValues);
59+
//using (CreateTransactionScope())
60+
return CachedContextSet<T>().Find(keyValues);
6561
}
6662

6763
public ValueTask<T?> FindAsync<T>(object[] keyValues, CancellationToken cancellationToken = default) where T : class
6864
{
6965
ArgumentNullException.ThrowIfNull(keyValues, nameof(keyValues));
7066

71-
using (CreateTransactionScope())
72-
return CachedContextSet<T>().FindAsync(keyValues, cancellationToken);
67+
//using (CreateTransactionScope())
68+
return CachedContextSet<T>().FindAsync(keyValues, cancellationToken);
7369
}
7470

7571
public T? FirstOrDefault<T>(Expression<Func<T, bool>> predicate) where T : class
7672
{
7773
ArgumentNullException.ThrowIfNull(predicate, nameof(predicate));
7874

79-
using (CreateTransactionScope())
80-
return AsQueryable<T>().FirstOrDefault(predicate);
75+
//using (CreateTransactionScope())
76+
return AsQueryable<T>().FirstOrDefault(predicate);
8177
}
8278

8379
public T? FirstOrDefault<T>() where T : class
8480
{
85-
using (CreateTransactionScope())
86-
return AsQueryable<T>().FirstOrDefault();
81+
//using (CreateTransactionScope())
82+
return AsQueryable<T>().FirstOrDefault();
8783
}
8884

8985
public Task<T?> FirstOrDefaultAsync<T>(Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default) where T : class
9086
{
9187
ArgumentNullException.ThrowIfNull(predicate, nameof(predicate));
9288

93-
using (CreateTransactionScope())
94-
return AsQueryable<T>().FirstOrDefaultAsync(predicate, cancellationToken);
89+
//using (CreateTransactionScope())
90+
return AsQueryable<T>().FirstOrDefaultAsync(predicate, cancellationToken);
9591
}
9692

9793
public Task<T?> FirstOrDefaultAsync<T>(CancellationToken cancellationToken = default) where T : class
9894
{
99-
using (CreateTransactionScope())
100-
return AsQueryable<T>().FirstOrDefaultAsync(cancellationToken);
95+
//using (CreateTransactionScope())
96+
return AsQueryable<T>().FirstOrDefaultAsync(cancellationToken);
10197
}
10298

10399
public T? GetById<T>(object id) where T : class
@@ -165,8 +161,8 @@ public Task<List<T>> WhereWithTransactionScopeAsync<T>(Expression<Func<T, bool>>
165161
{
166162
ArgumentNullException.ThrowIfNull(predicate, nameof(predicate));
167163

168-
using (CreateTransactionScope())
169-
return AsQueryable<T>().Where(predicate).ToListAsync();
164+
//using (CreateTransactionScope())
165+
return AsQueryable<T>().Where(predicate).ToListAsync();
170166
}
171167

172168
private void _InternalInsert<T>(IEnumerable<T> entities) where T : class
@@ -183,15 +179,10 @@ private Task _InternalInsertAsync<T>(IEnumerable<T> entities, CancellationToken
183179
return CachedContextSet<T>().AddRangeAsync(entities.Select(f => (T)_funcCreatedAt(f)), cancellationToken);
184180
}
185181

186-
private TransactionScope CreateTransactionScope()
187-
{
188-
TransactionScopeAsyncFlowOption transactionScopeAsyncFlowOption =
189-
_transactionScopeOption == TransactionScopeOption.Suppress ? TransactionScopeAsyncFlowOption.Suppress : TransactionScopeAsyncFlowOption.Enabled;
190-
return new TransactionScope(_transactionScopeOption, _transactionOptions, transactionScopeAsyncFlowOption);
191-
}
192-
193-
public override DbContext CreateDbContext()
194-
{
195-
return _dbContextFactory.CreateDbContext();
196-
}
182+
//private TransactionScope CreateTransactionScope()
183+
//{
184+
// TransactionScopeAsyncFlowOption transactionScopeAsyncFlowOption =
185+
// transactionScopeOption == TransactionScopeOption.Suppress ? TransactionScopeAsyncFlowOption.Suppress : TransactionScopeAsyncFlowOption.Suppress;
186+
// return new TransactionScope(transactionScopeOption, transactionOptions, transactionScopeAsyncFlowOption);
187+
//}
197188
}
Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Collections.Concurrent;
1+
using Microsoft.EntityFrameworkCore;
2+
using System.Collections.Concurrent;
23

34
namespace SampleDotnet.RepositoryFactory;
45

@@ -11,15 +12,16 @@ internal abstract class RepositoryBase : IRepository
1112
return entity;
1213
});
1314

14-
private DbContext context;
15+
private readonly DbContext _context;
1516
internal readonly ConcurrentDictionary<string, IQueryable> _cachedDbSets = new();
17+
private bool disposedValue;
1618

1719
protected RepositoryBase(DbContext context)
1820
{
19-
this.context = context;
21+
this._context = context;
2022
}
2123

22-
public DbContext DbContext => context;
24+
public DbContext CurrentDbContext => _context;
2325

2426
public virtual void Delete(object entity)
2527
{
@@ -30,13 +32,7 @@ public virtual void Delete(object entity)
3032

3133
public virtual void DeleteRange(params object[] entities)
3234
{
33-
context.RemoveRange(entities);
34-
}
35-
36-
public void Dispose()
37-
{
38-
_cachedDbSets.Clear();
39-
GC.SuppressFinalize(this);
35+
_context.RemoveRange(entities);
4036
}
4137

4238
public virtual void Update(object entity)
@@ -48,17 +44,26 @@ public virtual void Update(object entity)
4844

4945
public virtual void UpdateRange(params object[] entities)
5046
{
51-
context.UpdateRange(entities.Select(f => _funcUpdatedAt(f)));
47+
_context.UpdateRange(entities.Select(f => _funcUpdatedAt(f)));
5248
}
5349

54-
public abstract DbContext CreateDbContext();
55-
56-
public DbContext RefreshDbContext()
50+
protected virtual void Dispose(bool disposing)
5751
{
58-
try { context.Dispose(); } catch { }
59-
60-
context = CreateDbContext();
52+
if (!disposedValue)
53+
{
54+
if (disposing)
55+
{
56+
_cachedDbSets.Clear();
57+
}
58+
59+
disposedValue = true;
60+
}
61+
}
6162

62-
return context;
63+
public void Dispose()
64+
{
65+
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
66+
Dispose(disposing: true);
67+
GC.SuppressFinalize(this);
6368
}
6469
}

src/SampleDotnet.RepositoryFactory/SampleDotnet.RepositoryFactory.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@
3131
<ContinuousIntegrationBuild>True</ContinuousIntegrationBuild>
3232
<EmbedUntrackedSources>True</EmbedUntrackedSources>
3333
<Copyright>Copyright 2023</Copyright>
34-
<AssemblyVersion>3.0.0.2</AssemblyVersion>
35-
<Version>3.0.0.2-alpha</Version>
34+
<AssemblyVersion>3.0.0.3</AssemblyVersion>
35+
<Version>3.0.0.3-alpha</Version>
3636
</PropertyGroup>
3737

3838
<ItemGroup>

src/SampleDotnet.RepositoryFactory/UnitOfWork.cs

Lines changed: 30 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace SampleDotnet.RepositoryFactory;
44

55
internal class UnitOfWork : IUnitOfWork
66
{
7-
private readonly Queue<DbContextId> _dbContextQueue = new();
7+
private readonly Queue<DbContext> _dbContextPool = new();
88
private readonly ConcurrentDictionary<DbContextId, IRepository> _repositoryPool = new();
99
private readonly SemaphoreSlim _semaphoreSlim = new(1, 1);
1010
private readonly IServiceProvider _serviceProvider;
@@ -19,15 +19,17 @@ public UnitOfWork(IServiceProvider serviceProvider)
1919

2020
public SaveChangesExceptionDetail? SaveChangesException { get; private set; }
2121

22-
public IRepository<TDbContext> CreateRepository<TDbContext>(System.Transactions.TransactionScopeOption transactionScopeOption = System.Transactions.TransactionScopeOption.Required, System.Transactions.IsolationLevel isolationLevel = System.Transactions.IsolationLevel.ReadCommitted)
22+
public IRepository<TDbContext> CreateRepository<TDbContext>(/*System.Transactions.TransactionScopeOption transactionScopeOption = System.Transactions.TransactionScopeOption.RequiresNew, System.Transactions.IsolationLevel isolationLevel = System.Transactions.IsolationLevel.ReadCommitted*/)
2323
where TDbContext : DbContext
2424
{
2525
var dbContext = _serviceProvider
26-
.GetRequiredService<IDbContextFactory<TDbContext>>();
26+
.GetRequiredService<IDbContextFactory<TDbContext>>()
27+
.CreateDbContext();
2728

28-
var repository = new Repository<TDbContext>(dbContext, transactionScopeOption, isolationLevel);
29-
_dbContextQueue.Enqueue(repository.DbContext.ContextId);
30-
_repositoryPool.TryAdd(repository.DbContext.ContextId, repository);
29+
_dbContextPool.Enqueue(dbContext);
30+
31+
var repository = new Repository<TDbContext>(dbContext/*, transactionScopeOption, isolationLevel*/);
32+
_repositoryPool.TryAdd(dbContext.ContextId, repository);
3133
return repository;
3234
}
3335

@@ -45,47 +47,34 @@ public bool SaveChanges()
4547
public async Task<bool> SaveChangesAsync(CancellationToken cancellationToken = default)
4648
{
4749
DbContext? thrownExceptionDbContext = null;
50+
await _semaphoreSlim.WaitAsync(cancellationToken);
4851
try
4952
{
50-
await _semaphoreSlim.WaitAsync(cancellationToken);
51-
52-
int count = _dbContextQueue.Count;
53-
foreach (var dbContextKey in _dbContextQueue)
53+
var cached = _dbContextPool.ToArray();
54+
foreach (var dbContext in cached)
5455
{
55-
if (_repositoryPool.TryGetValue(dbContextKey, out var repo))
56+
try
5657
{
57-
try
58-
{
59-
if (!repo.ChangeTracker.AutoDetectChangesEnabled)
60-
repo.ChangeTracker.DetectChanges();
61-
await repo.DbContext.SaveChangesAsync(false, cancellationToken);
62-
}
63-
catch
58+
if (!dbContext.ChangeTracker.AutoDetectChangesEnabled)
59+
dbContext.ChangeTracker.DetectChanges();
60+
await dbContext.SaveChangesAsync(false, cancellationToken);
61+
}
62+
catch
63+
{
64+
thrownExceptionDbContext = dbContext;
65+
foreach (var context in cached)
6466
{
65-
thrownExceptionDbContext = repo.DbContext;
66-
67-
foreach (var dbContextKey2 in _dbContextQueue)
68-
{
69-
if (_repositoryPool.TryGetValue(dbContextKey2, out var repo2))
70-
{
71-
await repo2.DbContext.RollbackChangesAsync(false, cancellationToken);
72-
}
73-
}
74-
throw;
67+
await context.RollbackChangesAsync(false, cancellationToken);
7568
}
69+
throw;
7670
}
7771
}
7872

79-
for (int i = 0; i < count; i++)
73+
for (int i = 0; i < cached.Length; i++)
8074
{
81-
if (_dbContextQueue.TryDequeue(out var dbContextKey) && dbContextKey != null)
75+
if (_dbContextPool.TryDequeue(out var dbContext) && dbContext != null)
8276
{
83-
if (_repositoryPool.TryRemove(dbContextKey, out var repo))
84-
{
85-
var newDbContext = repo.RefreshDbContext();
86-
_dbContextQueue.Enqueue(newDbContext.ContextId);
87-
_repositoryPool.TryAdd(newDbContext.ContextId, repo);
88-
}
77+
dbContext.ChangeTracker.AcceptAllChanges();
8978
}
9079
}
9180
}
@@ -115,7 +104,11 @@ protected virtual void Dispose(bool disposing)
115104
}
116105
}
117106

118-
_dbContextQueue.Clear();
107+
while (_dbContextPool.TryDequeue(out var dbContext) && dbContext != null)
108+
{
109+
try { dbContext.Dispose(); } catch { }
110+
}
111+
119112
_semaphoreSlim.Dispose();
120113
}
121114
disposedValue = true;

0 commit comments

Comments
 (0)