From f7e2bb243484bb1d5f829073c022edacf99db231 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 20 May 2025 10:35:27 +0200 Subject: [PATCH] Refactor repository pattern and mark methods obsolete Updated `IRepository` to introduce new reading methods and mark existing ones as obsolete. The `CRUDRepository` class is now deprecated, encouraging a transition to alternative implementations. Significant changes in `DbRepository` include the removal of old read methods in favor of a new `Read` method returning `IReadQuery`. Added a `ReadQuery` class to enhance querying capabilities with a fluent API. Obsolete methods are organized under a `#region Obsolete` directive for better management. --- .../Interfaces/Repository/IRepository.cs | 2 + .../CRUDRepository.cs | 1 + .../DbRepository.cs | 48 +++++++++++-------- DigitalData.Core.Infrastructure/ReadQuery.cs | 48 +++++++++++++++++++ 4 files changed, 79 insertions(+), 20 deletions(-) create mode 100644 DigitalData.Core.Infrastructure/ReadQuery.cs diff --git a/DigitalData.Core.Application/Interfaces/Repository/IRepository.cs b/DigitalData.Core.Application/Interfaces/Repository/IRepository.cs index 42e72b4..2164059 100644 --- a/DigitalData.Core.Application/Interfaces/Repository/IRepository.cs +++ b/DigitalData.Core.Application/Interfaces/Repository/IRepository.cs @@ -16,6 +16,7 @@ public interface IRepository public Task DeleteAsync(Expression> expression, CancellationToken cancellation = default); + #region Obsolete [Obsolete("Use Read-method returning IReadQuery instead.")] public Task> ReadAllAsync(Expression>? expression = null, CancellationToken cancellation = default); @@ -27,4 +28,5 @@ public interface IRepository [Obsolete("Use Read-method returning IReadQuery instead.")] public Task ReadOrDefaultAsync(Expression> expression, bool single = true, CancellationToken cancellation = default); + #endregion } diff --git a/DigitalData.Core.Infrastructure/CRUDRepository.cs b/DigitalData.Core.Infrastructure/CRUDRepository.cs index 6162bf5..e89103c 100644 --- a/DigitalData.Core.Infrastructure/CRUDRepository.cs +++ b/DigitalData.Core.Infrastructure/CRUDRepository.cs @@ -15,6 +15,7 @@ namespace DigitalData.Core.Infrastructure /// This repository abstracts the common database operations, offering an asynchronous API to work with the entity's data. /// It leverages the EF Core's DbContext and DbSet to perform these operations. /// + [Obsolete("Use Repository")] public class CRUDRepository : ICRUDRepository where TEntity : class where TDbContext : DbContext diff --git a/DigitalData.Core.Infrastructure/DbRepository.cs b/DigitalData.Core.Infrastructure/DbRepository.cs index b1d1216..f0be2bf 100644 --- a/DigitalData.Core.Infrastructure/DbRepository.cs +++ b/DigitalData.Core.Infrastructure/DbRepository.cs @@ -33,25 +33,8 @@ public class DbRepository : IRepository where TDbC return entities; } - public virtual async Task> ReadAllAsync(Expression>? expression = null, CancellationToken ct = default) - => expression is null - ? await Entities.AsNoTracking().ToListAsync(ct) - : await Entities.AsNoTracking().Where(expression).ToListAsync(ct); - - public virtual async Task ReadOrDefaultAsync(Expression> expression, bool single = true, CancellationToken ct = default) - => single - ? await Entities.AsNoTracking().Where(expression).SingleOrDefaultAsync(ct) - : await Entities.AsNoTracking().Where(expression).FirstOrDefaultAsync(ct); - - public virtual async Task> ReadAllAsync(Expression>? expression = null, CancellationToken ct = default) - => Mapper.Map(await ReadAllAsync(expression, ct)); - - public virtual async Task ReadOrDefaultAsync(Expression> expression, bool single = true, CancellationToken ct = default) - { - var entity = await ReadOrDefaultAsync(expression, single, ct); - return entity is null ? default : Mapper.Map(entity); - } - + public IReadQuery Read(params Expression>[] expressions) => new ReadQuery(Entities.AsNoTracking()).Where(expressions); + public virtual async Task UpdateAsync(TDto dto, Expression> expression, CancellationToken ct = default) { var entities = await Entities.Where(expression).ToListAsync(ct); @@ -76,4 +59,29 @@ public class DbRepository : IRepository where TDbC await Context.SaveChangesAsync(ct); } -} + + #region Obsolete + [Obsolete("Use Read-method returning IReadQuery instead.")] + public virtual async Task> ReadAllAsync(Expression>? expression = null, CancellationToken ct = default) + => expression is null + ? await Entities.AsNoTracking().ToListAsync(ct) + : await Entities.AsNoTracking().Where(expression).ToListAsync(ct); + + [Obsolete("Use Read-method returning IReadQuery instead.")] + public virtual async Task ReadOrDefaultAsync(Expression> expression, bool single = true, CancellationToken ct = default) + => single + ? await Entities.AsNoTracking().Where(expression).SingleOrDefaultAsync(ct) + : await Entities.AsNoTracking().Where(expression).FirstOrDefaultAsync(ct); + + [Obsolete("Use Read-method returning IReadQuery instead.")] + public virtual async Task> ReadAllAsync(Expression>? expression = null, CancellationToken ct = default) + => Mapper.Map(await ReadAllAsync(expression, ct)); + + [Obsolete("Use Read-method returning IReadQuery instead.")] + public virtual async Task ReadOrDefaultAsync(Expression> expression, bool single = true, CancellationToken ct = default) + { + var entity = await ReadOrDefaultAsync(expression, single, ct); + return entity is null ? default : Mapper.Map(entity); + } + #endregion +} \ No newline at end of file diff --git a/DigitalData.Core.Infrastructure/ReadQuery.cs b/DigitalData.Core.Infrastructure/ReadQuery.cs new file mode 100644 index 0000000..baa5f25 --- /dev/null +++ b/DigitalData.Core.Infrastructure/ReadQuery.cs @@ -0,0 +1,48 @@ +using DigitalData.Core.Application.Interfaces.Repository; +using Microsoft.EntityFrameworkCore; +using System.Linq.Expressions; + +namespace DigitalData.Core.Infrastructure; + +public sealed record ReadQuery : IReadQuery +{ + private IQueryable _query; + + internal ReadQuery(IQueryable queryable) + { + _query = queryable; + } + + public TEntity First() => _query.First(); + + public Task FirstAsync(CancellationToken cancellation = default) => _query.FirstAsync(cancellation); + + public TEntity? FirstOrDefault() => _query.FirstOrDefault(); + + + public Task FirstOrDefaultAsync(CancellationToken cancellation = default) => _query.FirstOrDefaultAsync(cancellation); + + + public TEntity Single() => _query.Single(); + + + public Task SingleAsync(CancellationToken cancellation = default) => _query.SingleAsync(cancellation); + + + public TEntity? SingleOrDefault() => _query.SingleOrDefault(); + + + public Task SingleOrDefaultAsync(CancellationToken cancellation = default) => _query.SingleOrDefaultAsync(cancellation); + + + public IEnumerable ToList() => _query.ToList(); + + + public async Task> ToListAsync(CancellationToken cancellation = default) => await _query.ToListAsync(cancellation); + + public IReadQuery Where(Expression> expression) + { + _query = _query.Where(expression); + return this; + } +} \ No newline at end of file