Compare commits

..

11 Commits

Author SHA1 Message Date
ad734f77f9 chore(DigitalData.Core.Infrastructure): bump to 2.4.0 2025-09-12 12:55:03 +02:00
4c003745ff chore(Abstractions.Application): bump to 1.3.0 2025-09-12 12:53:50 +02:00
89de237aff refactor(DbRepository); split to regions 2025-09-12 12:45:20 +02:00
a2567791b7 refactor(DbRepositoryTests): update to use Get instead of Where 2025-09-12 12:42:25 +02:00
4526ba189a refactor(DbRepository): make Mapper nullable to be able to use without adding automapper 2025-09-12 12:40:22 +02:00
f544ea4887 refactor(DbRepository): implement create method for dtros 2025-09-12 12:08:01 +02:00
173c4fdbc4 refactor(repository): restructure IRepository interface with CRUD regions
- Removed AutoMapper dependency from IRepository interface.
- Organized methods into Create, Read, Update, Delete (CRUD) regions.
- Added CreateAsync overloads for DTOs.
- Retained obsolete methods under a dedicated region.
2025-09-12 12:04:28 +02:00
babddfff83 feat(DbRepository): add GetAll and GetAllAsync, make Where read-only
- Updated Where() to use AsNoTracking() for read-only queries
- Added GetAll() and GetAllAsync() to retrieve all entities
- Improved repository methods for efficient read operations
2025-09-12 11:50:59 +02:00
56b467ddfc add CancellationToken to GetAllAsync 2025-09-12 11:45:12 +02:00
63c97b4dc7 feat(repository): add GetAll and GetAllAsync methods to IRepository
- Introduced GetAll() and GetAllAsync() to provide full entity retrieval options.
- Updated obsolete warnings:
  - Read() now marked with "Use CreateAsync, UpdateAsync or DeleteAsync".
  - ReadOnly() points to IRepository<TEntity>.Where.
- Removed old Get() method in favor of new retrieval methods.
2025-09-12 11:44:02 +02:00
e2853b64d1 refactor(repository): replace queryFactory with DbSetFactory in DbRepository
- Updated DbRepository constructor to use DbSetFactory<TDbContext, TEntity>
  instead of a Func<TDbContext, DbSet<TEntity>> queryFactory.
- Adjusted namespace imports to include DigitalData.Core.Infrastructure.Factory.
- Improved repository instantiation consistency and encapsulation.
2025-09-12 11:35:40 +02:00
8 changed files with 70 additions and 50 deletions

View File

@ -14,9 +14,9 @@
<PackageIcon>core_icon.png</PackageIcon>
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
<PackageTags>digital data core application clean architecture abstraction</PackageTags>
<Version>1.2.1</Version>
<AssemblyVersion>1.2.1</AssemblyVersion>
<FileVersion>1.2.1</FileVersion>
<Version>1.3.0</Version>
<AssemblyVersion>1.3.0</AssemblyVersion>
<FileVersion>1.3.0</FileVersion>
</PropertyGroup>
<ItemGroup>

View File

@ -4,27 +4,18 @@ namespace DigitalData.Core.Abstraction.Application.Repository;
public static class Extensions
{
#region Create
public static Task<TEntity> CreateAsync<TEntity, TDto>(this IRepository<TEntity> repository, TDto dto, CancellationToken ct = default)
{
var entity = repository.Mapper.Map<TEntity>(dto);
return repository.CreateAsync(entity, ct);
}
public static Task<IEnumerable<TEntity>> CreateAsync<TEntity, TDto>(this IRepository<TEntity> repository, IEnumerable<TDto> dtos, CancellationToken ct = default)
{
var entities = dtos.Select(dto => repository.Mapper.Map<TEntity>(dto));
return repository.CreateAsync(entities, ct);
}
#endregion
#region IRepository
public static IQueryable<TEntity> Get<TEntity>(this IRepository repository) where TEntity : IEntity
=> repository.Entity<TEntity>().Get();
#region Read
public static IEnumerable<TEntity> GetAll<TEntity>(this IRepository repository) where TEntity : IEntity
=> repository.Entity<TEntity>().GetAll();
public static Task<IEnumerable<TEntity>> GetAllAsync<TEntity>(this IRepository repository, CancellationToken cancel = default) where TEntity : IEntity
=> repository.Entity<TEntity>().GetAllAsync(cancel);
public static IQueryable<TEntity> Where<TEntity>(this IRepository repository, Expression<Func<TEntity, bool>> expression)
where TEntity : IEntity
=> repository.Entity<TEntity>().Where(expression);
#endregion
#region Create
public static Task<TEntity> CreateAsync<TEntity>(this IRepository repository, TEntity entity, CancellationToken cancel = default)

View File

@ -1,33 +1,44 @@
using AutoMapper;
using System.Linq.Expressions;
using System.Linq.Expressions;
namespace DigitalData.Core.Abstraction.Application.Repository;
public interface IRepository<TEntity>
{
public IMapper Mapper { get; }
#region Create
public Task<TEntity> CreateAsync(TEntity entity, CancellationToken cancel = default);
public Task<IEnumerable<TEntity>> CreateAsync(IEnumerable<TEntity> entities, CancellationToken cancel = default);
public Task<TEntity> CreateAsync<TDto>(TDto dto, CancellationToken cancel = default);
public Task<IEnumerable<TEntity>> CreateAsync<TDto>(IEnumerable<TDto> dtos, CancellationToken cancel = default);
#endregion Create
#region Read
public IQueryable<TEntity> Where(Expression<Func<TEntity, bool>> expression);
public IQueryable<TEntity> Get();
public IEnumerable<TEntity> GetAll();
public Task<IEnumerable<TEntity>> GetAllAsync(CancellationToken cancel = default);
#endregion Read
#region Update
public Task UpdateAsync<TDto>(TDto dto, Expression<Func<TEntity, bool>> expression, CancellationToken cancel = default);
public Task UpdateAsync<TDto>(TDto dto, Func<IQueryable<TEntity>, IQueryable<TEntity>> query, CancellationToken cancel = default);
#endregion Update
#region Delete
public Task DeleteAsync(Expression<Func<TEntity, bool>> expression, CancellationToken cancel = default);
public Task DeleteAsync(Func<IQueryable<TEntity>, IQueryable<TEntity>> query, CancellationToken cancel = default);
#endregion Delete
#region Obsolete
[Obsolete("Use IRepository<TEntity>.Where")]
[Obsolete("Use CreateAsync, UpdateAsync or DeleteAsync")]
public IQueryable<TEntity> Read();
[Obsolete("Use IRepository<TEntity>.Get")]
[Obsolete("Use IRepository<TEntity>.Where")]
public IQueryable<TEntity> ReadOnly();
#endregion
}

View File

@ -1,5 +1,6 @@
using AutoMapper;
using DigitalData.Core.Abstraction.Application.Repository;
using DigitalData.Core.Infrastructure.Factory;
using Microsoft.EntityFrameworkCore;
using System.Linq.Expressions;
@ -11,15 +12,16 @@ public class DbRepository<TDbContext, TEntity> : IRepository<TEntity> where TDbC
protected internal readonly DbSet<TEntity> Entities;
public IMapper Mapper { get; }
public IMapper? Mapper { get; }
public DbRepository(TDbContext context, Func<TDbContext, DbSet<TEntity>> queryFactory, IMapper mapper)
public DbRepository(TDbContext context, DbSetFactory<TDbContext, TEntity> factory, IMapper? mapper = null)
{
Context = context;
Entities = queryFactory(context);
Entities = factory.Create(context);
Mapper = mapper;
}
#region Create
public virtual async Task<TEntity> CreateAsync(TEntity entity, CancellationToken cancel = default)
{
Entities.Add(entity);
@ -34,10 +36,21 @@ public class DbRepository<TDbContext, TEntity> : IRepository<TEntity> where TDbC
return entities;
}
public IQueryable<TEntity> Where(Expression<Func<TEntity, bool>> expression) => Entities.AsQueryable().Where(expression);
public virtual Task<TEntity> CreateAsync<TDto>(TDto dto, CancellationToken cancel = default) => CreateAsync(Mapper!.Map<TEntity>(dto), cancel);
public IQueryable<TEntity> Get() => Entities.AsNoTracking();
public virtual Task<IEnumerable<TEntity>> CreateAsync<TDto>(IEnumerable<TDto> dtos, CancellationToken cancel = default)
=> CreateAsync(Mapper!.Map<IEnumerable<TEntity>>(dtos), cancel);
#endregion Create
#region Read
public virtual IQueryable<TEntity> Where(Expression<Func<TEntity, bool>> expression) => Entities.AsNoTracking().Where(expression);
public virtual IEnumerable<TEntity> GetAll() => Entities.AsNoTracking().ToList();
public virtual async Task<IEnumerable<TEntity>> GetAllAsync(CancellationToken cancel = default) => await Entities.AsNoTracking().ToListAsync(cancel);
#endregion Read
#region Update
public virtual Task UpdateAsync<TDto>(TDto dto, Expression<Func<TEntity, bool>> expression, CancellationToken cancel = default) => UpdateAsync(dto, q => q.Where(expression), cancel);
public virtual async Task UpdateAsync<TDto>(TDto dto, Func<IQueryable<TEntity>, IQueryable<TEntity>> query, CancellationToken cancel = default)
@ -46,13 +59,15 @@ public class DbRepository<TDbContext, TEntity> : IRepository<TEntity> where TDbC
for (int i = entities.Count - 1; i >= 0; i--)
{
Mapper.Map(dto, entities[i]);
Mapper!.Map(dto, entities[i]);
Entities.Update(entities[i]);
}
await Context.SaveChangesAsync(cancel);
}
#endregion Update
#region Delete
public virtual Task DeleteAsync(Expression<Func<TEntity, bool>> expression, CancellationToken cancel = default) => DeleteAsync(q => q.Where(expression), cancel);
public virtual async Task DeleteAsync(Func<IQueryable<TEntity>, IQueryable<TEntity>> query, CancellationToken cancel = default)
@ -66,13 +81,14 @@ public class DbRepository<TDbContext, TEntity> : IRepository<TEntity> where TDbC
await Context.SaveChangesAsync(cancel);
}
#endregion Delete
#region Obsolete
[Obsolete("Use IRepository<TEntity>.Where")]
public IQueryable<TEntity> Read() => Entities.AsQueryable();
public virtual IQueryable<TEntity> Read() => Entities.AsQueryable();
[Obsolete("Use IRepository<TEntity>.Get")]
public IQueryable<TEntity> ReadOnly() => Entities.AsNoTracking();
public virtual IQueryable<TEntity> ReadOnly() => Entities.AsNoTracking();
#endregion
}

View File

@ -44,7 +44,7 @@ public static class DependencyInjection
// 2. register entities (can overwrite)
RegsEntity.InvokeAll(services);
// 1. register db set factories (can overwrite)
// 3. register db set factories (can overwrite)
RegsDbSetFactory.InvokeAll(services);
}
@ -77,7 +77,7 @@ public static class DependencyInjection
BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
var genericMethod = addDbSetFactoryMethod!.MakeGenericMethod(typeof(TDbContext), entityType);
genericMethod.Invoke(null, new [] { services });
genericMethod.Invoke(null, new [] { services, null });
#endregion DbSetFactory
}
}
@ -85,7 +85,7 @@ public static class DependencyInjection
RegsFromAssembly.Enqueue(reg);
}
public void RegisterEntity<TDbContext, TEntity>(Func<TDbContext, DbSet<TEntity>>? dbSetFactory = null)
public void RegisterEntity<TDbContext, TEntity>(Func<TDbContext, DbSet<TEntity>>? dbSetFactory = null)
where TDbContext : DbContext
where TEntity : class
{
@ -99,7 +99,7 @@ public static class DependencyInjection
public void RegisterDbSetFactory<TDbContext, TEntity>(Func<TDbContext, DbSet<TEntity>> dbSetFactory)
where TDbContext : DbContext
where TEntity : class
where TEntity : class
=> RegsDbSetFactory.Enqueue(s => s.AddDbSetFactory(dbSetFactory));
}
@ -109,8 +109,8 @@ public static class DependencyInjection
queue.Dequeue().Invoke(services);
}
internal static IServiceCollection AddDbSetFactory<TDbContext, TEntity>(this IServiceCollection services, Func<TDbContext, DbSet<TEntity>>? create = null)
where TDbContext : DbContext
internal static IServiceCollection AddDbSetFactory<TDbContext, TEntity>(this IServiceCollection services, Func<TDbContext, DbSet<TEntity>>? create = null)
where TDbContext : DbContext
where TEntity : class
{
create ??= ctx => ctx.Set<TEntity>();

View File

@ -15,9 +15,9 @@
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
<RepositoryType>digital data core abstractions clean architecture</RepositoryType>
<PackageTags>digital data core infrastructure clean architecture</PackageTags>
<Version>2.3.1</Version>
<AssemblyVersion>2.3.1</AssemblyVersion>
<FileVersion>2.3.1</FileVersion>
<Version>2.4.0</Version>
<AssemblyVersion>2.4.0</AssemblyVersion>
<FileVersion>2.4.0</FileVersion>
</PropertyGroup>
<ItemGroup>

View File

@ -1,6 +1,4 @@
namespace DigitalData.Core.Tests.Infrastructure;
using DigitalData.Core.Tests.Mock;
using DigitalData.Core.Tests.Mock;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
@ -8,6 +6,8 @@ using System.Reflection;
using DigitalData.Core.Abstraction.Application.Repository;
using DigitalData.Core.Infrastructure;
namespace DigitalData.Core.Tests.Infrastructure;
public class DbRepositoryTests
{
private IHost _host;
@ -63,7 +63,7 @@ public class DbRepositoryTests
? await Repo.CreateAsync<User, UserCreateDto>(Fake.UserCreateDto)
: await Repo.CreateAsync(Fake.User);
var readUser = await Repo.Get<User>().Where(u => u.Id == createdUser.Id).FirstOrDefaultAsync();
var readUser = await Repo.Where<User>(u => u.Id == createdUser.Id).FirstOrDefaultAsync();
// Assert
Assert.Multiple(() =>
@ -82,7 +82,7 @@ public class DbRepositoryTests
// Act
await Repo.UpdateAsync<User, UserUpdateDto>(userUpdateDto, u => u.Id == createdUser!.Id);
var upToDateUser = await Repo.Get<User>().Where(u => u.Id == createdUser!.Id).FirstOrDefaultAsync();
var upToDateUser = await Repo.Where<User>(u => u.Id == createdUser!.Id).FirstOrDefaultAsync();
// Assert
Assert.Multiple(() =>
@ -99,11 +99,11 @@ public class DbRepositoryTests
{
// Arrange
var createdUser = await Repo.CreateAsync<User, UserCreateDto>(Fake.UserCreateDto);
var readUser = await Repo.Get<User>().Where(u => u.Id == createdUser.Id).FirstOrDefaultAsync();
var readUser = await Repo.Where<User>(u => u.Id == createdUser.Id).FirstOrDefaultAsync();
// Act
await Repo.DeleteAsync<User>(u => u.Id == createdUser.Id);
var deletedUser = await Repo.Get<User>().Where(u => u.Id == createdUser.Id).FirstOrDefaultAsync();
var deletedUser = await Repo.Where<User>(u => u.Id == createdUser.Id).FirstOrDefaultAsync();
// Assert
Assert.Multiple(() =>

View File

@ -1,7 +1,9 @@
using DigitalData.Core.Abstraction.Application.Repository;
using System.ComponentModel.DataAnnotations.Schema;
namespace DigitalData.Core.Tests.Mock;
[Table("USER")]
public class User : UserBase, IEntity
{
public required int Id { get; init; }