-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added new design of repository and unit of work pattern
- Loading branch information
Showing
12 changed files
with
487 additions
and
15 deletions.
There are no files selected for viewing
65 changes: 65 additions & 0 deletions
65
src/Core/Logistics.Domain/Persistence/IMasterRepository2.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
using System.Linq.Expressions; | ||
using Logistics.Domain.Core; | ||
using Logistics.Domain.Specifications; | ||
|
||
namespace Logistics.Domain.Persistence; | ||
|
||
/// <summary> | ||
/// Generic repository. | ||
/// </summary> | ||
/// <typeparam name="TEntity">Class that implements the <see cref="IEntity{TKey}"/> interface</typeparam> | ||
public interface IMasterRepository2<TEntity> where TEntity : class, IEntity<string> | ||
{ | ||
/// <summary> | ||
/// Asynchronously counts number of entities. | ||
/// </summary> | ||
/// <param name="predicate">Predicate to filter query</param> | ||
/// <returns>Number of elements that satisfies the specified condition</returns> | ||
Task<int> CountAsync(Expression<Func<TEntity, bool>>? predicate = default); | ||
|
||
/// <summary> | ||
/// Gets an entity object by ID. | ||
/// </summary> | ||
/// <param name="id">Entity primary key</param> | ||
/// <returns>Entity object</returns> | ||
Task<TEntity?> GetByIdAsync(string id); | ||
|
||
/// <summary> | ||
/// Gets an entity object filtered by predicate. | ||
/// </summary> | ||
/// <param name="predicate">Predicate to filter query</param> | ||
/// <returns>Entity object</returns> | ||
Task<TEntity?> GetAsync(Expression<Func<TEntity, bool>> predicate); | ||
|
||
/// <summary> | ||
/// Gets a list of the entity objects | ||
/// </summary> | ||
/// <param name="predicate">Predicate to filter query</param> | ||
/// <returns>List of entity objects</returns> | ||
Task<List<TEntity>> GetListAsync(Expression<Func<TEntity, bool>> predicate); | ||
|
||
/// <summary> | ||
/// Gets a list of the entity objects | ||
/// </summary> | ||
/// <param name="specification">Specification</param> | ||
/// <returns>List of entity objects</returns> | ||
Task<List<TEntity>> GetListAsync(ISpecification<TEntity>? specification = default); | ||
|
||
/// <summary> | ||
/// Adds new entry to database. | ||
/// </summary> | ||
/// <param name="entity">Entity object</param> | ||
Task AddAsync(TEntity entity); | ||
|
||
/// <summary> | ||
/// Updates existing entry. | ||
/// </summary> | ||
/// <param name="entity">Entity object</param> | ||
void Update(TEntity entity); | ||
|
||
/// <summary> | ||
/// Deletes entity object from database. | ||
/// </summary> | ||
/// <param name="entity">Entity object</param> | ||
void Delete(TEntity? entity); | ||
} |
14 changes: 14 additions & 0 deletions
14
src/Core/Logistics.Domain/Persistence/IMasterUnityOfWork.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using Logistics.Domain.Core; | ||
|
||
namespace Logistics.Domain.Persistence; | ||
|
||
public interface IMasterUnityOfWork : IDisposable | ||
{ | ||
IMasterRepository2<TEntity> Repository<TEntity>() where TEntity : class, IEntity<string>; | ||
|
||
/// <summary> | ||
/// Save changes to database | ||
/// </summary> | ||
/// <returns>Number of rows modified after save changes.</returns> | ||
Task<int> CommitAsync(); | ||
} |
27 changes: 27 additions & 0 deletions
27
src/Core/Logistics.Domain/Persistence/ITenantRepository2.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
using Logistics.Domain.Core; | ||
using Logistics.Domain.Entities; | ||
|
||
namespace Logistics.Domain.Persistence; | ||
|
||
/// <summary> | ||
/// Tenant's repository. | ||
/// </summary> | ||
public interface ITenantRepository2<TEntity> : IMasterRepository2<TEntity> where TEntity : class, ITenantEntity | ||
{ | ||
/// <summary> | ||
/// Gets the current tenant data. | ||
/// </summary> | ||
Tenant GetCurrentTenant(); | ||
|
||
/// <summary> | ||
/// Manually set the current tenant by its ID | ||
/// </summary> | ||
/// <param name="tenantId">Tenant ID</param> | ||
void SetCurrentTenantById(string tenantId); | ||
|
||
/// <summary> | ||
/// Manually set the current tenant by directly passing the instance | ||
/// </summary> | ||
/// <param name="tenant">An instance of Tenant class</param> | ||
void SetCurrentTenant(Tenant tenant); | ||
} |
14 changes: 14 additions & 0 deletions
14
src/Core/Logistics.Domain/Persistence/ITenantUnityOfWork.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using Logistics.Domain.Core; | ||
|
||
namespace Logistics.Domain.Persistence; | ||
|
||
public interface ITenantUnityOfWork : IDisposable | ||
{ | ||
ITenantRepository2<TEntity> Repository<TEntity>() where TEntity : class, ITenantEntity; | ||
|
||
/// <summary> | ||
/// Save changes to database | ||
/// </summary> | ||
/// <returns>Number of rows modified after save changes.</returns> | ||
Task<int> CommitAsync(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
48 changes: 36 additions & 12 deletions
48
src/Core/Logistics.Domain/Specifications/BaseSpecification.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,45 @@ | ||
using System.Linq.Expressions; | ||
using Logistics.Domain.Core; | ||
|
||
namespace Logistics.Domain.Specifications; | ||
|
||
public abstract class BaseSpecification<TEntity> : | ||
ISpecification<TEntity> where TEntity : class, IEntity<string> | ||
public abstract class BaseSpecification<T> : ISpecification<T> | ||
{ | ||
protected BaseSpecification() | ||
public Expression<Func<T, bool>>? Criteria { get; protected set; } | ||
public List<Expression<Func<T, object>>> Includes { get; } = new(); | ||
public List<string> IncludeStrings { get; } = new(); | ||
public Expression<Func<T, object?>>? OrderBy { get; protected set; } | ||
public Expression<Func<T, object>>? GroupBy { get; protected set; } | ||
public int PageSize { get; private set; } | ||
public int Page { get; private set; } | ||
public bool IsPagingEnabled { get; private set; } | ||
public bool Descending { get; protected set; } | ||
|
||
protected virtual void AddInclude(Expression<Func<T, object>> includeExpression) | ||
{ | ||
Criteria = entity => true; | ||
OrderBy = entity => entity.Id; | ||
Includes = new List<Expression<Func<TEntity, object>>>(); | ||
Descending = false; | ||
Includes.Add(includeExpression); | ||
} | ||
|
||
public Expression<Func<TEntity, bool>> Criteria { get; protected init; } | ||
public List<Expression<Func<TEntity, object>>> Includes { get; } | ||
public Expression<Func<TEntity, object?>> OrderBy { get; protected init; } | ||
public bool Descending { get; protected init; } | ||
protected virtual void AddInclude(string includeString) | ||
{ | ||
IncludeStrings.Add(includeString); | ||
} | ||
|
||
protected virtual void ApplyPaging(int page, int pageSize) | ||
{ | ||
Page = page; | ||
PageSize = pageSize; | ||
IsPagingEnabled = true; | ||
} | ||
|
||
protected virtual void ApplyOrderBy(Expression<Func<T, object?>> orderByExpression, bool descending = false) | ||
{ | ||
OrderBy = orderByExpression; | ||
Descending = descending; | ||
} | ||
|
||
|
||
protected virtual void ApplyGroupBy(Expression<Func<T, object>> groupByExpression) | ||
{ | ||
GroupBy = groupByExpression; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
74 changes: 74 additions & 0 deletions
74
src/Core/Logistics.Infrastructure.EF/Persistence/MasterRepository2.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
using System.Linq.Expressions; | ||
using Logistics.Domain.Core; | ||
using Logistics.Domain.Persistence; | ||
using Logistics.Domain.Specifications; | ||
using Logistics.Infrastructure.EF.Data; | ||
using Microsoft.EntityFrameworkCore; | ||
|
||
namespace Logistics.Infrastructure.EF.Persistence; | ||
|
||
public class MasterRepository2<TEntity> : IMasterRepository2<TEntity> | ||
where TEntity : class, IEntity<string> | ||
{ | ||
private readonly MasterDbContext _masterDbContext; | ||
|
||
public MasterRepository2(MasterDbContext masterDbContext) | ||
{ | ||
_masterDbContext = masterDbContext; | ||
} | ||
|
||
public Task<int> CountAsync(Expression<Func<TEntity, bool>>? predicate = default) | ||
{ | ||
if (predicate is null) | ||
{ | ||
return _masterDbContext.Set<TEntity>().CountAsync(); | ||
} | ||
|
||
return _masterDbContext.Set<TEntity>().CountAsync(predicate); | ||
} | ||
|
||
public Task<TEntity?> GetByIdAsync(string id) | ||
{ | ||
return _masterDbContext.Set<TEntity>().FindAsync(id).AsTask(); | ||
} | ||
|
||
public Task<TEntity?> GetAsync(Expression<Func<TEntity, bool>> predicate) | ||
{ | ||
return _masterDbContext.Set<TEntity>().FirstOrDefaultAsync(predicate); | ||
} | ||
|
||
public Task<List<TEntity>> GetListAsync(Expression<Func<TEntity, bool>> predicate) | ||
{ | ||
return _masterDbContext.Set<TEntity>().Where(predicate).ToListAsync(); | ||
} | ||
|
||
public Task<List<TEntity>> GetListAsync(ISpecification<TEntity>? specification = default) | ||
{ | ||
if (specification is null) | ||
{ | ||
return _masterDbContext.Set<TEntity>().ToListAsync(); | ||
} | ||
|
||
return SpecificationEvaluator<TEntity>.GetQuery(_masterDbContext.Set<TEntity>(), specification).ToListAsync(); | ||
} | ||
|
||
public Task AddAsync(TEntity entity) | ||
{ | ||
return _masterDbContext.Set<TEntity>().AddAsync(entity).AsTask(); | ||
} | ||
|
||
public void Update(TEntity entity) | ||
{ | ||
_masterDbContext.Set<TEntity>().Update(entity); | ||
} | ||
|
||
public void Delete(TEntity? entity) | ||
{ | ||
if (entity is null) | ||
{ | ||
return; | ||
} | ||
|
||
_masterDbContext.Set<TEntity>().Remove(entity); | ||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
src/Core/Logistics.Infrastructure.EF/Persistence/MasterUnitOfWork.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
using System.Collections; | ||
using Logistics.Domain.Core; | ||
using Logistics.Domain.Persistence; | ||
using Logistics.Infrastructure.EF.Data; | ||
|
||
namespace Logistics.Infrastructure.EF.Persistence; | ||
|
||
public class MasterUnitOfWork : IMasterUnityOfWork | ||
{ | ||
private readonly MasterDbContext _masterDbContext; | ||
private readonly Hashtable _repositories = new(); | ||
|
||
public MasterUnitOfWork(MasterDbContext masterDbContext) | ||
{ | ||
_masterDbContext = masterDbContext; | ||
} | ||
|
||
public IMasterRepository2<TEntity> Repository<TEntity>() where TEntity : class, IEntity<string> | ||
{ | ||
var type = typeof(TEntity).Name; | ||
|
||
if (!_repositories.ContainsKey(type)) | ||
{ | ||
var repositoryType = typeof(MasterRepository2<>); | ||
|
||
var repositoryInstance = | ||
Activator.CreateInstance(repositoryType | ||
.MakeGenericType(typeof(TEntity)), _masterDbContext); | ||
|
||
_repositories.Add(type, repositoryInstance); | ||
} | ||
|
||
if (_repositories[type] is not IMasterRepository2<TEntity> repository) | ||
{ | ||
throw new InvalidOperationException("Could not create a repository"); | ||
} | ||
|
||
return repository; | ||
} | ||
|
||
public Task<int> CommitAsync() | ||
{ | ||
return _masterDbContext.SaveChangesAsync(); | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
_masterDbContext.Dispose(); | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
src/Core/Logistics.Infrastructure.EF/Persistence/SpecificationEvaluator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
using Logistics.Domain.Core; | ||
using Logistics.Domain.Specifications; | ||
using Microsoft.EntityFrameworkCore; | ||
|
||
namespace Logistics.Infrastructure.EF.Persistence; | ||
|
||
public static class SpecificationEvaluator<TEntity> where TEntity : class, IEntity<string> | ||
{ | ||
public static IQueryable<TEntity> GetQuery(IQueryable<TEntity> inputQuery, ISpecification<TEntity> specification) | ||
{ | ||
var query = inputQuery; | ||
|
||
// modify the IQueryable using the specification's criteria expression | ||
if (specification.Criteria != null) | ||
{ | ||
query = query.Where(specification.Criteria); | ||
} | ||
|
||
// Includes all expression-based includes | ||
query = specification.Includes.Aggregate(query, | ||
(current, include) => current.Include(include)); | ||
|
||
// Include any string-based include statements | ||
query = specification.IncludeStrings.Aggregate(query, | ||
(current, include) => current.Include(include)); | ||
|
||
// Apply ordering if expressions are set | ||
if (specification.OrderBy != null) | ||
{ | ||
query = specification.Descending | ||
? query.OrderByDescending(specification.OrderBy) | ||
: query.OrderBy(specification.OrderBy); | ||
} | ||
|
||
if (specification.GroupBy != null) | ||
{ | ||
query = query.GroupBy(specification.GroupBy).SelectMany(x => x); | ||
} | ||
|
||
// Apply paging if enabled | ||
if (specification.IsPagingEnabled) | ||
{ | ||
query = query.Skip((specification.Page - 1) * specification.PageSize) | ||
.Take(specification.PageSize); | ||
} | ||
return query; | ||
} | ||
} |
Oops, something went wrong.