Skip to content

Commit 052fa71

Browse files
committed
Feat: UnitOfWorkRepository
1 parent 5caa0aa commit 052fa71

File tree

5 files changed

+35
-4
lines changed

5 files changed

+35
-4
lines changed

src/CodeOfChaos.Types.UnitOfWork.Contracts/IUnitOfWork.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,5 @@ public interface IUnitOfWork : IAsyncDisposable {
2020

2121
ValueTask<TDbContext> GetDbContextAsync<TDbContext>(CancellationToken ct = default) where TDbContext : DbContext;
2222

23-
TRepo GetRepository<TRepo>() where TRepo : class, IUnitOfWorkRepository;
23+
ValueTask<TRepo> GetRepositoryAsync<TRepo>(CancellationToken ct = default) where TRepo : class, IUnitOfWorkRepository;
2424
}

src/CodeOfChaos.Types.UnitOfWork.Contracts/IUnitOfWorkRepository.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ namespace CodeOfChaos.Types.UnitOfWork;
55
// ---------------------------------------------------------------------------------------------------------------------
66
// Code
77
// ---------------------------------------------------------------------------------------------------------------------
8-
public interface IUnitOfWorkRepository;
8+
public interface IUnitOfWorkRepository {
9+
}

src/CodeOfChaos.Types.UnitOfWork/UnitOfWork.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,13 @@ public virtual async ValueTask<T> GetDbContextAsync<T>(CancellationToken ct = de
8484
return dbContext as T ?? throw new InvalidCastException($"Cannot cast DbContext of type '{dbContext.GetType()}' to '{typeof(T)}'");
8585
}
8686

87-
public virtual TRepo GetRepository<TRepo>() where TRepo : class, IUnitOfWorkRepository {
87+
public virtual async ValueTask<TRepo> GetRepositoryAsync<TRepo>(CancellationToken ct = default) where TRepo : class, IUnitOfWorkRepository {
8888
if (AttachedRepositories.TryGetValue(typeof(TRepo), out IUnitOfWorkRepository? cachedRepo) && cachedRepo is TRepo castedCachedRepo) return castedCachedRepo;
8989

9090
// Cache miss so we create a new instance
9191
var repo = serviceScope.ServiceProvider.GetRequiredService<TRepo>();
92+
if (repo is not UnitOfWorkRepository<TDbContext> castedRepo) throw new InvalidCastException($"Cannot cast repository of type '{repo.GetType()}' to '{typeof(TRepo)}'");
93+
await castedRepo.AttachAsync(this, ct);
9294

9395
AttachedRepositories.AddOrUpdate(typeof(TRepo), repo);
9496
return repo;
@@ -98,6 +100,10 @@ public virtual async ValueTask DisposeAsync() {
98100
if (_transaction != null) await TryRollbackTransactionAsync();
99101

100102
if (!AttachedRepositories.IsEmpty) {
103+
foreach (IUnitOfWorkRepository repository in AttachedRepositories.Values) {
104+
if (repository is not UnitOfWorkRepository<TDbContext> castedRepo) continue;
105+
castedRepo.Detach();
106+
}
101107
AttachedRepositories.Clear();
102108
}
103109

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// ---------------------------------------------------------------------------------------------------------------------
2+
// Imports
3+
// ---------------------------------------------------------------------------------------------------------------------
4+
using Microsoft.EntityFrameworkCore;
5+
6+
namespace CodeOfChaos.Types.UnitOfWork;
7+
8+
// ---------------------------------------------------------------------------------------------------------------------
9+
// Code
10+
// ---------------------------------------------------------------------------------------------------------------------
11+
public abstract class UnitOfWorkRepository<TDbContext> : IUnitOfWorkRepository
12+
where TDbContext : DbContext
13+
{
14+
private TDbContext? DbContext { get; set; }
15+
16+
// -----------------------------------------------------------------------------------------------------------------
17+
// Methods
18+
// -----------------------------------------------------------------------------------------------------------------
19+
internal async ValueTask AttachAsync(IUnitOfWork unitOfWork, CancellationToken ct = default) => DbContext = await unitOfWork.GetDbContextAsync<TDbContext>(ct);
20+
internal void Detach() => DbContext = null;// Remove the reference to the DbContext
21+
22+
protected TDbContext GetDbContext() => DbContext ?? throw new InvalidOperationException("Repository is not attached to a UnitOfWork.");
23+
protected DbSet<TModel> GetDbSet<TModel>() where TModel : class => DbContext?.Set<TModel>() ?? throw new InvalidOperationException("Repository is not attached to a UnitOfWork.");
24+
}

tests/Tests.CodeOfChaos.Types.UnitOfWork/UnitOfWorkTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ public async Task GetRepository_ShouldRetrieveRepositoryFromServiceProvider() {
200200

201201

202202
// Act
203-
var repository = _unitOfWork.GetRepository<IUnitOfWorkRepository>();
203+
var repository = await _unitOfWork.GetRepositoryAsync<IUnitOfWorkRepository>();
204204

205205
// Assert
206206
await Assert.That(repository).IsNotNull();

0 commit comments

Comments
 (0)