From fcf02c9b7b08d8f508eceaf4db6fa22c5affa118 Mon Sep 17 00:00:00 2001 From: Hadi Khazaee Asl Date: Mon, 4 May 2026 00:37:06 +0330 Subject: [PATCH] last ... --- Controllers/XBaseEntityController.cs | 1153 ++++++---- Controllers/XBaseEntityDtoController.cs | 712 ++++++ Db/XUnitOfWorks.cs | 8 +- EFRepositories/XBaseEFRepository.cs | 1405 ++++++------ Extensions/XGraphQLExtensions.cs | 4 - GraphQL/XBaseGraphQLQuery.cs | 52 +- Helpers/XGraphQLHelper.cs | 6 - InMemRepositories/XBaseInMemoryRepositoy.cs | 1012 +++++++-- Interfaces/IXBaseRepository.cs | 399 ++-- Interfaces/IXBaseRepositoryService.cs | 299 +++ Interfaces/IXEntityControllerActions.cs | 123 ++ Interfaces/IXEntityDtoControllerActions.cs | 124 ++ Interfaces/IXKeyGenerator.cs | 4 +- Interfaces/IXUnitOfWorks.cs | 5 +- MongoRepositories/XBaseMongoRepository.cs | 2150 ++++++++++--------- Providers/XBaseRepositoryService.cs | 745 +++++++ Providers/XGuidKeyGenerator.cs | 65 +- Providers/XIntKeyGenerator.cs | 50 +- Providers/XSequentialGuid.cs | 36 +- Providers/XStringKeyGenerator.cs | 32 +- xDataService.csproj | 4 +- 21 files changed, 5640 insertions(+), 2748 deletions(-) create mode 100644 Controllers/XBaseEntityDtoController.cs create mode 100644 Interfaces/IXBaseRepositoryService.cs create mode 100644 Interfaces/IXEntityControllerActions.cs create mode 100644 Interfaces/IXEntityDtoControllerActions.cs create mode 100644 Providers/XBaseRepositoryService.cs diff --git a/Controllers/XBaseEntityController.cs b/Controllers/XBaseEntityController.cs index ae8ac76..168587a 100644 --- a/Controllers/XBaseEntityController.cs +++ b/Controllers/XBaseEntityController.cs @@ -7,474 +7,707 @@ using xCommons.Configurations; using xCommons.Controllers; using xCommons.Extensions; using xCommons.Providers; -using xModels.Interfaces; using xExceptions.Constants; using xModels.Base; using xModels.Dtos; using xDataService.Interfaces; +using System.Linq; +using Microsoft.EntityFrameworkCore.Query; +using System.Threading; +using SQLitePCL; -namespace xDataService.Controllers { +namespace xDataService.Controllers +{ + /// + /// an Abstract Controller for Providing Entity Repository Actions Implementatin ... + /// + /// + /// public abstract class XBaseEntityController : XBaseController, IXEntityControllerActions - where TEntity : XBaseEntity { - public readonly IXBaseRepository repository; + where TEntity : XBaseEntity + { + // + #region Properties ... + public IXBaseRepository Repository { get; } - protected XBaseEntityController ( - ILogger logger, - XAppConfiguration appConfiguration, - XValidationProvider validationProvider, - IXBaseRepository repository - ) : base ( - logger, - appConfiguration, - validationProvider - ) { - // - this.repository = repository; - } + public Func, IOrderedQueryable> DefaultOrderBuilder { get; } + public Func, IIncludableQueryable> DefaultIncludeBuilder { get; } + #endregion + + // + #region Constructor ... + protected XBaseEntityController( + ILogger> logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider, + IXBaseRepository repository, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger, + appConfiguration, + validationProvider + ) + { // - #region Interface Implementations ... - // - #region Retrieve ... - [HttpGet ("{id}")] - public virtual async Task> Get ( - [FromRoute] TKey id, [FromQuery] bool ignoreSoftDeleteds = true, [FromQuery] bool containsDetail = false - ) { - // - try { - // - // Validate Args ... - ValidationProvider.NotNull (id); - - // - // Get Result ... - var result = await repository - .GetAsync ( - id, - ignoreSoftDeleteds : ignoreSoftDeleteds, - containsDetail : containsDetail - ); - - // - return Ok (result - .ToDynamicObject ()); - } catch (Exception ex) { - // - var result = GetExceptionActionResult (ex); - return result; - } - } - - [HttpGet] - public virtual async Task>> GetAll ( - [FromQuery] bool ignoreSoftDeleteds = true, [FromQuery] bool containsDetail = false - ) { - // - try { - // - // Get Result ... - var result = await repository - .GetAllAsync ( - ignoreSoftDeleteds: ignoreSoftDeleteds, - containsDetail: containsDetail - ); - - // - return Ok (result - .ToDynamicObject ()); - } catch (Exception ex) { - // - var result = GetExceptionActionResult (ex); - return result; - } - } - - [HttpGet ("FindOne/{query}")] - public virtual async Task> FindOne ( - [FromRoute] string query, [FromQuery] bool ignoreSoftDeleteds = true, [FromQuery] bool containsDetail = false - ) { - // - try { - // - // Validate Args ... - ValidationProvider.NotEmpty (query); - - // - // Get Result ... - var result = await repository - .FindOneAsync (t => - t.PropValuesContains (query), - ignoreSoftDeleteds : ignoreSoftDeleteds, - containsDetail : containsDetail - ); - - // - return Ok (result - .ToDynamicObject ()); - } catch (Exception ex) { - // - var result = GetExceptionActionResult (ex); - return result; - } - } - - [HttpGet ("FindMany/{query}")] - public virtual async Task>> FindMany ( - [FromRoute] string query, [FromQuery] bool ignoreSoftDeleteds = true, [FromQuery] bool containsDetail = false - ) { - // - try { - // - // Validate Args ... - ValidationProvider.NotEmpty (query); - - // - // Get Result ... - var result = await repository - .FindManyAsync (t => - t.PropValuesContains (query), - ignoreSoftDeleteds : ignoreSoftDeleteds, - containsDetail : containsDetail - ); - - // - return Ok (result - .ToDynamicObject ()); - } catch (Exception ex) { - // - var result = GetExceptionActionResult (ex); - return result; - } - } - - [HttpGet ("Query")] - public virtual async Task>> Query ( - [FromQuery] XQuery query, [FromQuery] bool ignoreSoftDeleteds = true - ) { - // - try { - // - // Validate Args ... - ValidationProvider.NotNull (query); - - // - // Get Result ... - var result = await repository - .QueryAsync ( - query, - ignoreSoftDeleteds : ignoreSoftDeleteds - ); - - // - return Ok (result - .ToDynamicObject ()); - } catch (Exception ex) { - // - var result = GetExceptionActionResult (ex); - return result; - } - } - - // - // TODO: Fix this ... - // [HttpGet ("RequestPage")] - // public virtual async Task>> RequestPage ( - // [FromQuery] XPageRequest request, [FromQuery] bool ignoreSoftDeleteds = true, [FromQuery] bool containsDetail = false - // ) { - // // - // try { - // // - // // Validate Args ... - // ValidationProvider.NotNull (request); - - // // - // // Get Result ... - // var result = await repository - // .RequestPageAsync ( - // request, - // ignoreSoftDeleteds : ignoreSoftDeleteds, - // containsDetail : containsDetail - // ); - - // // - // return Ok (result - // .ToDynamicObject ()); - // } catch (Exception ex) { - // // - // var result = GetExceptionActionResult (ex); - // return result; - // } - // } - #endregion - - // - #region Add ... - [HttpPost] - public virtual async Task> Add ( - [FromBody] TEntity item - ) { - // - try { - // - // Validate Args ... - if (!ModelState.IsValid) { - XException.InvalidArgs.Throw (); - } - ValidationProvider.NotNull (item); - - // - // Get Result ... - var result = await repository - .AddAsync ( - item, - saveChanges : true - ); - - // - return Ok (result - .ToDynamicObject ()); - } catch (Exception ex) { - // - var result = GetExceptionActionResult (ex); - return result; - } - } - - [HttpPost ("AddOrUpdate")] - public virtual async Task> AddOrUpdate ( - [FromBody] TEntity item - ) { - // - try { - // - // Validate Args ... - if (!ModelState.IsValid) { - XException.InvalidArgs.Throw (); - } - ValidationProvider.NotNull (item); - - // - // Get Result ... - var result = await repository - .AddOrUpdateAsync ( - item, - saveChanges : true - ); - - // - return Ok (result - .ToDynamicObject ()); - } catch (Exception ex) { - // - var result = GetExceptionActionResult (ex); - return result; - } - } - - [HttpPost ("AddMany")] - public virtual async Task AddMany ( - [FromBody] XBaseRangeRequest request - ) { - // - try { - // - // Validate Args ... - if (!ModelState.IsValid) { - XException.InvalidArgs.Throw (); - } - await ValidationProvider - .GroupValidationBuilder () - .AddNotNull (request) - .AddNotZeroChilds (request.Items) - .ValidateGroupAsync (); - - // - // Get Result ... - await repository - .AddRangeAsync ( - request.Items, - saveChanges : true - ); - - // - return Ok (); - } catch (Exception ex) { - // - var result = GetExceptionActionResult (ex); - return result; - } - } - #endregion - - // - #region Update ... - [HttpPut ("{id}")] - public virtual async Task> Update ( - [FromRoute] TKey id, [FromBody] TEntity item - ) { - // - try { - // - // Validate Args ... - if (!ModelState.IsValid) { - XException.InvalidArgs.Throw (); - } - await ValidationProvider - .GroupValidationBuilder () - .AddNotNull (id, item) - .ValidateGroupAsync (); - - // - // Get Result ... - var result = await repository - .UpdateAsync ( - id, - item, - saveChanges : true - ); - - // - return Ok (result - .ToDynamicObject ()); - } catch (Exception ex) { - // - var result = GetExceptionActionResult (ex); - return result; - } - } - - [HttpPost ("UpdateMany")] - public virtual async Task> UpdateMany ( - [FromBody] XBaseRangeRequest request - ) { - // - try { - // - // Validate Args ... - if (!ModelState.IsValid) { - XException.InvalidArgs.Throw (); - } - await ValidationProvider - .GroupValidationBuilder () - .AddNotNull (request) - .AddNotZeroChilds (request.Items) - .ValidateGroupAsync (); - - // - // Get Result ... - var result = await repository - .UpdateRangeAsync ( - request.Items, - saveChanges : true - ); - - // - return Ok (result - .ToDynamicObject ()); - } catch (Exception ex) { - // - var result = GetExceptionActionResult (ex); - return result; - } - } - #endregion - - // - #region Exists ... - [HttpGet ("{id}/IsExists")] - public virtual async Task> IsExists ( - [FromRoute] TKey id, [FromQuery] bool ignoreSoftDeleteds = true - ) { - // - try { - // - // Validate Args ... - ValidationProvider.NotNull (id); - - // - // Get Result ... - var result = await repository - .IsExistsAsync ( - id, - ignoreSoftDeleteds : ignoreSoftDeleteds - ); - - // - return Ok (result - .ToDynamicObject ()); - } catch (Exception ex) { - // - var result = GetExceptionActionResult (ex); - return result; - } - } - #endregion - - // - #region Remove ... - [HttpDelete ("{id}")] - public virtual async Task> Remove ( - [FromRoute] TKey id, - bool softDelete = true - ) { - // - try { - // - // Validate Args ... - ValidationProvider.NotNull (id); - - // - // Get Result ... - var result = await repository - .RemoveAsync ( - id, - saveChanges : true, - softDelete : softDelete - ); - - // - return Ok (result - .ToDynamicObject ()); - } catch (Exception ex) { - // - var result = GetExceptionActionResult (ex); - return result; - } - } - - [HttpPost ("RemoveMany")] - public virtual async Task RemoveMany ( - [FromBody] XBaseRangeRequest request, - bool softDelete = true - ) { - // - try { - // - // Validate Args ... - await ValidationProvider - .GroupValidationBuilder () - .AddNotNull (request) - .AddNotZeroChilds (request.Items) - .ValidateGroupAsync (); - - // - // Get Result ... - await repository - .RemoveRangeAsync ( - request.Items, - saveChanges : true, - softDelete : softDelete - ); - - // - return Ok (); - } catch (Exception ex) { - // - var result = GetExceptionActionResult (ex); - return result; - } - } - #endregion - #endregion + Repository = repository; + DefaultOrderBuilder = defaultOrderBuilder; + DefaultIncludeBuilder = defaultIncludeBuilder; } + #endregion + + // + #region Actions ... + // + #region Add ... + /// + /// Add Specified Item ... + /// + /// + /// + /// + /// + /// + [HttpPost] + public virtual async Task> Add( + [FromBody] TEntity item, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + ValidationProvider.NotNull(item); + + // + // Get Result ... + var result = await Repository + .AddAsync( + item, + saveChanges: true, + cancellationToken: cancellationToken + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Add or Update Specified Item ... + /// + /// + /// + /// + /// + /// + [HttpPost("AddOrUpdate")] + public virtual async Task> AddOrUpdate( + [FromBody] TEntity item, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + ValidationProvider.NotNull(item); + + // + // Get Result ... + var result = await Repository + .AddOrUpdateAsync( + item, + saveChanges: true, + cancellationToken: cancellationToken + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Add a Collection of Items ... + /// + /// + /// + /// + /// + /// + [HttpPost("AddMany")] + public virtual async Task AddMany( + [FromBody] XBaseRangeRequest request, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(request) + .AddNotZeroChilds(request.Items) + .ValidateGroupAsync(); + + // + // Get Result ... + await Repository + .AddRangeAsync( + request.Items, + saveChanges: true, + cancellationToken: cancellationToken + ); + + // + return Ok(); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + + // + #region Retrieve ... + /// + /// Get Specified Item ... + /// + /// + /// + /// + /// + /// + /// + /// + [HttpGet("{id}")] + public virtual async Task> Get( + [FromRoute] TKey id, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + ValidationProvider.NotNull(id); + + // + // Get Result ... + var result = await Repository + .GetAsync( + id, + includeBuilder: useDefaultIncludes + ? DefaultIncludeBuilder + : null, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Get All Items ... + /// + /// + /// + /// + /// + /// + /// + /// + [HttpGet] + public virtual async Task>> GetAll( + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Get Result ... + var result = await Repository + .GetAllAsync( + orderBuilder: useDefaultOrders + ? DefaultOrderBuilder + : null, + includeBuilder: useDefaultIncludes + ? DefaultIncludeBuilder + : null, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Find Specified Item ... + /// + /// + /// + /// + /// + /// + /// + /// + [HttpGet("FindOne/{query}")] + public virtual async Task> FindOne( + [FromRoute] string query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(query); + + // + // Get Result ... + var result = await Repository + .FindOneAsync( + predicate: t => + t.PropValuesContains(query), + includeBuilder: useDefaultIncludes + ? DefaultIncludeBuilder + : null, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Find Many Items ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + [HttpGet("FindMany/{query}")] + public virtual async Task>> FindMany( + [FromRoute] string query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(query); + + // + // Get Result ... + var result = await Repository + .FindManyAsync( + predicate: t => + t.PropValuesContains(query), + orderBuilder: useDefaultOrders + ? DefaultOrderBuilder + : null, + includeBuilder: useDefaultIncludes + ? DefaultIncludeBuilder + : null, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Retrieve Items Based on Query Mechanism ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + [HttpGet("Query")] + public virtual async Task>> Query( + [FromQuery] XQuery query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + ValidationProvider.NotNull(query); + + // + // Get Result ... + var result = await Repository + .QueryAsync( + query: query, + orderBuilder: useDefaultOrders + ? DefaultOrderBuilder + : null, + includeBuilder: useDefaultIncludes + ? DefaultIncludeBuilder + : null, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + + // + #region Update ... + /// + /// Update Item ... + /// + /// + /// + /// + /// + /// + /// + [HttpPut("{id}")] + public virtual async Task> Update( + [FromRoute] TKey id, + [FromBody] TEntity item, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(id, item) + .ValidateGroupAsync(); + + // + // Get Result ... + var result = await Repository + .UpdateAsync( + id: id, + item: item, + saveChanges: true, + cancellationToken: cancellationToken + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Update a Collection of items ... + /// + /// + /// + /// + /// + /// + [HttpPost("UpdateMany")] + public virtual async Task> UpdateMany( + [FromBody] XBaseRangeRequest request, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(request) + .AddNotZeroChilds(request.Items) + .ValidateGroupAsync(); + + // + // Get Result ... + var result = await Repository + .UpdateRangeAsync( + saveChanges: true, + items: request.Items, + cancellationToken: cancellationToken + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + + // + #region Exists ... + /// + ///Check an Item Exists or not ... + /// + /// + /// + /// + /// + /// + /// + [HttpGet("{id}/IsExists")] + public virtual async Task> IsExists( + [FromRoute] TKey id, + [FromQuery] bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + ValidationProvider.NotNull(id); + + // + // Get Result ... + var result = await Repository + .IsExistsAsync( + id: id, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + + // + #region Remove ... + /// + /// Remove an Items ... + /// + /// + /// + /// + /// + /// + /// + [HttpDelete("{id}")] + public virtual async Task> Remove( + [FromRoute] TKey id, + [FromQuery] bool softDelete = true, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + ValidationProvider.NotNull(id); + + // + // Get Result ... + var result = await Repository + .RemoveAsync( + id: id, + saveChanges: true, + softDelete: softDelete, + cancellationToken: cancellationToken + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Remove Many Items ... + /// + /// + /// + /// + /// + /// + /// + [HttpPost("RemoveMany")] + public virtual async Task RemoveMany( + [FromBody] XBaseRangeRequest request, + [FromQuery] bool softDelete = true, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(request) + .AddNotZeroChilds(request.Items) + .ValidateGroupAsync(); + + // + // Get Result ... + await Repository + .RemoveRangeAsync( + saveChanges: true, + items: request.Items, + softDelete: softDelete, + cancellationToken: cancellationToken + ); + + // + return Ok(); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + #endregion + } } \ No newline at end of file diff --git a/Controllers/XBaseEntityDtoController.cs b/Controllers/XBaseEntityDtoController.cs new file mode 100644 index 0000000..d043740 --- /dev/null +++ b/Controllers/XBaseEntityDtoController.cs @@ -0,0 +1,712 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.Logging; +using xCommons.Configurations; +using xCommons.Controllers; +using xCommons.Extensions; +using xCommons.Providers; +using xDataService.Interfaces; +using xExceptions.Constants; +using xModels.Base; +using xModels.Dtos; + +namespace xDataService.Controllers +{ + /// + /// an Abstract Controller for Providing Entity Dto Repository Service Actions Implementatin ... + /// + /// + /// + /// + public abstract class XBaseEntityDtoController : XBaseController, IXEntityDtoControllerActions + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + { + // + #region Properties ... + public IXBaseRepositoryService RepositoryService { get; } + public Func, IOrderedQueryable> DefaultOrderBuilder { get; } + public Func, IIncludableQueryable> DefaultIncludeBuilder { get; } + #endregion + + // + #region Constructor ... + protected XBaseEntityDtoController( + ILogger> logger, + XAppConfiguration appConfiguration, + XValidationProvider validationProvider, + IXBaseRepositoryService repositoryService, + Func, IOrderedQueryable> defaultOrderBuilder = null, + Func, IIncludableQueryable> defaultIncludeBuilder = null + ) : base( + logger, + appConfiguration, + validationProvider + ) + { + // + RepositoryService = repositoryService; + DefaultOrderBuilder = defaultOrderBuilder; + DefaultIncludeBuilder = defaultIncludeBuilder; + } + #endregion + + // + #region Actions ... + // + #region Add ... + /// + /// Add Specified Item ... + /// + /// + /// + /// + /// + /// + [HttpPost] + public virtual async Task> Add( + [FromBody] TDto item, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + ValidationProvider.NotNull(item); + + // + // Get Result ... + var result = await RepositoryService + .AddAsync( + item, + saveChanges: true, + cancellationToken: cancellationToken + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Add or Update Specified Item ... + /// + /// + /// + /// + /// + /// + [HttpPost("AddOrUpdate")] + public virtual async Task> AddOrUpdate( + [FromBody] TDto item, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + ValidationProvider.NotNull(item); + + // + // Get Result ... + var result = await RepositoryService + .AddOrUpdateAsync( + item, + saveChanges: true, + cancellationToken: cancellationToken + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Add a Collection of Items ... + /// + /// + /// + /// + /// + /// + [HttpPost("AddMany")] + public virtual async Task AddMany( + [FromBody] XBaseRangeRequest request, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(request) + .AddNotZeroChilds(request.Items) + .ValidateGroupAsync(); + + // + // Get Result ... + await RepositoryService + .AddRangeAsync( + request.Items, + saveChanges: true, + cancellationToken: cancellationToken + ); + + // + return Ok(); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + + // + #region Retrieve ... + /// + /// Get Specified Item ... + /// + /// + /// + /// + /// + /// + /// + /// + [HttpGet("{id}")] + public virtual async Task> Get( + [FromRoute] TKey id, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + ValidationProvider.NotNull(id); + + // + // Get Result ... + var result = await RepositoryService + .GetAsync( + id, + includeBuilder: useDefaultIncludes + ? DefaultIncludeBuilder + : null, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Get All Items ... + /// + /// + /// + /// + /// + /// + /// + /// + [HttpGet] + public virtual async Task>> GetAll( + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Get Result ... + var result = await RepositoryService + .GetAllAsync( + orderBuilder: useDefaultOrders + ? DefaultOrderBuilder + : null, + includeBuilder: useDefaultIncludes + ? DefaultIncludeBuilder + : null, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Find Specified Item ... + /// + /// + /// + /// + /// + /// + /// + /// + [HttpGet("FindOne/{query}")] + public virtual async Task> FindOne( + [FromRoute] string query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(query); + + // + // Get Result ... + var result = await RepositoryService + .FindOneAsync( + predicate: t => + t.PropValuesContains(query), + includeBuilder: useDefaultIncludes + ? DefaultIncludeBuilder + : null, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Find Many Items ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + [HttpGet("FindMany/{query}")] + public virtual async Task>> FindMany( + [FromRoute] string query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + ValidationProvider.NotEmpty(query); + + // + // Get Result ... + var result = await RepositoryService + .FindManyAsync( + predicate: t => + t.PropValuesContains(query), + orderBuilder: useDefaultOrders + ? DefaultOrderBuilder + : null, + includeBuilder: useDefaultIncludes + ? DefaultIncludeBuilder + : null, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Retrieve Items Based on Query Mechanism ... + /// + /// + /// + /// + /// + /// + /// + /// + /// + [HttpGet("Query")] + public virtual async Task>> Query( + [FromQuery] XQuery query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + ValidationProvider.NotNull(query); + + // + // Get Result ... + var result = await RepositoryService + .QueryAsync( + query: query, + orderBuilder: useDefaultOrders + ? DefaultOrderBuilder + : null, + includeBuilder: useDefaultIncludes + ? DefaultIncludeBuilder + : null, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + + // + #region Update ... + /// + /// Update Item ... + /// + /// + /// + /// + /// + /// + /// + [HttpPut("{id}")] + public virtual async Task> Update( + [FromRoute] TKey id, + [FromBody] TDto item, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(id, item) + .ValidateGroupAsync(); + + // + // Get Result ... + var result = await RepositoryService + .UpdateAsync( + id: id, + item: item, + saveChanges: true, + cancellationToken: cancellationToken + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Update a Collection of items ... + /// + /// + /// + /// + /// + /// + [HttpPost("UpdateMany")] + public virtual async Task> UpdateMany( + [FromBody] XBaseRangeRequest request, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + if (!ModelState.IsValid) + { + XException.InvalidArgs.Throw(); + } + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(request) + .AddNotZeroChilds(request.Items) + .ValidateGroupAsync(); + + // + // Get Result ... + var result = await RepositoryService + .UpdateRangeAsync( + saveChanges: true, + items: request.Items, + cancellationToken: cancellationToken + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + + // + #region Exists ... + /// + ///Check an Item Exists or not ... + /// + /// + /// + /// + /// + /// + /// + [HttpGet("{id}/IsExists")] + public virtual async Task> IsExists( + [FromRoute] TKey id, + [FromQuery] bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + ValidationProvider.NotNull(id); + + // + // Get Result ... + var result = await RepositoryService + .IsExistsAsync( + id: id, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + + // + #region Remove ... + /// + /// Remove an Items ... + /// + /// + /// + /// + /// + /// + /// + [HttpDelete("{id}")] + public virtual async Task> Remove( + [FromRoute] TKey id, + [FromQuery] bool softDelete = true, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + ValidationProvider.NotNull(id); + + // + // Get Result ... + var result = await RepositoryService + .RemoveAsync( + id: id, + saveChanges: true, + softDelete: softDelete, + cancellationToken: cancellationToken + ); + + // + return Ok(result + .ToDynamicObject()); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + + /// + /// Remove Many Items ... + /// + /// + /// + /// + /// + /// + /// + [HttpPost("RemoveMany")] + public virtual async Task RemoveMany( + [FromBody] XBaseRangeRequest request, + [FromQuery] bool softDelete = true, + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Validate Args ... + await ValidationProvider + .GroupValidationBuilder() + .AddNotNull(request) + .AddNotZeroChilds(request.Items) + .ValidateGroupAsync(); + + // + // Get Result ... + await RepositoryService + .RemoveRangeAsync( + saveChanges: true, + items: request.Items, + softDelete: softDelete, + cancellationToken: cancellationToken + ); + + // + return Ok(); + } + catch (Exception ex) + { + // + var result = GetExceptionActionResult(ex); + return result; + } + } + #endregion + #endregion + } +} \ No newline at end of file diff --git a/Db/XUnitOfWorks.cs b/Db/XUnitOfWorks.cs index ca57bea..1975202 100644 --- a/Db/XUnitOfWorks.cs +++ b/Db/XUnitOfWorks.cs @@ -1,3 +1,4 @@ +using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using xDataService.Interfaces; @@ -32,9 +33,12 @@ namespace xDataService.Db { /// /// Save Changes Async /// + /// /// - public async Task SaveChangesAsync () { - return await DbContext.SaveChangesAsync (); + public async Task SaveChangesAsync ( + CancellationToken cancellationToken = default + ) { + return await DbContext.SaveChangesAsync (cancellationToken); } public void Dispose () { diff --git a/EFRepositories/XBaseEFRepository.cs b/EFRepositories/XBaseEFRepository.cs index 5326fd1..4257659 100644 --- a/EFRepositories/XBaseEFRepository.cs +++ b/EFRepositories/XBaseEFRepository.cs @@ -2,41 +2,42 @@ using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Query; using xCommons.Extensions; using xDataService.Configuration; using xDataService.Db; using xDataService.Extensions; using xDataService.Interfaces; using xDataService.Models; -using xExceptions.Constants; using xModels.Base; using xModels.Dtos; namespace xDataService.EFRepositories { /// - /// Base EFCore Base Entity Repository Pattern implementation ... - /// Only Used on EFCore ... + /// a Base Repository Pattern Implementation Specially for EF DBs using + /// EF Core Capabilities ... /// - /// is the Entity type - /// is the Entity Key Type - /// is the DbContext Type + /// + /// + /// public abstract class XBaseEFRepository : IXBaseRepository where T : XBaseEntity where TDbContext : XDbContext { + // + #region Properties ... // public readonly DbSet dbSet; private readonly IXKeyGenerator keyGenerator; public readonly IXUnitOfWorks unitOfWorks; public readonly XDataServiceConfiguration configuration; private readonly IXBaseRepositoryEvents baseRepositoryEvents; - - // - public abstract IQueryable GetFullDbSet(); + #endregion // #region Constructor ... @@ -47,28 +48,44 @@ namespace xDataService.EFRepositories IXBaseRepositoryEvents baseRepositoryEvents = null ) { - this.keyGenerator = keyGenerator; // this.unitOfWorks = unitOfWorks; + this.keyGenerator = keyGenerator; this.configuration = configuration; this.dbSet = unitOfWorks.GetDbSet(); this.baseRepositoryEvents = baseRepositoryEvents; } #endregion + // + #region Actions ... // #region Add ... + /// + /// add a new Entity ... + /// + /// + /// + /// + /// public async Task AddAsync( T item, - bool saveChanges = true + bool saveChanges = true, + CancellationToken cancellationToken = default ) { // // Handle Key ... - item = await HandleKeyAsync(item); + item = await HandleKeyAsync( + item: item, + cancellationToken: cancellationToken + ); // - var entry = await dbSet.AddAsync(item); + var entry = await dbSet.AddAsync( + entity: item, + cancellationToken: cancellationToken + ); // // Check action is Succeeded or not ... @@ -77,7 +94,7 @@ namespace xDataService.EFRepositories { // // Get Modified Count ... - var qResult = await SaveChangesAsync(); + var qResult = await SaveChangesAsync(cancellationToken); // // set isSucceed Value based on Changes ... @@ -91,6 +108,7 @@ namespace xDataService.EFRepositories !baseRepositoryEvents.IsNull() ) { + // baseRepositoryEvents .AddEvent(new XBaseEventModel(entry.Entity)); } @@ -102,42 +120,67 @@ namespace xDataService.EFRepositories null; } + /// + /// add or update an Entity (add if not exists/update if exists) ... + /// + /// + /// + /// + /// public async Task AddOrUpdateAsync( T item, - bool saveChanges = true + bool saveChanges = true, + CancellationToken cancellationToken = default ) { // - var isExists = await IsExistsAsync(GetKey(item)); + var isExists = await IsExistsAsync( + id: GetKey(item), + cancellationToken: cancellationToken + ); if (!isExists) { + // return await AddAsync( - item, - saveChanges + item: item, + saveChanges: saveChanges, + cancellationToken: cancellationToken ); } else { + // return await UpdateAsync( - GetKey(item), - item, - saveChanges + item: item, + id: GetKey(item), + saveChanges: saveChanges, + cancellationToken: cancellationToken ); } } + /// + /// add a range of new Entities ... + /// + /// + /// + /// + /// public async Task AddRangeAsync( IEnumerable items, - bool saveChanges = true + bool saveChanges = true, + CancellationToken cancellationToken = default ) { // + // Preparing Ids and Keys ... await dbSet.AddRangeAsync( await Task .WhenAll( items .Select(async i => await HandleKeyAsync(i)) - ) + ), + cancellationToken: cancellationToken ); // @@ -147,7 +190,7 @@ namespace xDataService.EFRepositories { // // Get Modified Count ... - var qResult = await SaveChangesAsync(); + var qResult = await SaveChangesAsync(cancellationToken); // // set isSucceed Value based on Changes ... @@ -161,27 +204,156 @@ namespace xDataService.EFRepositories !baseRepositoryEvents.IsNull() ) { + // baseRepositoryEvents .AddManyEvent(new XBaseEventModel>(null)); } } #endregion + // + #region Update ... + /// + /// Update an Entity values ... + /// + /// + /// + /// + /// + /// + public async Task UpdateAsync( + TKey id, + T item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + // Get Exists Entity ... + var existsEntity = await GetAsync( + id: id, + ignoreSoftDeleteds: false, + cancellationToken: cancellationToken + ); + + // + // Update Data ... + existsEntity.UpdateData(item); + var entry = dbSet.Attach(existsEntity); + entry.State = EntityState.Modified; + + // + // Check action is Succeeded or not ... + var isSucceed = false; + if (saveChanges) + { + // + // Get Modified Count ... + var qResult = await SaveChangesAsync(cancellationToken); + + // + // set isSucceed Value based on Changes ... + isSucceed = qResult > 0; + } + + // + // Notify Event ... + if ( + isSucceed && + !baseRepositoryEvents.IsNull() + ) + { + // + baseRepositoryEvents + .UpdateEvent(new XBaseEventModel(entry.Entity)); + } + + // + // Return result base on action Succeed ... + return isSucceed ? + entry.Entity : + null; + } + + /// + /// Update a range of Entities ... + /// + /// + /// + /// + /// + public async Task UpdateRangeAsync( + IEnumerable items, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + items + .ToList() + .ForEach(i => + { + var entry = dbSet.Attach(i); + entry.State = EntityState.Modified; + }); + + // + // Check action is Succeeded or not ... + var isSucceed = false; + if (saveChanges) + { + // + // Get Modified Count ... + var qResult = await SaveChangesAsync(cancellationToken); + + // + // set isSucceed Value based on Changes ... + isSucceed = qResult > 0; + } + + // + // Notify Event ... + if ( + isSucceed && + !baseRepositoryEvents.IsNull() + ) + { + // + baseRepositoryEvents + .UpdateManyEvent(new XBaseEventModel>(null)); + } + + // + // Return result base on action Succeed ... + return isSucceed; + } + #endregion + // #region Remove ... + /// + /// remove an Entity by it's Id ... + /// + /// + /// + /// + /// + /// public async Task RemoveAsync( TKey id, bool softDelete = true, - bool saveChanges = true + bool saveChanges = true, + CancellationToken cancellationToken = default ) { // // Retrieve Item ... var item = await GetAsync( id, - ignoreSoftDeleteds: false + ignoreSoftDeleteds: false, + cancellationToken: cancellationToken ); - if (item.IsNull()) + if (item.IsNullOrDefault()) { return null; } @@ -207,7 +379,7 @@ namespace xDataService.EFRepositories { // // Get Modified Count ... - var qResult = await SaveChangesAsync(); + var qResult = await SaveChangesAsync(cancellationToken); // // set isSucceed Value based on Changes ... @@ -221,6 +393,7 @@ namespace xDataService.EFRepositories !baseRepositoryEvents.IsNull() ) { + // baseRepositoryEvents .RemoveEvent(new XBaseEventModel(entry.Entity)); } @@ -232,73 +405,46 @@ namespace xDataService.EFRepositories null; } + /// + /// remove an Entity ... + /// + /// + /// + /// + /// + /// public async Task RemoveAsync( T item, bool softDelete = true, - bool saveChanges = true + bool saveChanges = true, + CancellationToken cancellationToken = default ) { // - // Check item Exists ... - var isExists = await IsExistsAsync( - GetKey(item), - ignoreSoftDeleteds: false + var result = await RemoveAsync( + id: item.Id, + softDelete: softDelete, + saveChanges: saveChanges, + cancellationToken: cancellationToken ); - if (!isExists) - { - return null; - } // - // Handle Remove ... - EntityEntry entry = null; - if (softDelete && configuration.EnableSoftDelete) - { - // - item.Deleted = true; - entry = dbSet.Update(item); - } - else - { - entry = dbSet.Remove(item); - } - - // - // Check action is Succeeded or not ... - var isSucceed = false; - if (saveChanges) - { - // - // Get Modified Count ... - var qResult = await SaveChangesAsync(); - - // - // set isSucceed Value based on Changes ... - isSucceed = qResult > 0; - } - - // - // Notify Event ... - if ( - isSucceed && - !baseRepositoryEvents.IsNull() - ) - { - baseRepositoryEvents - .RemoveEvent(new XBaseEventModel(entry.Entity)); - } - - // - // Return result base on action Succeed ... - return isSucceed ? - entry.Entity : - null; + return result; } + /// + /// remove a range of exists Entities ... + /// + /// + /// + /// + /// + /// public async Task RemoveRangeAsync( IEnumerable items, bool softDelete = true, - bool saveChanges = true + bool saveChanges = true, + CancellationToken cancellationToken = default ) { // @@ -326,7 +472,7 @@ namespace xDataService.EFRepositories { // // Get Modified Count ... - var qResult = await SaveChangesAsync(); + var qResult = await SaveChangesAsync(cancellationToken); // // set isSucceed Value based on Changes ... @@ -340,624 +486,67 @@ namespace xDataService.EFRepositories !baseRepositoryEvents.IsNull() ) { + // baseRepositoryEvents .RemoveManyEvent(new XBaseEventModel>(null)); } } #endregion - // - #region Retrieve ... - public IQueryable AsQueryable() - { - return dbSet.AsQueryable(); - } - - public async Task GetAsync( - TKey id, - bool ignoreSoftDeleteds = true, - bool containsDetail = false - ) - { - // - var result = await FindOneAsync(i => - GetKey(i) - .ToString() == - id - .ToString(), - ignoreSoftDeleteds: ignoreSoftDeleteds, - containsDetail: containsDetail - ); - - // - return result; - } - - public async Task> GetAllAsync( - bool ignoreSoftDeleteds = true, - bool containsDetail = false - ) - { - return await Task.Run(() => - { - // - var result = GetDbSet( - containsDetail: containsDetail - ); - - // - // Handle Soft Deleted Items ... - if (ignoreSoftDeleteds && configuration.EnableSoftDelete) - { - result = result.Where(x => x.Deleted == false); - } - - // - return result.AsEnumerable(); - }); - } - - public async Task FindOneAsync( - Expression> whereClause, - bool ignoreSoftDeleteds = true, - bool containsDetail = false - ) - { - // - // Generate Where Function ... - var whereFunc = whereClause.Compile(); - - // - // Handle Soft Deleted Items ... - Func ignoreSoftDeletedsWhereFunc = null; - if (ignoreSoftDeleteds && configuration.EnableSoftDelete) - { - // - Expression> ignoreSoftDeletedsWhereClause = x => x.Deleted == false; - ignoreSoftDeletedsWhereFunc = ignoreSoftDeletedsWhereClause.Compile(); - } - - // - // Get Enumerable ... - var enumerator = GetDbSet( - containsDetail: containsDetail - ) - .AsAsyncEnumerable(); - - // - T result = null; - await - foreach (var entity in enumerator) - { - // - var isApproved = whereFunc(entity) && - (ignoreSoftDeletedsWhereFunc.IsNull() ? - true : - ignoreSoftDeletedsWhereFunc(entity)); - if (isApproved) - { - // - result = entity; - break; - } - } - - // - return result; - } - - public async Task> FindManyAsync( - Expression> whereClause, - bool ignoreSoftDeleteds = true, - bool containsDetail = false - ) - { - // - // Generate Where Function ... - var whereFunc = whereClause.Compile(); - - // - // Handle Soft Deleted Items ... - Func ignoreSoftDeletedsWhereFunc = null; - if (ignoreSoftDeleteds && configuration.EnableSoftDelete) - { - // - Expression> ignoreSoftDeletedsWhereClause = x => x.Deleted == false; - ignoreSoftDeletedsWhereFunc = ignoreSoftDeletedsWhereClause.Compile(); - } - - // - // Get Enumerable ... - var enumerator = GetDbSet( - containsDetail: containsDetail - ) - .AsAsyncEnumerable(); - - // - var result = new List(); - await - foreach (var entity in enumerator) - { - // - var isApproved = whereFunc(entity) && - (ignoreSoftDeletedsWhereFunc.IsNull() ? - true : - ignoreSoftDeletedsWhereFunc(entity)); - if (isApproved) - { - result.Add(entity); - } - } - - // - return result.AsEnumerable(); - } - - public async Task> QueryAsync( - XQuery query, - bool ignoreSoftDeleteds = true - ) - { - // - // Validate ... - if (query.IsNull()) - { - XException.InvalidArgs.Throw(); - } - - // - // Normalize ... - query = query.NormalizeQuery(configuration); - - // - // Handle Soft Deleted Items ... - Func ignoreSoftDeletedsWhereFunc = null; - if (ignoreSoftDeleteds && configuration.EnableSoftDelete) - { - // - Expression> ignoreSoftDeletedsWhereClause = x => x.Deleted == false; - ignoreSoftDeletedsWhereFunc = ignoreSoftDeletedsWhereClause.Compile(); - } - - // - // Get Enumerable ... - var enumerator = GetDbSet( - containsDetail: query.ContainsDetail - ) - .AsAsyncEnumerable(); - - // - var totalEntities = new List(); - await - foreach (var entity in enumerator) - { - // - var isApproved = (ignoreSoftDeletedsWhereFunc.IsNull() ? - true : - ignoreSoftDeletedsWhereFunc(entity)); - if (isApproved) - { - totalEntities.Add(entity); - } - } - - // - var items = totalEntities.AsEnumerable(); - var totalItemsCount = items.Count(); - - // - // Apply Filter ... - if (!query.Filter.IsNullOrEmpty()) - { - // - items = items - .ApplyFilter(query.Filter); - } - int filteredItemsCount = items.Count(); - - // - // Count Pages ... - var totalPagesCount = query.CountPages(totalItemsCount); - var filteredPagesCount = query.CountPages(filteredItemsCount); - - // - // Apply Paging and Sorting ... - if (totalItemsCount > 0 && - filteredItemsCount > 0) - { - // - // Apply Sorting ... - items = items - .ToList() - .ApplySorting( - query.SortBy, - query.IsAscending - ); - - // - // Apply Paging ... - items = items - .ToList() - .ApplyPaging( - query.Page, - query.PageSize - ); - } - - // - // Prepare Result ... - var result = new XQueryResult - { - Page = query.Page, - Items = items.ToList(), - PageSize = query.PageSize, - TotalPages = totalPagesCount, - TotalItems = totalItemsCount, - TotalFilteredPages = filteredPagesCount, - TotalFilteredItems = filteredItemsCount - }; - - // - return result; - } - - public async Task> ConditionalQueryAsync( - Expression> condition, - XQuery query, - bool ignoreSoftDeleteds = true - ) - { - // - // Validate ... - if (query.IsNull() || condition.IsNull()) - { - XException.InvalidArgs.Throw(); - } - - // - // Normalize ... - query = query.NormalizeQuery(configuration); - - // - // Handle Soft Deleted Items ... - Func ignoreSoftDeletedsWhereFunc = null; - if (ignoreSoftDeleteds && configuration.EnableSoftDelete) - { - // - Expression> ignoreSoftDeletedsWhereClause = x => x.Deleted == false; - ignoreSoftDeletedsWhereFunc = ignoreSoftDeletedsWhereClause.Compile(); - } - - // - // Compile Condition ... - Func conditionFunc = condition.Compile(); - - // - // Get Enumerable ... - var enumerator = GetDbSet( - containsDetail: query.ContainsDetail - ) - .AsAsyncEnumerable(); - - // - var totalEntities = new List(); - await - foreach (var entity in enumerator) - { - // - var isApproved = (ignoreSoftDeletedsWhereFunc.IsNull() ? - true : - ignoreSoftDeletedsWhereFunc(entity)) && - conditionFunc(entity); - if (isApproved) - { - totalEntities.Add(entity); - } - } - - // - var items = totalEntities.AsEnumerable(); - var totalItemsCount = items.Count(); - - // - // Apply Filter ... - if (!query.Filter.IsNullOrEmpty()) - { - // - items = items - .ApplyFilter(query.Filter); - } - int filteredItemsCount = items.Count(); - - // - // Count Pages ... - var totalPagesCount = query.CountPages(totalItemsCount); - var filteredPagesCount = query.CountPages(filteredItemsCount); - - // - // Apply Paging and Sorting ... - if (totalItemsCount > 0 && - filteredItemsCount > 0) - { - // - // Apply Sorting ... - items = items - .ToList() - .ApplySorting( - query.SortBy, - query.IsAscending - ); - - // - // Apply Paging ... - items = items - .ToList() - .ApplyPaging( - query.Page, - query.PageSize - ); - } - - // - // Prepare Result ... - var result = new XQueryResult - { - Page = query.Page, - Items = items.ToList(), - PageSize = query.PageSize, - TotalPages = totalPagesCount, - TotalItems = totalItemsCount, - TotalFilteredPages = filteredPagesCount, - TotalFilteredItems = filteredItemsCount - }; - - // - return result; - } - - // - // TODO: Fix this ... - // public Task> RequestPageAsync ( - // XPageRequest request, - // bool ignoreSoftDeleteds = true, - // bool containsDetail = true - // ) { - // // - // var result = GetDbSet ( - // containsDetail: containsDetail - // ); - - // // - // // Handle Soft Deleted Items ... - // if (ignoreSoftDeleteds) { - // result = result.Where (x => x.Deleted == false); - // } - - // // - // if (request.First.HasValue) { - // // - // if (!request.After.IsNullOrEmpty ()) { - // // - // TKey lastId = XCursorHelper.FromCursor (request.After); - // result = result.Where (x => GetKey (x).ToString() > lastId.ToString()); - // } - - // // - // result = result.Take (request.First.Value); - // } - - // // - // // Apply Sorting ... - // List nodes = null; - // if (!request.SortBy.IsNullOrEmpty ()) { - // nodes = result - // .ApplySorting ( - // request.SortBy, !request.DescendingSort - // ).ToList (); - // } - - // // - // // Claculate Required Info ... - // int totalCount = result.CountAsync ().Result; - // int maxId = nodes.Max (x => Convert.ToInt32 (GetKey (x))); - // int minId = nodes.Min (x => Convert.ToInt32 (GetKey (x))); - // bool hasNextPage = nodes.Any (x => Convert.ToInt32 (GetKey (x)) > maxId); - // bool hasPreviousPage = nodes.Any (x => Convert.ToInt32 (GetKey (x)) < minId); - - // // - // return Task.FromResult (new XPageResponse { - // Nodes = nodes, - // TotalCount = totalCount, - // HasNextPage = hasNextPage, - // HasPreviousPage = hasPreviousPage - // }); - // } - - // - // TODO: Fix this ... - // public Task> RequestConditionalPageAsync ( - // Expression> condition, - // XPageRequest request, - // bool ignoreSoftDeleteds = true, - // bool containsDetail = true - // ) { - // // - // var result = GetDbSet ( - // containsDetail: containsDetail - // ) - // .Where (condition); - - // // - // // Handle Soft Deleted Items ... - // if (ignoreSoftDeleteds) { - // result = result.Where (x => x.Deleted == false); - // } - - // // - // if (request.First.HasValue) { - // // - // if (!request.After.IsNullOrEmpty ()) { - // // - // int lastId = XCursorHelper.FromCursor (request.After); - // result = result.Where (x => Convert.ToInt32 (GetKey (x)) > lastId); - // } - - // // - // result = result.Take (request.First.Value); - // } - - // // - // // Apply Sorting ... - // List nodes = null; - // if (!request.SortBy.IsNullOrEmpty ()) { - // nodes = result - // .ApplySorting ( - // request.SortBy, !request.DescendingSort) - // .ToList (); - // } - - // // - // // Claculate Required Info ... - // int totalCount = result.CountAsync ().Result; - // int maxId = nodes.Max (x => Convert.ToInt32 (GetKey (x))); - // int minId = nodes.Min (x => Convert.ToInt32 (GetKey (x))); - // bool hasNextPage = result.Any (x => Convert.ToInt32 (GetKey (x)) > maxId); - // bool hasPreviousPage = result.Any (x => Convert.ToInt32 (GetKey (x)) < minId); - - // // - // return Task.FromResult (new XPageResponse { - // Nodes = nodes, - // TotalCount = totalCount, - // HasNextPage = hasNextPage, - // HasPreviousPage = hasPreviousPage - // }); - // } - #endregion - - // - #region Update ... - public async Task UpdateAsync( - TKey id, - T item, - bool saveChanges = true - ) - { - // - // Get Exists Entity ... - var existsEntity = await GetAsync( - id, - containsDetail: true, - ignoreSoftDeleteds: false - ); - - // - // Update Data ... - existsEntity.UpdateData(item); - var entry = dbSet.Attach(existsEntity); - entry.State = EntityState.Modified; - - // - // Check action is Succeeded or not ... - var isSucceed = false; - if (saveChanges) - { - // - // Get Modified Count ... - var qResult = await SaveChangesAsync(); - - // - // set isSucceed Value based on Changes ... - isSucceed = qResult > 0; - } - - // - // Notify Event ... - if ( - isSucceed && - !baseRepositoryEvents.IsNull() - ) - { - baseRepositoryEvents - .UpdateEvent(new XBaseEventModel(entry.Entity)); - } - - // - // Return result base on action Succeed ... - return isSucceed ? - entry.Entity : - null; - } - - public async Task UpdateRangeAsync( - IEnumerable items, - bool saveChanges = true - ) - { - // - items - .ToList() - .ForEach(i => - { - var entry = dbSet.Attach(i); - entry.State = EntityState.Modified; - }); - - // - // Check action is Succeeded or not ... - var isSucceed = false; - if (saveChanges) - { - // - // Get Modified Count ... - var qResult = await SaveChangesAsync(); - - // - // set isSucceed Value based on Changes ... - isSucceed = qResult > 0; - } - - // - // Notify Event ... - if ( - isSucceed && - !baseRepositoryEvents.IsNull() - ) - { - baseRepositoryEvents - .UpdateManyEvent(new XBaseEventModel>(null)); - } - - // - // Return result base on action Succeed ... - return isSucceed; - } - #endregion - // #region Count ... - public async Task CountAsync(bool ignoreSoftDeleteds = true) + /// + /// count all exists Entities ... + /// + /// + /// + /// + public async Task CountAsync( + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) { // - var dbSet = GetDbSet(); + var result = 0; // - if (ignoreSoftDeleteds && configuration.EnableSoftDelete) + if (ignoreSoftDeleteds) { - return await dbSet.CountAsync(i => i.Deleted == false); + // + result = await dbSet.CountAsync( + predicate: x => !x.Deleted, + cancellationToken: cancellationToken + ); } else { - return await dbSet.CountAsync(); + result = await dbSet.CountAsync(cancellationToken); } + + // + return result; } + /// + /// count all exists Entities Pages by providing page size ... + /// + /// + /// + /// + /// + /// public async Task PagesCountAsync( int pageSize, - int? totalItems = null + int? totalItems = null, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default ) { // - int count = totalItems.HasValue ? totalItems.Value : await CountAsync(); + int count = totalItems.HasValue ? totalItems.Value : await CountAsync( + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); int pagesCount = count / pageSize; // @@ -972,58 +561,371 @@ namespace xDataService.EFRepositories #endregion // - #region Exists ... - public async Task IsExistsAsync( - TKey id, - bool ignoreSoftDeleteds = true + #region Retrieve ... + /// + /// retrieve whole items as queryable ... + /// + /// a flag for Tracking behaviour + /// an Expression for Filter Items ... + /// an Order Builder expression for Ordering Query ... + /// an Include Builder expression for Including Navigation Properties ... + /// + public IQueryable AsQueryable( + bool asNoTracking = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null ) { // - var result = await FindOneAsync(i => - GetKey(i) - .ToString() == id - .ToString(), - ignoreSoftDeleteds: ignoreSoftDeleteds, - containsDetail: false - ); + var result = dbSet + .AsQueryable(); // - Detach(result); + // Apply Predicate ... + if (!predicate.IsNull()) + { + result = result + .Where(predicate); + } // - return !result.IsNull(); + // Apply Includes ... + if (!includeBuilder.IsNull()) + { + result = includeBuilder(result); + } + + // + // Apply Ordering ... + if (!orderBuilder.IsNull()) + { + result = orderBuilder(result); + } + + // + return result; + } + + /// + /// retrieve an Entity by it's Id ... + /// + /// + /// + /// + /// + /// + public async Task GetAsync( + TKey id, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + orderBuilder: null, + predicate: predicator, + includeBuilder: includeBuilder + ) + .Where(x => GetKey(x).ToString() == id.ToString()) + .FirstOrDefaultAsync(cancellationToken); + + // + return result; + } + + /// + /// retrieve all exists Entities ... + /// + /// + /// + /// + /// + /// + public async Task> GetAllAsync( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + predicate: predicator, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder + ) + .ToListAsync(cancellationToken); + + // + return result; + } + + /// + /// retrieve all exists Entities + /// as Async Enumerable ... + /// + /// + /// + /// + /// + public IAsyncEnumerable GetAllAsAsyncEnumerable( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = AsQueryable( + asNoTracking: true, + predicate: predicator, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder + ) + .AsAsyncEnumerable(); + + // + return result; + + } + + /// + /// find an Entity by providing a Conditional Expression ... + /// + /// + /// + /// + /// + /// + public async Task FindOneAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + orderBuilder: null, + predicate: predicator, + includeBuilder: includeBuilder + ) + .Where(predicate) + .FirstOrDefaultAsync(cancellationToken); + + // + return result; + } + + /// + /// find a collection of Entities by proving a Conditional Expression ... + /// + /// + /// + /// + /// + /// + /// + public async Task> FindManyAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + predicate: predicator, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder + ) + .Where(predicate) + .ToListAsync(cancellationToken); + + // + return result; + } + + /// + /// retrieve Entities based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + /// + /// + public async Task> QueryAsync( + XQuery query, + bool ignoreSoftDeleteds = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Normalize Query ... + query = query.NormalizeQuery(configuration); + + // + var items = await GetAllAsync(); + var totalItemsCount = items.Count(); + + // + // Apply Filter ... + if (!query.Filter.IsNullOrEmpty()) + { + // + items = items + .ApplyFilter(query.Filter); + } + int filteredItemsCount = items.Count(); + + // + // Count Pages ... + var totalPagesCount = query.CountPages(totalItemsCount); + var filteredPagesCount = query.CountPages(filteredItemsCount); + + // + // Apply Paging and Sorting ... + if (totalItemsCount > 0 && + filteredItemsCount > 0) + { + // + // Apply Sorting ... + items = items + .ToList() + .ApplySorting( + query.SortBy, + query.IsAscending + ); + + // + // Apply Paging ... + items = items + .ToList() + .ApplyPaging( + query.Page, + query.PageSize + ); + } + + // + // Prepare Result ... + var result = new XQueryResult + { + Page = query.Page, + Items = items.ToList(), + PageSize = query.PageSize, + TotalPages = totalPagesCount, + TotalItems = totalItemsCount, + TotalFilteredPages = filteredPagesCount, + TotalFilteredItems = filteredItemsCount + }; + + // + return result; + } + #endregion + + // + #region Exists ... + /// + /// Check an Entity exists or not ... + /// + /// + /// + /// + /// + public async Task IsExistsAsync( + TKey id, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + orderBuilder: null, + includeBuilder: null, + predicate: predicator + ) + .AnyAsync( + cancellationToken: cancellationToken, + predicate: x => GetKey(x).ToString() == id.ToString()); + + // + return result; } #endregion // #region Unit Of Work ... - public async Task SaveChangesAsync() + /// + /// Save all unsaved Transactions on DbContext ... + /// used fo Unit Of Works Design Pattern ... + /// + /// + /// + public async Task SaveChangesAsync( + CancellationToken cancellationToken = default + ) { - return await unitOfWorks.SaveChangesAsync(); + return await unitOfWorks.SaveChangesAsync(cancellationToken); } #endregion // - #region Keys ... - public void SetKey( - ref T item, - TKey id - ) - { - // - var props = item.GetType().GetProperties(); - var keyProp = props.FirstOrDefault(p => p.Name == "Id"); - if (keyProp.IsNull()) - { - return; - } - - // - Type t = Nullable.GetUnderlyingType(keyProp.PropertyType) ?? keyProp.PropertyType; - object safeValue = (id == null) ? null : Convert.ChangeType(id, t); - keyProp.SetValue(item, safeValue, null); - } - + #region Key ... + /// + /// Retrieve Key of Entity ... + /// + /// + /// public TKey GetKey(T item) { // @@ -1058,7 +960,40 @@ namespace xDataService.EFRepositories return keyString.FromJSON(); } - public async Task HandleKeyAsync(T item) + /// + /// Set Key of Entity ... + /// + /// + /// + public void SetKey( + ref T item, + TKey id + ) + { + // + var props = item.GetType().GetProperties(); + var keyProp = props.FirstOrDefault(p => p.Name == "Id"); + if (keyProp.IsNull()) + { + return; + } + + // + Type t = Nullable.GetUnderlyingType(keyProp.PropertyType) ?? keyProp.PropertyType; + object safeValue = (id == null) ? null : Convert.ChangeType(id, t); + keyProp.SetValue(item, safeValue, null); + } + + /// + /// Handle Checking Key ... + /// + /// + /// + /// + public async Task HandleKeyAsync( + T item, + CancellationToken cancellationToken = default + ) { // var keyType = typeof(TKey); @@ -1088,6 +1023,10 @@ namespace xDataService.EFRepositories // #region Detach ... + /// + /// Detach an Entity ... + /// + /// public void Detach(T item) { // @@ -1100,6 +1039,10 @@ namespace xDataService.EFRepositories Entry(item).State = EntityState.Detached; } + /// + /// Detach an Enumerable of Entities ... + /// + /// public void Detach(IEnumerable items) { // @@ -1117,6 +1060,10 @@ namespace xDataService.EFRepositories }); } + /// + /// Detach a Query Result of Entity ... + /// + /// public void Detach(XQueryResult query) { // @@ -1128,18 +1075,7 @@ namespace xDataService.EFRepositories // Detach(query.Items); } - - public void Detach(XPageResponse page) - { - // - if (page.IsNull() || !page.Nodes.HasChild()) - { - return; - } - - // - Detach(page.Nodes); - } + #endregion #endregion // @@ -1163,25 +1099,10 @@ namespace xDataService.EFRepositories { return unitOfWorks.DbContext.Entry(item); } + #endregion - public IQueryable GetDbSet( - bool containsDetail = false - ) - { - // - IQueryable result = null; - if (!containsDetail) - { - result = dbSet; - } - else - { - result = GetFullDbSet(); - } - - // - return result; - } + // + #region Private ... #endregion } } \ No newline at end of file diff --git a/Extensions/XGraphQLExtensions.cs b/Extensions/XGraphQLExtensions.cs index 8ca6de9..a6bccfb 100644 --- a/Extensions/XGraphQLExtensions.cs +++ b/Extensions/XGraphQLExtensions.cs @@ -13,10 +13,6 @@ namespace xDataService.Extensions { return source.GetArgument (XGraphQLHelper.IgnoreSoftDeletedArgument.Name); } - public static bool GetContainsDetailArgument (this IResolveFieldContext source) { - return source.GetArgument (XGraphQLHelper.ContainsDetailArgument.Name); - } - public static string GetSearchQueryArgument (this IResolveFieldContext source) { return source.GetArgument (XGraphQLHelper.SearchQueryArgument.Name); } diff --git a/GraphQL/XBaseGraphQLQuery.cs b/GraphQL/XBaseGraphQLQuery.cs index 6c7ca14..06783f6 100644 --- a/GraphQL/XBaseGraphQLQuery.cs +++ b/GraphQL/XBaseGraphQLQuery.cs @@ -41,15 +41,14 @@ namespace xDataService.GraphQL { name: helper.GetInQuerySingleName (), arguments: new QueryArguments ( XGraphQLHelper.GetIdArgument (), - XGraphQLHelper.IgnoreSoftDeletedArgument, - XGraphQLHelper.ContainsDetailArgument + XGraphQLHelper.IgnoreSoftDeletedArgument ), resolve : async context => await repository - .GetAsync ( + .GetAsync ( + includeBuilder: null, id: context.GetIdArgument (), - ignoreSoftDeleteds: context.GetIgnoreSoftDeletedArgument (), - containsDetail: context.GetContainsDetailArgument () + ignoreSoftDeleteds: context.GetIgnoreSoftDeletedArgument () ) .ToDynamicObject () ); @@ -59,14 +58,14 @@ namespace xDataService.GraphQL { FieldAsync> ( name: helper.GetInQueryCollectionName (), arguments: new QueryArguments ( - XGraphQLHelper.IgnoreSoftDeletedArgument, - XGraphQLHelper.ContainsDetailArgument + XGraphQLHelper.IgnoreSoftDeletedArgument ), resolve : async context => await repository .GetAllAsync ( - ignoreSoftDeleteds: context.GetIgnoreSoftDeletedArgument (), - containsDetail: context.GetContainsDetailArgument () + orderBuilder: null, + includeBuilder: null, + ignoreSoftDeleteds: context.GetIgnoreSoftDeletedArgument () ) .ToDynamicObject () ); @@ -77,8 +76,7 @@ namespace xDataService.GraphQL { name: helper.GetFindOneName (), arguments: new QueryArguments ( XGraphQLHelper.SearchQueryArgument, - XGraphQLHelper.IgnoreSoftDeletedArgument, - XGraphQLHelper.ContainsDetailArgument + XGraphQLHelper.IgnoreSoftDeletedArgument ), resolve : async (context) => { // @@ -89,9 +87,9 @@ namespace xDataService.GraphQL { // return await repository .FindOneAsync ( - whereClause: whereClase, - ignoreSoftDeleteds: context.GetIgnoreSoftDeletedArgument (), - containsDetail: context.GetContainsDetailArgument () + includeBuilder: null, + predicate: whereClase, + ignoreSoftDeleteds: context.GetIgnoreSoftDeletedArgument () ) .ToDynamicObject (); } @@ -103,8 +101,7 @@ namespace xDataService.GraphQL { name: helper.GetFindManyName (), arguments: new QueryArguments ( XGraphQLHelper.SearchQueryArgument, - XGraphQLHelper.IgnoreSoftDeletedArgument, - XGraphQLHelper.ContainsDetailArgument + XGraphQLHelper.IgnoreSoftDeletedArgument ), resolve : async (context) => { // @@ -115,9 +112,10 @@ namespace xDataService.GraphQL { // return await repository .FindManyAsync ( - whereClause: whereClase, - ignoreSoftDeleteds: context.GetIgnoreSoftDeletedArgument (), - containsDetail: context.GetContainsDetailArgument () + orderBuilder: null, + includeBuilder: null, + predicate: whereClase, + ignoreSoftDeleteds: context.GetIgnoreSoftDeletedArgument () ) .ToDynamicObject (); } @@ -134,6 +132,9 @@ namespace xDataService.GraphQL { resolve : async context => await repository .QueryAsync ( + predicate: null, + orderBuilder: null, + includeBuilder: null, query: context.GetQueryArgument (), ignoreSoftDeleteds: context.GetIgnoreSoftDeletedArgument () ) @@ -144,9 +145,14 @@ namespace xDataService.GraphQL { // Count ... FieldAsync ( name: helper.GetCountName (), + arguments: new QueryArguments ( + XGraphQLHelper.IgnoreSoftDeletedArgument + ), resolve: async context => await repository - .CountAsync () + .CountAsync ( + ignoreSoftDeleteds: context.GetIgnoreSoftDeletedArgument () + ) ); // @@ -154,11 +160,13 @@ namespace xDataService.GraphQL { FieldAsync ( name: helper.GetExistsName (), arguments: new QueryArguments ( - XGraphQLHelper.GetIdArgument () + XGraphQLHelper.GetIdArgument (), + XGraphQLHelper.IgnoreSoftDeletedArgument ), resolve : async context => await repository.IsExistsAsync ( - context.GetIdArgument () + id: context.GetIdArgument (), + ignoreSoftDeleteds: context.GetIgnoreSoftDeletedArgument () ) ); #endregion diff --git a/Helpers/XGraphQLHelper.cs b/Helpers/XGraphQLHelper.cs index 7d7b2d9..51b3074 100644 --- a/Helpers/XGraphQLHelper.cs +++ b/Helpers/XGraphQLHelper.cs @@ -18,12 +18,6 @@ namespace xDataService.Constants { Description = "determines result should ignore soft deleted items or not ..." }; - public static QueryArgument ContainsDetailArgument = new QueryArgument { - Name = "containsDetail", - DefaultValue = false, - Description = "determines result should contains navigation properties or not ..." - }; - public static QueryArgument SearchQueryArgument = new QueryArgument { Name = "searchQuery", Description = "the value which had to looking for ..." diff --git a/InMemRepositories/XBaseInMemoryRepositoy.cs b/InMemRepositories/XBaseInMemoryRepositoy.cs index 0e07f7e..c74d0da 100644 --- a/InMemRepositories/XBaseInMemoryRepositoy.cs +++ b/InMemRepositories/XBaseInMemoryRepositoy.cs @@ -3,16 +3,26 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using System.Threading; using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; using xCommons.Extensions; using xDataService.Configuration; using xDataService.Extensions; using xDataService.Interfaces; +using xDataService.Models; using xModels.Base; using xModels.Dtos; namespace xDataService.InMemRepositories { + /// + /// a Base Repository Pattern Implementation Specially for InMemory Stores using + /// EF Core Capabilities ... + /// + /// + /// public abstract class XBaseInMemoryRepositoy : IXBaseRepository where TEntity : XBaseEntity { @@ -32,82 +42,169 @@ namespace xDataService.InMemRepositories IXBaseRepositoryEvents baseRepositoryEvents = null ) { - this.configuration = configuration; this.keyGenerator = keyGenerator; + this.configuration = configuration; this.baseRepositoryEvents = baseRepositoryEvents; } #endregion + // + #region Actions ... // #region Add ... - public async Task AddAsync(TEntity item, bool saveChanges = true) + /// + /// add a new Entity ... + /// + /// + /// + /// + /// + public async Task AddAsync( + TEntity item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) { // // Handle Key ... - item = await HandleKeyAsync(item); + item = await HandleKeyAsync( + item: item, + cancellationToken: cancellationToken + ); + + // + var isSucceed = false; // // Add Item to Dictionary ... - store[item.Id] = item; + try + { + // + store[item.Id] = item; + isSucceed = true; + } + catch + { + isSucceed = false; + } + + // + // Notify Event ... + if ( + isSucceed && + !baseRepositoryEvents.IsNull() + ) + { + // + baseRepositoryEvents + .AddEvent(new XBaseEventModel(item)); + } // // Return Item ... return item; } - public async Task AddOrUpdateAsync(TEntity item, bool saveChanges = true) + /// + /// add or update an Entity (add if not exists/update if exists) ... + /// + /// + /// + /// + /// + public async Task AddOrUpdateAsync( + TEntity item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) { // - var isExists = await IsExistsAsync(item.Id); + var isExists = await IsExistsAsync( + id: item.Id, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + + // + var isSucceed = false; if (isExists) { - item = await UpdateAsync(item.Id, item); + // + try + { + // + item = await UpdateAsync( + item: item, + id: item.Id, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + isSucceed = true; + } + catch + { + isSucceed = false; + } } else { - item = await AddAsync(item); + // + try + { + // + item = await AddAsync( + item: item, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + isSucceed = true; + } + catch + { + isSucceed = false; + } + } + + // + // Notify Event ... + if ( + isSucceed && + !baseRepositoryEvents.IsNull() + ) + { + // + if (isExists) + { + // + baseRepositoryEvents + .UpdateEvent(new XBaseEventModel(item)); + } + else + { + // + baseRepositoryEvents + .AddEvent(new XBaseEventModel(item)); + } } // return item; } - public async Task AddRangeAsync(IEnumerable items, bool saveChanges = true) - { - // - foreach (var item in items) - { - var handled = await HandleKeyAsync(item); - store[handled.Id] = handled; - } - } - #endregion - - // - #region Remove ... - public async Task RemoveAsync(TKey id, bool softDelete = true, bool saveChanges = true) - { - // - var isExists = await IsExistsAsync(id); - if (!isExists) - { - return null; - } - - // - var result = store[id]; - store.TryRemove(id, out _); - - // - return result; - } - - public async Task RemoveAsync(TEntity item, bool softDelete = true, bool saveChanges = true) - { - return await RemoveAsync(item.Id); - } - - public async Task RemoveRangeAsync(IEnumerable items, bool softDelete = true, bool saveChanges = true) + /// + /// add a range of new Entities ... + /// + /// + /// + /// + /// + public async Task AddRangeAsync( + IEnumerable items, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) { // var isValid = !items.IsNull() && items.HasChild(); @@ -119,55 +216,589 @@ namespace xDataService.InMemRepositories // foreach (var item in items) { - await RemoveAsync(item); + // + await AddAsync( + item: item, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + } + + // + // Notify Event ... + if ( + isValid && + !baseRepositoryEvents.IsNull() + ) + { + // + baseRepositoryEvents + .AddManyEvent(new XBaseEventModel>(null)); } } #endregion // - #region Retrieve ... - public IQueryable AsQueryable() - { - return store.Values.AsQueryable(); - } - - public async Task GetAsync(TKey id, bool ignoreSoftDeleteds = true, bool containsDetail = false) + #region Update ... + /// + /// Update an Entity values ... + /// + /// + /// + /// + /// + /// + public async Task UpdateAsync( + TKey id, + TEntity item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) { // - var isExists = await IsExistsAsync(id); + var isExists = await IsExistsAsync( + id: id, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); if (!isExists) { return null; } // - return store[id]; + var isSucceed = false; + var exists = store[id]; + + // + try + { + // + exists = exists.UpdateData( + exists, + propertyBlackList: new List { nameof(exists.Id) } + ); + store[id] = exists; + + // + isSucceed = true; + } + catch + { + isSucceed = false; + } + + // + // Notify Event ... + if ( + isSucceed && + !baseRepositoryEvents.IsNull() + ) + { + // + baseRepositoryEvents + .UpdateEvent(new XBaseEventModel(item)); + } + + // + return exists; } - public async Task> GetAllAsync(bool ignoreSoftDeleteds = true, bool containsDetail = false) + /// + /// Update a range of Entities ... + /// + /// + /// + /// + /// + public async Task UpdateRangeAsync( + IEnumerable items, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) { // - var result = store.Values.AsEnumerable(); - return await Task.FromResult(result); - } + var result = false; + var isValid = !items.IsNull() && items.HasChild(); + if (!isValid) + { + return result; + } - public async Task FindOneAsync(Expression> whereClause, bool ignoreSoftDeleteds = true, bool containsDetail = false) + // + foreach (var item in items) + { + // + var updated = await UpdateAsync( + item: item, + id: item.Id, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + if (!updated.IsNullOrDefault() && !result) + { + result = true; + } + } + + // + return result; + } + #endregion + + // + #region Remove ... + /// + /// remove an Entity by it's Id ... + /// + /// + /// + /// + /// + /// + public async Task RemoveAsync( + TKey id, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) { // - var func = whereClause.Compile(); - var result = store.Values.FirstOrDefault(x => func(x)); - return await Task.FromResult(result); + var isExists = await IsExistsAsync( + id: id, + cancellationToken: cancellationToken + ); + if (!isExists) + { + return null; + } + + // + TEntity result = null; + if (softDelete && configuration.EnableSoftDelete) + { + // + store[id].Deleted = true; + result = store[id]; + } + else + { + // + result = store[id]; + store.TryRemove(id, out _); + } + + // + return result; } - public async Task> FindManyAsync(Expression> whereClause, bool ignoreSoftDeleteds = true, bool containsDetail = false) + /// + /// remove an Entity ... + /// + /// + /// + /// + /// + /// + public async Task RemoveAsync( + TEntity item, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) { // - var func = whereClause.Compile(); - var result = store.Values.Where(x => func(x)); - return await Task.FromResult(result); + var result = await RemoveAsync( + id: item.Id, + softDelete: softDelete, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + return result; } - public async Task> QueryAsync(XQuery query, bool ignoreSoftDeleteds = true) + /// + /// remove a range of exists Entities ... + /// + /// + /// + /// + /// + /// + public async Task RemoveRangeAsync( + IEnumerable items, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + var isValid = !items.IsNull() && items.HasChild(); + if (!isValid) + { + return; + } + + // + foreach (var item in items) + { + // + await RemoveAsync( + item: item, + softDelete: softDelete, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + } + } + #endregion + + // + #region Count ... + /// + /// count all exists Entities ... + /// + /// + /// + /// + public async Task CountAsync( + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + orderBuilder: null, + includeBuilder: null, + predicate: predicator + ) + .CountAsync(cancellationToken); + + // + return result; + } + + /// + /// count all exists Entities Pages by providing page size ... + /// + /// + /// + /// + /// + /// + public async Task PagesCountAsync( + int pageSize, + int? totalItems = null, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + int count = totalItems.HasValue ? totalItems.Value : await CountAsync( + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + int pagesCount = count / pageSize; + + // + if (count % pageSize > 0) + { + pagesCount++; + } + + // + return pagesCount; + } + #endregion + + // + #region Exists ... + /// + /// Check an Entity exists or not ... + /// + /// + /// + /// + /// + public async Task IsExistsAsync( + TKey id, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + orderBuilder: null, + includeBuilder: null, + predicate: predicator + ) + .AnyAsync( + cancellationToken: cancellationToken, + predicate: x => GetKey(x).ToString() == id.ToString()); + + // + return result; + } + #endregion + + // + #region Retrieve ... + /// + /// retrieve whole items as queryable ... + /// + /// a flag for Tracking behaviour + /// an Expression for Filter Items ... + /// an Order Builder expression for Ordering Query ... + /// an Include Builder expression for Including Navigation Properties ... + /// + public IQueryable AsQueryable( + bool asNoTracking = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null + ) + { + // + var result = store.Values.AsQueryable(); + + // + // Predicate ... + if (!predicate.IsNull()) + { + result = result.Where(predicate); + } + + // + // Apply Orders ... + if (!orderBuilder.IsNull()) + { + result = orderBuilder(result); + } + + // + // Apply Includes ... + if (!includeBuilder.IsNull()) + { + result = includeBuilder(result); + } + + // + if (asNoTracking) + { + result = result.AsNoTracking(); + } + + // + return result; + } + + /// + /// retrieve an Entity by it's Id ... + /// + /// + /// + /// + /// + /// + public async Task GetAsync( + TKey id, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + orderBuilder: null, + predicate: predicator, + includeBuilder: includeBuilder + ) + .Where(x => GetKey(x).ToString() == id.ToString()) + .FirstOrDefaultAsync(cancellationToken); + + // + return result; + } + + /// + /// retrieve all exists Entities ... + /// + /// + /// + /// + /// + /// + public async Task> GetAllAsync( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + orderBuilder: null, + predicate: predicator, + includeBuilder: includeBuilder + ) + .ToListAsync(cancellationToken); + + // + return result; + } + + /// + /// retrieve all exists Entities + /// as Async Enumerable ... + /// + /// + /// + /// + /// + public IAsyncEnumerable GetAllAsAsyncEnumerable( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null + ) + { + // + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = AsQueryable( + asNoTracking: true, + predicate: predicator, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder + ) + .AsAsyncEnumerable(); + + // + return result; + } + + /// + /// find an Entity by providing a Conditional Expression ... + /// + /// + /// + /// + /// + /// + public async Task FindOneAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + orderBuilder: null, + predicate: predicator, + includeBuilder: includeBuilder + ) + .Where(predicate) + .FirstOrDefaultAsync(cancellationToken); + + // + return result; + } + + /// + /// find a collection of Entities by proving a Conditional Expression ... + /// + /// + /// + /// + /// + /// + /// + public async Task> FindManyAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + orderBuilder: null, + predicate: predicator, + includeBuilder: includeBuilder + ) + .Where(predicate) + .ToListAsync(cancellationToken); + + // + return result; + } + + /// + /// retrieve Entities based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + /// + /// + public async Task> QueryAsync( + XQuery query, + bool ignoreSoftDeleteds = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) { // // Normalize Query ... @@ -232,174 +863,35 @@ namespace xDataService.InMemRepositories // return result; } + #endregion - public async Task> ConditionalQueryAsync(Expression> whereClause, XQuery query, bool ignoreSoftDeleteds = true) + // + #region Unit Of Work ... + /// + /// Save all unsaved Transactions on DbContext ... + /// used fo Unit Of Works Design Pattern ... + /// + /// + /// + public async Task SaveChangesAsync( + CancellationToken cancellationToken = default + ) { // - // Normalize Query ... - query = query.NormalizeQuery(configuration); - - // - var func = whereClause.Compile(); - - // - var items = (await GetAllAsync()).Where(x => func(x)); - var totalItemsCount = items.Count(); - - // - // Apply Filter ... - if (!query.Filter.IsNullOrEmpty()) - { - // - items = items - .ApplyFilter(query.Filter); - } - int filteredItemsCount = items.Count(); - - // - // Count Pages ... - var totalPagesCount = query.CountPages(totalItemsCount); - var filteredPagesCount = query.CountPages(filteredItemsCount); - - // - // Apply Paging and Sorting ... - if (totalItemsCount > 0 && - filteredItemsCount > 0) - { - // - // Apply Sorting ... - items = items - .ToList() - .ApplySorting( - query.SortBy, - query.IsAscending - ); - - // - // Apply Paging ... - items = items - .ToList() - .ApplyPaging( - query.Page, - query.PageSize - ); - } - - // - // Prepare Result ... - var result = new XQueryResult - { - Page = query.Page, - Items = items.ToList(), - PageSize = query.PageSize, - TotalPages = totalPagesCount, - TotalItems = totalItemsCount, - TotalFilteredPages = filteredPagesCount, - TotalFilteredItems = filteredItemsCount - }; - - // - return result; + return await Task.Run( + () => 0, + cancellationToken + ); } #endregion // - #region Update ... - public async Task UpdateAsync(TKey id, TEntity item, bool saveChanges = true) - { - // - var isExists = await IsExistsAsync(id); - if (!isExists) - { - return null; - } - - // - var exists = store[id]; - exists = exists.UpdateData(exists, propertyBlackList: new List { nameof(exists.Id) }); - store[id] = exists; - - // - return exists; - } - - public async Task UpdateRangeAsync(IEnumerable items, bool saveChanges = true) - { - // - var result = false; - var isValid = !items.IsNull() && items.HasChild(); - if (!isValid) - { - return result; - } - - // - foreach (var item in items) - { - // - var updated = await UpdateAsync(item.Id, item); - if (!updated.IsNullOrDefault() && !result) - { - result = true; - } - } - - // - return result; - } - #endregion - - // - #region Count ... - public async Task CountAsync(bool ignoreSoftDeleteds = true) - { - // - var result = store.Count(); - return await Task.FromResult(result); - } - - public Task PagesCountAsync(int pageSize, int? totalItems = null) - { - return Task.FromResult(0); - } - #endregion - - // - #region Exists ... - public async Task IsExistsAsync(TKey id, bool ignoreSoftDeleteds = true) - { - // - var result = store.Keys.Contains(id); - return await Task.FromResult(result); - } - #endregion - - // - #region Unit Of Works ... - public async Task SaveChangesAsync() - { - return await Task.FromResult(0); - } - #endregion - - // - #region Keys ... - public void SetKey(ref TEntity item, TKey id) - { - // - var props = item.GetType().GetProperties(); - var keyProp = props.FirstOrDefault(p => p.Name == "Id"); - if (keyProp.IsNull()) - { - return; - } - - // - Type t = Nullable.GetUnderlyingType(keyProp.PropertyType) ?? keyProp.PropertyType; - object safeValue = (id == null) ? null : Convert.ChangeType(id, t); - keyProp.SetValue(item, safeValue, null); - } - + #region Key ... + /// + /// Retrieve Key of Entity ... + /// + /// + /// public TKey GetKey(TEntity item) { // @@ -434,7 +926,40 @@ namespace xDataService.InMemRepositories return keyString.FromJSON(); } - public async Task HandleKeyAsync(TEntity item) + /// + /// Set Key of Entity ... + /// + /// + /// + public void SetKey( + ref TEntity item, + TKey id + ) + { + // + var props = item.GetType().GetProperties(); + var keyProp = props.FirstOrDefault(p => p.Name == "Id"); + if (keyProp.IsNull()) + { + return; + } + + // + Type t = Nullable.GetUnderlyingType(keyProp.PropertyType) ?? keyProp.PropertyType; + object safeValue = (id == null) ? null : Convert.ChangeType(id, t); + keyProp.SetValue(item, safeValue, null); + } + + /// + /// Handle Checking Key ... + /// + /// + /// + /// + public async Task HandleKeyAsync( + TEntity item, + CancellationToken cancellationToken = default + ) { // var keyType = typeof(TKey); @@ -464,17 +989,24 @@ namespace xDataService.InMemRepositories // #region Detach ... - public void Detach(TEntity item) - { } + /// + /// Detach an Entity ... + /// + /// + public void Detach(TEntity item) { } - public void Detach(IEnumerable items) - { } + /// + /// Detach an Enumerable of Entities ... + /// + /// + public void Detach(IEnumerable items) { } - public void Detach(XQueryResult query) - { } - - public void Detach(XPageResponse page) - { } + /// + /// Detach a Query Result of Entity ... + /// + /// + public void Detach(XQueryResult query) { } + #endregion #endregion // @@ -482,5 +1014,9 @@ namespace xDataService.InMemRepositories public void Dispose() { } #endregion + + // + #region Private ... + #endregion } } \ No newline at end of file diff --git a/Interfaces/IXBaseRepository.cs b/Interfaces/IXBaseRepository.cs index 9585c33..9a829ec 100644 --- a/Interfaces/IXBaseRepository.cs +++ b/Interfaces/IXBaseRepository.cs @@ -2,18 +2,23 @@ using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using System.Threading; using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.Query; using xModels.Base; using xModels.Dtos; -namespace xDataService.Interfaces { +namespace xDataService.Interfaces +{ /// - /// a Base Repository Interface for Manipulate - /// an Entity in DataBase + /// Base Repository Pattern Contracts in XDashboard's Data Service ... + /// use for Data Manipulation ... /// /// + /// public interface IXBaseRepository : IDisposable - where T : XBaseEntity { + where T : XBaseEntity + { // #region Add ... /// @@ -21,10 +26,12 @@ namespace xDataService.Interfaces { /// /// /// + /// /// - Task AddAsync ( + Task AddAsync( T item, - bool saveChanges = true + bool saveChanges = true, + CancellationToken cancellationToken = default ); /// @@ -32,10 +39,12 @@ namespace xDataService.Interfaces { /// /// /// + /// /// - Task AddOrUpdateAsync ( + Task AddOrUpdateAsync( T item, - bool saveChanges = true + bool saveChanges = true, + CancellationToken cancellationToken = default ); /// @@ -43,135 +52,12 @@ namespace xDataService.Interfaces { /// /// /// + /// /// - Task AddRangeAsync ( + Task AddRangeAsync( IEnumerable items, - bool saveChanges = true - ); - #endregion - - // - #region Remove ... - /// - /// remove an Entity by it's Id ... - /// - /// - /// - /// - /// - Task RemoveAsync ( - TKey id, - bool softDelete = true, - bool saveChanges = true - ); - - /// - /// remove an Entity ... - /// - /// - /// - /// - /// - Task RemoveAsync ( - T item, - bool softDelete = true, - bool saveChanges = true - ); - - /// - /// remove a range of exists Entities ... - /// - /// - /// - /// - /// - Task RemoveRangeAsync ( - IEnumerable items, - bool softDelete = true, - bool saveChanges = true - ); - #endregion - - // - #region Retrieve ... - /// - /// retrieve whole items as queryable ... - /// - /// - IQueryable AsQueryable (); - - /// - /// retrieve an Entity by it's Id ... - /// - /// - /// - /// - /// - Task GetAsync ( - TKey id, - bool ignoreSoftDeleteds = true, - bool containsDetail = false - ); - - /// - /// retrieve all exists Entities ... - /// - /// - /// - /// - Task> GetAllAsync ( - bool ignoreSoftDeleteds = true, - bool containsDetail = false - ); - - /// - /// find an Entity by providing a Conditional Expression ... - /// - /// - /// - /// - /// - Task FindOneAsync ( - Expression> whereClause, - bool ignoreSoftDeleteds = true, - bool containsDetail = false - ); - - /// - /// find a collection of Entities by proving a Conditional Expression ... - /// - /// - /// - /// - /// - Task> FindManyAsync ( - Expression> whereClause, - bool ignoreSoftDeleteds = true, - bool containsDetail = false - ); - - /// - /// retrieve Entities based on XQuery Pagination structure ... - /// - /// - /// - /// - Task> QueryAsync ( - XQuery query, - bool ignoreSoftDeleteds = true - ); - - /// - /// retrieve Entities based on XQuery Pagination structure by providing a Conditional Expression ... - /// - /// - /// - /// - /// - Task> ConditionalQueryAsync ( - Expression> whereClause, - XQuery query, - bool ignoreSoftDeleteds = true + bool saveChanges = true, + CancellationToken cancellationToken = default ); #endregion @@ -183,11 +69,13 @@ namespace xDataService.Interfaces { /// /// /// + /// /// - Task UpdateAsync ( + Task UpdateAsync( TKey id, T item, - bool saveChanges = true + bool saveChanges = true, + CancellationToken cancellationToken = default ); /// @@ -195,10 +83,60 @@ namespace xDataService.Interfaces { /// /// /// + /// /// - Task UpdateRangeAsync ( + Task UpdateRangeAsync( IEnumerable items, - bool saveChanges = true + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Remove ... + /// + /// remove an Entity by it's Id ... + /// + /// + /// + /// + /// + /// + Task RemoveAsync( + TKey id, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + + /// + /// remove an Entity ... + /// + /// + /// + /// + /// + /// + Task RemoveAsync( + T item, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + + /// + /// remove a range of exists Entities ... + /// + /// + /// + /// + /// + /// + Task RemoveRangeAsync( + IEnumerable items, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default ); #endregion @@ -208,18 +146,139 @@ namespace xDataService.Interfaces { /// count all exists Entities ... /// /// + /// /// - Task CountAsync (bool ignoreSoftDeleteds = true); + Task CountAsync( + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ); /// /// count all exists Entities Pages by providing page size ... /// /// /// + /// + /// /// - Task PagesCountAsync ( + Task PagesCountAsync( int pageSize, - int? totalItems = null + int? totalItems = null, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Retrieve ... + /// + /// retrieve whole items as queryable ... + /// + /// a flag for Tracking behaviour + /// an Expression for Filter Items ... + /// an Order Builder expression for Ordering Query ... + /// an Include Builder expression for Including Navigation Properties ... + /// + IQueryable AsQueryable( + bool asNoTracking = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null + ); + + /// + /// retrieve an Entity by it's Id ... + /// + /// + /// + /// + /// + /// + Task GetAsync( + TKey id, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve all exists Entities ... + /// + /// + /// + /// + /// + /// + Task> GetAllAsync( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve all exists Entities + /// as Async Enumerable ... + /// + /// + /// + /// + /// + IAsyncEnumerable GetAllAsAsyncEnumerable( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null + ); + + /// + /// find an Entity by providing a Conditional Expression ... + /// + /// + /// + /// + /// + /// + Task FindOneAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// find a collection of Entities by proving a Conditional Expression ... + /// + /// + /// + /// + /// + /// + /// + Task> FindManyAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve Entities based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + /// + /// + Task> QueryAsync( + XQuery query, + bool ignoreSoftDeleteds = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default ); #endregion @@ -230,10 +289,12 @@ namespace xDataService.Interfaces { /// /// /// + /// /// - Task IsExistsAsync ( + Task IsExistsAsync( TKey id, - bool ignoreSoftDeleteds = true + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default ); #endregion @@ -243,31 +304,63 @@ namespace xDataService.Interfaces { /// Save all unsaved Transactions on DbContext ... /// used fo Unit Of Works Design Pattern ... /// + /// /// - Task SaveChangesAsync (); + Task SaveChangesAsync( + CancellationToken cancellationToken = default + ); #endregion // #region Key ... - TKey GetKey (T item); + /// + /// Retrieve Key of Entity ... + /// + /// + /// + TKey GetKey(T item); - void SetKey ( + /// + /// Set Key of Entity ... + /// + /// + /// + void SetKey( ref T item, TKey id ); - Task HandleKeyAsync (T item); + /// + /// Handle Checking Key ... + /// + /// + /// + /// + Task HandleKeyAsync( + T item, + CancellationToken cancellationToken = default + ); #endregion // #region Detach ... - void Detach (T item); + /// + /// Detach an Entity ... + /// + /// + void Detach(T item); - void Detach (IEnumerable items); + /// + /// Detach an Enumerable of Entities ... + /// + /// + void Detach(IEnumerable items); - void Detach (XQueryResult query); - - void Detach (XPageResponse page); + /// + /// Detach a Query Result of Entity ... + /// + /// + void Detach(XQueryResult query); #endregion } } \ No newline at end of file diff --git a/Interfaces/IXBaseRepositoryService.cs b/Interfaces/IXBaseRepositoryService.cs new file mode 100644 index 0000000..0aaea60 --- /dev/null +++ b/Interfaces/IXBaseRepositoryService.cs @@ -0,0 +1,299 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using AutoMapper; +using Microsoft.EntityFrameworkCore.Query; +using xModels.Base; +using xModels.Dtos; + +namespace xDataService.Interfaces +{ + /// + /// an Interface for Manipulating Data Using Services based on Dto Mapping ... + /// + /// + /// + /// + public interface IXBaseRepositoryService : IDisposable + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + { + // + #region Properties ... + /// + /// Mapper Instance ... + /// + IMapper Mapper { get; } + + /// + /// Repository Implementation for Data Manipulations ... + /// + IXBaseRepository Repository { get; } + + /// + /// Mapping Profile Configuration ... + /// + /// this Configuration used for Mapping TEntity to Dto and reverse ... + /// default mapping prepared on Constructing time, other custom maps + /// must handled manually if required ... + /// + MapperConfiguration MapperConfiguration { get; } + #endregion + + // + #region Actions ... + // + #region Add ... + /// + /// add a new Dto ... + /// + /// + /// + /// + /// + Task AddAsync( + TDto item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + + /// + /// add or update a Dto (add if not exists/update if exists) ... + /// + /// + /// + /// + /// + Task AddOrUpdateAsync( + TDto item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + + /// + /// add a range of new Dtos ... + /// + /// + /// + /// + /// + Task AddRangeAsync( + IEnumerable items, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Update ... + /// + /// Update an Dto values ... + /// + /// + /// + /// + /// + /// + Task UpdateAsync( + TKey id, + TDto item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + + /// + /// Update a range of Dtos ... + /// + /// + /// + /// + /// + Task UpdateRangeAsync( + IEnumerable items, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Remove ... + /// + /// remove an Dto by it's Id ... + /// + /// + /// + /// + /// + /// + Task RemoveAsync( + TKey id, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + + /// + /// remove an Dto ... + /// + /// + /// + /// + /// + /// + Task RemoveAsync( + TDto item, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + + /// + /// remove a range of exists Dtos ... + /// + /// + /// + /// + /// + /// + Task RemoveRangeAsync( + IEnumerable items, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Count ... + /// + /// count all exists Dtos ... + /// + /// + /// + /// + Task CountAsync( + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ); + + /// + /// count all exists Entities Pages by providing page size ... + /// + /// + /// + /// + /// + /// + Task PagesCountAsync( + int pageSize, + int? totalItems = null, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Retrieve ... + /// + /// retrieve an Dto by it's Id ... + /// + /// + /// + /// + /// + /// + Task GetAsync( + TKey id, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve all exists Dtos ... + /// + /// + /// + /// + /// + /// + Task> GetAllAsync( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// find an Dto by providing a Conditional Expression ... + /// + /// + /// + /// + /// + /// + Task FindOneAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// find a collection of Dtos by proving a Conditional Expression ... + /// + /// + /// + /// + /// + /// + /// + Task> FindManyAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + + /// + /// retrieve Dtos based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + /// + /// + Task> QueryAsync( + XQuery query, + bool ignoreSoftDeleteds = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Exists ... + /// + /// Check a Dto exists or not ... + /// + /// + /// + /// + /// + Task IsExistsAsync( + TKey id, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ); + #endregion + #endregion + } +} \ No newline at end of file diff --git a/Interfaces/IXEntityControllerActions.cs b/Interfaces/IXEntityControllerActions.cs new file mode 100644 index 0000000..9f35a9b --- /dev/null +++ b/Interfaces/IXEntityControllerActions.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore.Query; +using xModels.Base; +using xModels.Dtos; + +namespace xDataService.Interfaces +{ + /// + /// an Interface for Base Repository Actions Providing Using Controllers ... + /// + /// + /// + /// + public interface IXEntityControllerActions + where TEntity : XBaseEntity + { + // + IXBaseRepository Repository { get; } + Func, IOrderedQueryable> DefaultOrderBuilder { get; } + Func, IIncludableQueryable> DefaultIncludeBuilder { get; } + + // + #region Add ... + Task> Add( + TEntity item, + CancellationToken cancellationToken = default + ); + + Task> AddOrUpdate( + TEntity item, + CancellationToken cancellationToken = default + ); + + Task AddMany( + XBaseRangeRequest request, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Retrieve ... + Task> Get( + [FromRoute] TKey id, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + Task>> GetAll( + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + Task> FindOne( + [FromRoute] string query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + Task>> FindMany( + [FromRoute] string query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + Task>> Query( + [FromQuery] XQuery query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Update ... + Task> Update( + [FromRoute] TKey id, + [FromBody] TEntity item, + CancellationToken cancellationToken = default + ); + + Task> UpdateMany( + [FromBody] XBaseRangeRequest request, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Exists ... + Task> IsExists( + [FromRoute] TKey id, + [FromQuery] bool ignoreSoftDeletedss = true, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Remove ... + Task> Remove( + [FromRoute] TKey id, + [FromQuery] bool softDelete = true, + CancellationToken cancellationToken = default + ); + + Task RemoveMany( + [FromBody] XBaseRangeRequest request, + [FromQuery] bool softDelete = true, + CancellationToken cancellationToken = default + ); + #endregion + } +} \ No newline at end of file diff --git a/Interfaces/IXEntityDtoControllerActions.cs b/Interfaces/IXEntityDtoControllerActions.cs new file mode 100644 index 0000000..43d9ed8 --- /dev/null +++ b/Interfaces/IXEntityDtoControllerActions.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore.Query; +using xModels.Base; +using xModels.Dtos; + +namespace xDataService.Interfaces +{ + /// + /// an Interface for Base RepositoryService Actions Providing Using Controllers ... + /// + /// + /// + /// + public interface IXEntityDtoControllerActions + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + { + // + IXBaseRepositoryService RepositoryService { get; } + Func, IOrderedQueryable> DefaultOrderBuilder { get; } + Func, IIncludableQueryable> DefaultIncludeBuilder { get; } + + // + #region Add ... + Task> Add( + TDto item, + CancellationToken cancellationToken = default + ); + + Task> AddOrUpdate( + TDto item, + CancellationToken cancellationToken = default + ); + + Task AddMany( + XBaseRangeRequest request, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Retrieve ... + Task> Get( + [FromRoute] TKey id, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + Task>> GetAll( + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + Task> FindOne( + [FromRoute] string query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + Task>> FindMany( + [FromRoute] string query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + + Task>> Query( + [FromQuery] XQuery query, + [FromQuery] bool ignoreSoftDeleteds = true, + [FromQuery] bool useDefaultOrders = false, + [FromQuery] bool useDefaultIncludes = false, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Update ... + Task> Update( + [FromRoute] TKey id, + [FromBody] TDto item, + CancellationToken cancellationToken = default + ); + + Task> UpdateMany( + [FromBody] XBaseRangeRequest request, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Exists ... + Task> IsExists( + [FromRoute] TKey id, + [FromQuery] bool ignoreSoftDeletedss = true, + CancellationToken cancellationToken = default + ); + #endregion + + // + #region Remove ... + Task> Remove( + [FromRoute] TKey id, + [FromQuery] bool softDelete = true, + CancellationToken cancellationToken = default + ); + + Task RemoveMany( + [FromBody] XBaseRangeRequest request, + [FromQuery] bool softDelete = true, + CancellationToken cancellationToken = default + ); + #endregion + } +} \ No newline at end of file diff --git a/Interfaces/IXKeyGenerator.cs b/Interfaces/IXKeyGenerator.cs index 3b595c4..caba890 100644 --- a/Interfaces/IXKeyGenerator.cs +++ b/Interfaces/IXKeyGenerator.cs @@ -1,3 +1,4 @@ +using System.Threading; using System.Threading.Tasks; using xModels.Base; @@ -6,7 +7,8 @@ namespace xDataService.Interfaces { where TEntity : XBaseEntity { bool IsEmpty (TKey id); Task GenerateKey ( - IXBaseRepository repository + IXBaseRepository repository, + CancellationToken cancellationToken = default ); } } \ No newline at end of file diff --git a/Interfaces/IXUnitOfWorks.cs b/Interfaces/IXUnitOfWorks.cs index 825d775..a26899e 100644 --- a/Interfaces/IXUnitOfWorks.cs +++ b/Interfaces/IXUnitOfWorks.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using xDataService.Db; @@ -15,6 +16,8 @@ namespace xDataService.Interfaces { TDbContext DbContext { get; } DbSet GetDbSet () where T : XBaseEntity; int SaveChanges (); - Task SaveChangesAsync (); + Task SaveChangesAsync ( + CancellationToken cancellationToken = default + ); } } \ No newline at end of file diff --git a/MongoRepositories/XBaseMongoRepository.cs b/MongoRepositories/XBaseMongoRepository.cs index 4755989..83321f1 100644 --- a/MongoRepositories/XBaseMongoRepository.cs +++ b/MongoRepositories/XBaseMongoRepository.cs @@ -2,9 +2,13 @@ using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using System.Threading; using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; using MongoDB.Driver; using MongoDB.Driver.Linq; +using Org.BouncyCastle.Asn1.Ocsp; using xCommons.Extensions; using xDataService.Configuration; using xDataService.Extensions; @@ -13,1086 +17,1140 @@ using xDataService.Models; using xModels.Base; using xModels.Dtos; -namespace xDataService.MongoRepositories { +namespace xDataService.MongoRepositories +{ /// - /// Base MongoDb Base Entity Repository Pattern implementation ... - /// Only Used on MongoDb ... + /// a Base Repository Pattern Implementation Specially for Mongo DBs using + /// EF Core Capabilities ... /// - /// is the Entity type - /// is the Entity Key Type - /// is the DbContext Type + /// + /// public abstract class XBaseMongoRepository : IXBaseRepository - where T : XBaseEntity { - // - private readonly string collectionName; - public List> bulkCollection; - public readonly IMongoCollection collection; - private readonly IXKeyGenerator keyGenerator; - public readonly XDataServiceConfiguration configuration; - private readonly IXBaseRepositoryEvents baseRepositoryEvents; + where T : XBaseEntity + { + // + #region Properties ... + private readonly string collectionName; + public List> bulkCollection; + public readonly IMongoCollection collection; + private readonly IXKeyGenerator keyGenerator; + public readonly XDataServiceConfiguration configuration; + private readonly IXBaseRepositoryEvents baseRepositoryEvents; + #endregion - // - public abstract IMongoQueryable GetFullDbSet (); + // + #region Constructor ... + protected XBaseMongoRepository( + XDataServiceConfiguration configuration, + string collectionName = null, + IXKeyGenerator keyGenerator = null, + IXBaseRepositoryEvents baseRepositoryEvents = null + ) + { + // + this.keyGenerator = keyGenerator; + this.configuration = configuration; + this.collectionName = collectionName + .IsNullOrEmpty() ? + typeof(T).Name : + collectionName; + this.baseRepositoryEvents = baseRepositoryEvents; // - #region Constructor ... - protected XBaseMongoRepository ( - XDataServiceConfiguration configuration, - string collectionName = null, - IXKeyGenerator keyGenerator = null, - IXBaseRepositoryEvents baseRepositoryEvents = null - ) { + var client = new MongoClient(configuration.GetMongoDbURI()); + var database = client.GetDatabase(configuration.GetMongoDbDatabase()); + + // + bulkCollection = new List>(); + collection = database.GetCollection( + this.collectionName + ); + } + #endregion + + // + #region Actions ... + // + #region Add ... + /// + /// add a new Entity ... + /// + /// + /// + /// + /// + public async Task AddAsync( + T item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + // Handle Key ... + item = await HandleKeyAsync( + item: item, + cancellationToken: cancellationToken + ); + + // + // Add Action model to Bulk Collection ... + bulkCollection.Add(new InsertOneModel(item)); + + // + // Check action is Succeeded or not ... + var isSucceed = false; + if (saveChanges) + { // - this.keyGenerator = keyGenerator; - this.configuration = configuration; - this.collectionName = collectionName - .IsNullOrEmpty () ? - typeof (T).Name : - collectionName; - this.baseRepositoryEvents = baseRepositoryEvents; + // Get Modified Count ... + var qResult = await SaveChangesAsync(cancellationToken); // - var client = new MongoClient (configuration.GetMongoDbURI ()); - var database = client.GetDatabase (configuration.GetMongoDbDatabase ()); - - // - bulkCollection = new List> (); - collection = database.GetCollection ( - this.collectionName - ); + // set isSucceed Value based on Changes ... + isSucceed = qResult > 0; } - #endregion // - #region Add ... - public async Task AddAsync ( - T item, - bool saveChanges = true - ) { + // Notify Event ... + if ( + isSucceed && + !baseRepositoryEvents.IsNull() + ) + { + // + baseRepositoryEvents + .AddEvent(new XBaseEventModel(item)); + } + + // + // Return result base on action Succeed ... + return isSucceed ? + item : + null; + } + + /// + /// add or update an Entity (add if not exists/update if exists) ... + /// + /// + /// + /// + /// + public async Task AddOrUpdateAsync( + T item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + var isExists = await IsExistsAsync( + id: GetKey(item), + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + if (!isExists) + { // // Handle Key ... - item = await HandleKeyAsync (item); + item = await HandleKeyAsync( + item: item, + cancellationToken: cancellationToken + ); // // Add Action model to Bulk Collection ... - bulkCollection.Add (new InsertOneModel (item)); - - // - // Check action is Succeeded or not ... - var isSucceed = false; - if (saveChanges) { - // - // Get Modified Count ... - var qResult = await SaveChangesAsync (); - - // - // set isSucceed Value based on Changes ... - isSucceed = qResult > 0; - } - - // - // Notify Event ... - if ( - isSucceed && - !baseRepositoryEvents.IsNull () - ) { - baseRepositoryEvents - .AddEvent (new XBaseEventModel (item)); - } - - // - // Return result base on action Succeed ... - return isSucceed ? - item : - null; + bulkCollection + .Add(new InsertOneModel(item)); } - - public async Task AddOrUpdateAsync ( - T item, - bool saveChanges = true - ) { - // - var isExists = await IsExistsAsync (GetKey (item)); - if (!isExists) { - // - // Handle Key ... - item = await HandleKeyAsync (item); - - // - // Add Action model to Bulk Collection ... - bulkCollection - .Add (new InsertOneModel (item)); - } else { - // - // Filter ... - var filter = Builders.Filter.Eq (i => i.Id, item.Id); - - // - // Add Action model to Bulk Collection ... - bulkCollection - .Add (new ReplaceOneModel (filter, item) { IsUpsert = true }); - } - - // - // Check action is Succeeded or not ... - var isSucceed = false; - if (saveChanges) { - // - // Get Modified Count ... - var qResult = await SaveChangesAsync (); - - // - // set isSucceed Value based on Changes ... - isSucceed = qResult > 0; - } - - // - // Notify Event ... - if ( - isSucceed && - !baseRepositoryEvents.IsNull () - ) { - // - if (isExists) { - baseRepositoryEvents - .UpdateEvent (new XBaseEventModel (item)); - } else { - baseRepositoryEvents - .AddEvent (new XBaseEventModel (item)); - } - } - - // - // Return result base on action Succeed ... - return isSucceed ? - item : - null; - } - - public async Task AddRangeAsync ( - IEnumerable items, - bool saveChanges = true - ) { - // - foreach (var item in items) { - // - // Handle Key ... - var keyHandledItem = await HandleKeyAsync (item); - - // - // Add Action model to Bulk Collection ... - bulkCollection.Add (new InsertOneModel (keyHandledItem)); - } - - // - // Check action is Succeeded or not ... - var isSucceed = false; - if (saveChanges) { - // - // Get Modified Count ... - var qResult = await SaveChangesAsync (); - - // - // set isSucceed Value based on Changes ... - isSucceed = qResult > 0; - } - - // - // Notify Event ... - if ( - isSucceed && - !baseRepositoryEvents.IsNull () - ) { - baseRepositoryEvents - .AddManyEvent (new XBaseEventModel> (null)); - } - } - #endregion - - // - #region Remove ... - public async Task RemoveAsync ( - TKey id, - bool softDelete = true, - bool saveChanges = true - ) { - // - // Retrieve Item ... - var item = await GetAsync ( - id, - ignoreSoftDeleteds : softDelete - ); - if (item.IsNull ()) { - return null; - } - + else + { // // Filter ... - var filter = Builders.Filter.Eq (i => i.Id, id); - - // - // Check SoftDelete ... - if (softDelete && configuration.EnableSoftDelete) { - // - // Set Soft Delete ... - item.Deleted = true; - - // - // Update ... - // Add Action model to Bulk Collection ... - bulkCollection.Add (new ReplaceOneModel (filter, item) { IsUpsert = true }); - } else { - // - // Delete ... - // Add Action model to Bulk Collection ... - bulkCollection.Add (new DeleteOneModel (filter)); - } - - // - // Check action is Succeeded or not ... - var isSucceed = false; - if (saveChanges) { - // - // Get Modified Count ... - var qResult = await SaveChangesAsync (); - - // - // set isSucceed Value based on Changes ... - isSucceed = qResult > 0; - } - - // - // Notify Event ... - if ( - isSucceed && - !baseRepositoryEvents.IsNull () - ) { - // - baseRepositoryEvents - .RemoveEvent (new XBaseEventModel (item)); - } - - // - // Return result base on action Succeed ... - return isSucceed ? - item : - null; - } - - public async Task RemoveAsync ( - T item, - bool softDelete = true, - bool saveChanges = true - ) { - // - // Check item Exists ... - var isExists = await IsExistsAsync (item.Id); - if (item.IsNull ()) { - return null; - } - - // - // Filter ... - var filter = Builders.Filter.Eq (i => i.Id, item.Id); - - // - // Check SoftDelete ... - if (softDelete && configuration.EnableSoftDelete) { - // - // Set Soft Delete ... - item.Deleted = true; - - // - // Update ... - // Add Action model to Bulk Collection ... - bulkCollection.Add (new ReplaceOneModel (filter, item) { IsUpsert = true }); - } else { - // - // Delete ... - // Add Action model to Bulk Collection ... - bulkCollection.Add (new DeleteOneModel (filter)); - } - - // - // Check action is Succeeded or not ... - var isSucceed = false; - if (saveChanges) { - // - // Get Modified Count ... - var qResult = await SaveChangesAsync (); - - // - // set isSucceed Value based on Changes ... - isSucceed = qResult > 0; - } - - // - // Notify Event ... - if ( - isSucceed && - !baseRepositoryEvents.IsNull () - ) { - // - baseRepositoryEvents - .RemoveEvent (new XBaseEventModel (item)); - } - - // - // Return result base on action Succeed ... - return isSucceed ? - item : - null; - } - - public async Task RemoveRangeAsync ( - IEnumerable items, - bool softDelete = true, - bool saveChanges = true - ) { - // - // loop through items ... - foreach (var item in items) { - // - // Filter ... - var filter = Builders.Filter.Eq (i => i.Id, item.Id); - - // - // Check Soft Delete ... - if (softDelete && configuration.EnableSoftDelete) { - // - // Set Soft Delete ... - item.Deleted = true; - - // - // Update ... - // Add Action model to Bulk Collection ... - bulkCollection.Add (new ReplaceOneModel (filter, item) { IsUpsert = true }); - } else { - // - // Delete ... - // Add Action model to Bulk Collection ... - bulkCollection.Add (new DeleteOneModel (filter)); - } - } - - // - // Check action is Succeeded or not ... - var isSucceed = false; - if (saveChanges) { - // - // Get Modified Count ... - var qResult = await SaveChangesAsync (); - - // - // set isSucceed Value based on Changes ... - isSucceed = qResult > 0; - } - - // - // Notify Event ... - if ( - isSucceed && - !baseRepositoryEvents.IsNull () - ) { - baseRepositoryEvents - .RemoveManyEvent (new XBaseEventModel> (null)); - } - } - #endregion - - // - #region Retrieve ... - public IQueryable AsQueryable () { - return collection.AsQueryable (); - } - - public async Task GetAsync ( - TKey id, - bool ignoreSoftDeleteds = true, - bool containsDetail = false - ) { - // - // Retrieve Result ... - var result = await FindOneAsync ( - whereClause: x => x.Id - .ToString () - .ToNormalString () == id - .ToString () - .ToNormalString (), - ignoreSoftDeleteds : true, - containsDetail : containsDetail - ); - - // - await Task.CompletedTask; - - // - // Resturn Result ... - return result; - } - - public async Task> GetAllAsync ( - bool ignoreSoftDeleteds = true, - bool containsDetail = false - ) { - // - var result = GetDbSet (containsDetail: containsDetail) - .AsQueryable () - .AsEnumerable (); - - // - if (ignoreSoftDeleteds && configuration.EnableSoftDelete) { - result = result.Where (i => i.Deleted == false); - } - - // - await Task.CompletedTask; - - // - return result; - } - - public async Task FindOneAsync ( - Expression> whereClause, - bool ignoreSoftDeleteds = true, - bool containsDetail = false - ) { - // - // Generate Where Function ... - var whereFunc = whereClause.Compile (); - - // - // Handle Soft Deleted Items ... - Func ignoreSoftDeletedsWhereFunc = null; - if (ignoreSoftDeleteds && configuration.EnableSoftDelete) { - // - Expression> ignoreSoftDeletedsWhereClause = x => x.Deleted == false; - ignoreSoftDeletedsWhereFunc = ignoreSoftDeletedsWhereClause.Compile (); - } - - // - // Get Enumerable ... - var enumerator = await GetAsyncEnumerable (containsDetail); - - // - T result = null; - await - foreach (var entity in enumerator) { - // - var isApproved = whereFunc (entity) && - (ignoreSoftDeletedsWhereFunc.IsNull () ? - true : - ignoreSoftDeletedsWhereFunc (entity)); - if (isApproved) { - // - result = entity; - break; - } - } - - // - return result; - } - - public async Task> FindManyAsync ( - Expression> whereClause, - bool ignoreSoftDeleteds = true, - bool containsDetail = false - ) { - // - // Generate Where Function ... - var whereFunc = whereClause.Compile (); - - // - // Handle Soft Deleted Items ... - Func ignoreSoftDeletedsWhereFunc = null; - if (ignoreSoftDeleteds && configuration.EnableSoftDelete) { - // - Expression> ignoreSoftDeletedsWhereClause = x => x.Deleted == false; - ignoreSoftDeletedsWhereFunc = ignoreSoftDeletedsWhereClause.Compile (); - } - - // - // Get Enumerable ... - var enumerator = await GetAsyncEnumerable (containsDetail); - - // - var result = new List (); - await - foreach (var entity in enumerator) { - // - var isApproved = whereFunc (entity) && - (ignoreSoftDeletedsWhereFunc.IsNull () ? - true : - ignoreSoftDeletedsWhereFunc (entity)); - if (isApproved) { - result.Add (entity); - } - } - - // - return result.AsEnumerable (); - } - - public async Task> QueryAsync ( - XQuery query, - bool ignoreSoftDeleteds = true - ) { - // - if (query.PageSize < configuration - .PagingConfiguration - .MinAvailablePageSize) { - query.PageSize = configuration - .PagingConfiguration - .DefaultPageSize; - } - - // - if (query.PageSize > configuration - .PagingConfiguration - .MaxAvailablePageSize) { - query.PageSize = configuration - .PagingConfiguration - .MaxAvailablePageSize; - } - - // - // Where Filter Handler ... - Expression> whereClause = i => - i.PropValuesContains (query.Filter); - var whereFunc = whereClause.Compile (); - - // - // Handle Soft Deleted Items ... - Func ignoreSoftDeletedsWhereFunc = null; - if (ignoreSoftDeleteds && configuration.EnableSoftDelete) { - // - Expression> ignoreSoftDeletedsWhereClause = x => x.Deleted == false; - ignoreSoftDeletedsWhereFunc = ignoreSoftDeletedsWhereClause.Compile (); - } - - // - // Get Enumerable ... - var enumerator = await GetAsyncEnumerable ( - containsDetail: query.ContainsDetail - ); - - // - // Count Total Items ... - var totalItemsCount = GetQueryable ( - containsDetail: query.ContainsDetail - ) - .Count (); - - // - var filteredItems = new List (); - await - foreach (var entity in enumerator) { - // - var isApproved = (query.Filter - .IsNullOrEmpty () ? - true : - whereFunc (entity) - ) && - (ignoreSoftDeletedsWhereFunc.IsNull () ? - true : - ignoreSoftDeletedsWhereFunc (entity)); - if (isApproved) { - filteredItems.Add (entity); - } - } - - // - // Count Filtered Items ... - int totalFilteredItemsCount = filteredItems.Count (); - - // - // Apply Paging ... - var items = filteredItems - .ApplyPaging ( - query.Page, - query.PageSize); - - // - // Apply Sorting ... - if (!query.SortBy.IsNullOrEmpty ()) { - items = items - .ApplySorting ( - query.SortBy, - query.IsAscending); - } - - // - // Generate Result Object ... - var queryResult = new XQueryResult { - Items = filteredItems.AsEnumerable (), - Page = query.Page, - PageSize = query.PageSize, - TotalItems = totalItemsCount, - TotalPages = await PagesCountAsync ( - query.PageSize, - totalFilteredItemsCount - ), - TotalFilteredItems = totalFilteredItemsCount - }; - - // - return queryResult; - } - - public async Task> ConditionalQueryAsync ( - Expression> whereClause, - XQuery query, - bool ignoreSoftDeleteds = true - ) { - // - if (query.PageSize < configuration - .PagingConfiguration - .MinAvailablePageSize) { - query.PageSize = configuration - .PagingConfiguration - .DefaultPageSize; - } - - // - if (query.PageSize > configuration - .PagingConfiguration - .MaxAvailablePageSize) { - query.PageSize = configuration - .PagingConfiguration - .MaxAvailablePageSize; - } - - // - // Generate Where Func ... - var whereFunc = whereClause.Compile (); - - // - // Where Filter Handler ... - Expression> whereFilterClause = i => - i.PropValuesContains (query.Filter); - var whereFilterFunc = whereClause.Compile (); - - // - // Handle Soft Deleted Items ... - Func ignoreSoftDeletedsWhereFunc = null; - if (ignoreSoftDeleteds && configuration.EnableSoftDelete) { - // - Expression> ignoreSoftDeletedsWhereClause = x => x.Deleted == false; - ignoreSoftDeletedsWhereFunc = ignoreSoftDeletedsWhereClause.Compile (); - } - - // - // Get Enumerable ... - var enumerator = await GetAsyncEnumerable ( - containsDetail: query.ContainsDetail - ); - - // - // Count Total Items ... - var totalItemsCount = GetQueryable ( - containsDetail: query.ContainsDetail - ) - .Count (); - - // - var filteredItems = new List (); - await - foreach (var entity in enumerator) { - // - var isApproved = whereFunc (entity) && - (query.Filter - .IsNullOrEmpty () ? - true : - whereFilterFunc (entity) - ) && - (ignoreSoftDeletedsWhereFunc.IsNull () ? - true : - ignoreSoftDeletedsWhereFunc (entity)); - if (isApproved) { - filteredItems.Add (entity); - } - } - - // - // Count Filtered Items ... - int totalFilteredItemsCount = filteredItems.Count (); - - // - // Apply Paging ... - var items = filteredItems - .ApplyPaging ( - query.Page, - query.PageSize); - - // - // Apply Sorting ... - if (!query.SortBy.IsNullOrEmpty ()) { - items = items - .ApplySorting ( - query.SortBy, - query.IsAscending); - } - - // - // Generate Result Object ... - var queryResult = new XQueryResult { - Items = filteredItems.AsEnumerable (), - Page = query.Page, - PageSize = query.PageSize, - TotalItems = totalItemsCount, - TotalPages = await PagesCountAsync ( - query.PageSize, - totalFilteredItemsCount - ), - TotalFilteredItems = totalFilteredItemsCount - }; - - // - return queryResult; - } - #endregion - - // - #region Update ... - public async Task UpdateAsync ( - TKey id, - T item, - bool saveChanges = true - ) { - // - // Filter ... - var filter = Builders.Filter.Eq (i => i.Id, id); + var filter = Builders.Filter.Eq(i => i.Id, item.Id); // // Add Action model to Bulk Collection ... - bulkCollection.Add (new ReplaceOneModel (filter, item) { IsUpsert = false }); + bulkCollection + .Add(new ReplaceOneModel(filter, item) { IsUpsert = true }); + } + + // + // Check action is Succeeded or not ... + var isSucceed = false; + if (saveChanges) + { + // + // Get Modified Count ... + var qResult = await SaveChangesAsync(cancellationToken); // - // Check action is Succeeded or not ... - var isSucceed = false; - if (saveChanges) { - // - // Get Modified Count ... - var qResult = await SaveChangesAsync (); - - // - // set isSucceed Value based on Changes ... - isSucceed = qResult > 0; - } + // set isSucceed Value based on Changes ... + isSucceed = qResult > 0; + } + // + // Notify Event ... + if ( + isSucceed && + !baseRepositoryEvents.IsNull() + ) + { // - // Notify Event ... - if ( - isSucceed && - !baseRepositoryEvents.IsNull () - ) { + if (isExists) + { + // baseRepositoryEvents - .UpdateEvent (new XBaseEventModel (item)); + .UpdateEvent(new XBaseEventModel(item)); } - - // - // Return result base on action Succeed ... - return isSucceed ? - item : - null; - } - - public async Task UpdateRangeAsync ( - IEnumerable items, - bool saveChanges = true - ) { - // - // loop through items ... - foreach (var item in items) { + else + { // - // Filter ... - var filter = Builders.Filter.Eq (i => i.Id, item.Id); - - // - // Add Action model to Bulk Collection ... - bulkCollection.Add (new ReplaceOneModel (filter, item) { IsUpsert = true }); - } - - // - // Check action is Succeeded or not ... - var isSucceed = false; - if (saveChanges) { - // - // Get Modified Count ... - var qResult = await SaveChangesAsync (); - - // - // set isSucceed Value based on Changes ... - isSucceed = qResult > 0; - } - - // - // Notify Event ... - if ( - isSucceed && - !baseRepositoryEvents.IsNull () - ) { baseRepositoryEvents - .UpdateManyEvent (new XBaseEventModel> (null)); + .AddEvent(new XBaseEventModel(item)); } - - // - // Return result base on action Succeed ... - return isSucceed; } - #endregion // - #region Count ... - public async Task CountAsync (bool ignoreSoftDeleteds = true) { - // - // Filter ... - var filter = Builders.Filter.Eq (i => i.Deleted, false); - - // - var result = ignoreSoftDeleteds && configuration.EnableSoftDelete ? (int) GetDbSet () - .Count (x => filter.Inject ()) : - (int) GetDbSet () - .Count (); - - // - await Task.CompletedTask; - - // - return result; - } - - public async Task PagesCountAsync ( - int pageSize, - int? totalItems = null - ) { - // - int count = totalItems.HasValue ? totalItems.Value : await CountAsync (); - int pagesCount = count / pageSize; - - // - if (count % pageSize > 0) { - pagesCount++; - } - - // - return pagesCount; - } - #endregion - - // - #region Exists ... - public async Task IsExistsAsync (TKey id, bool ignoreSoftDeleteds = true) { - // - var result = await FindOneAsync (i => - GetKey (i) - .ToString () == id - .ToString (), - ignoreSoftDeleteds : ignoreSoftDeleteds, - containsDetail : false - ); - - // - return !result.IsNull (); - } - #endregion - - // - #region Unit Of Work ... - public async Task SaveChangesAsync () { - // - try { - // - // Write all Bulk Models which stored inside bulkCollection ... - var result = await collection - .BulkWriteAsync (bulkCollection); - - // - // Clear Bulk Collection ... - bulkCollection.Clear (); - - // - // Return number of Modified Documents ... - var resultCount = (int) result.ModifiedCount + - (int) result.InsertedCount + - (int) result.DeletedCount; - - // - return resultCount; - } catch (Exception ex) { - // - // Log Thrown Exception ... - Console.WriteLine ($"XMongo Repository Exception: {ex.Message} ..."); - - // - // Return less than zero value ... - return -1; - } - } - #endregion - - // - #region Keys ... - public void SetKey ( - ref T item, - TKey id - ) { - // - var props = item.GetType ().GetProperties (); - var keyProp = props.FirstOrDefault (p => p.Name == nameof (XBaseEntity.Id)); - if (keyProp.IsNull ()) { - return; - } - - // - Type t = Nullable.GetUnderlyingType (keyProp.PropertyType) ?? keyProp.PropertyType; - object safeValue = (id == null) ? null : Convert.ChangeType (id, t); - keyProp.SetValue (item, safeValue, null); - } - - public TKey GetKey (T item) { - // - var props = item.GetType ().GetProperties (); - var keyProp = props.FirstOrDefault (p => p.Name == nameof (XBaseEntity.Id)); - - // - var keyString = string.Empty; - if (keyProp.IsNull ()) { - keyString = string.Empty; - } else { - keyString = keyProp.GetValue (item).ToString (); - } - - // - if (keyString.IsNullOrEmpty ()) { - return default (TKey); - } - - // - // Prevent Deserializing issues throug JsonReader ... - if (keyString.IsGuid () && typeof (TKey) == typeof (Guid)) { - return item.Id; - } - - // - return keyString.FromJSON (); - } - - public async Task HandleKeyAsync (T item) { - // - var keyType = typeof (TKey); - - // - // Handle Guid Key Type ... - if (!keyGenerator.IsNull () && - keyGenerator.IsEmpty (item.Id) - ) { - // - var newKey = await keyGenerator.GenerateKey (this); - - // - // InCrease Key if Type of TKey is Int and BulkDocs Contaisn Items ... - if (keyType == typeof (int)) { - // - var lastBulkedInsertedItem = bulkCollection - .Where (i => i.GetType () == typeof (InsertOneModel)) - .Select (i => (i as InsertOneModel).Document) - .OrderByDescending (nameof (XBaseEntity.Id)) - .FirstOrDefault (); - - // - // Renew Key if Exists inside bulkCollection ... - if (!lastBulkedInsertedItem.IsNull ()) { - // - var increasedKey = (Convert.ToInt32 (lastBulkedInsertedItem.Id)) + 1; - - // - newKey = Convert.ChangeType ( - increasedKey.ToDynamicObject (), - typeof (TKey) - ); - } - } - - // - SetKey (ref item, newKey); - } - - // - return item; - } - #endregion - - // - #region Detach ... - public void Detach (T item) { } - - public void Detach (IEnumerable items) { } - - public void Detach (XQueryResult query) { } - - public void Detach (XPageResponse page) { } - #endregion - - // - #region Others ... - public void Dispose () { } - - public string GetPropValues (T item) { - // - var props = item.GetType ().GetProperties (); - var vals = props.Select (p => p.GetValue (p.Name)); - - // - return vals.ToJSON (); - } - #endregion - - // - #region Private ... - private IMongoQueryable GetDbSet ( - bool containsDetail = false - ) { - // - IMongoQueryable result = null; - if (!containsDetail) { - result = collection - .AsQueryable (); - } else { - result = GetFullDbSet (); - } - - // - return result; - } - - private IMongoQueryable GetQueryable ( - bool containsDetail = false - ) { - return GetDbSet ( - containsDetail: containsDetail - ); - // .AsQueryable (); - } - - private async Task> GetAsyncCursor ( - bool containsDetail = false - ) { - return await GetQueryable ( - containsDetail: containsDetail - ) - .ToCursorAsync (); - } - - private async Task> GetAsyncEnumerable ( - bool containsDetail = false - ) { - return (await GetAsyncCursor ( - containsDetail: containsDetail - )) - .ToAsyncEnumerable (); - } - #endregion + // Return result base on action Succeed ... + return isSucceed ? + item : + null; } + + /// + /// add a range of new Entities ... + /// + /// + /// + /// + /// + public async Task AddRangeAsync( + IEnumerable items, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + var isValid = !items.IsNull() && items.HasChild(); + if (!isValid) + { + return; + } + + // + foreach (var item in items) + { + // + await AddAsync( + item: item, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + } + + // + // Notify Event ... + if ( + isValid && + !baseRepositoryEvents.IsNull() + ) + { + // + baseRepositoryEvents + .AddManyEvent(new XBaseEventModel>(null)); + } + } + #endregion + + // + #region Update ... + /// + /// Update an Entity values ... + /// + /// + /// + /// + /// + /// + public async Task UpdateAsync( + TKey id, + T item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + // Filter ... + var filter = Builders.Filter.Eq(i => i.Id, id); + + // + // Add Action model to Bulk Collection ... + bulkCollection.Add(new ReplaceOneModel(filter, item) { IsUpsert = false }); + + // + // Check action is Succeeded or not ... + var isSucceed = false; + if (saveChanges) + { + // + // Get Modified Count ... + var qResult = await SaveChangesAsync(cancellationToken); + + // + // set isSucceed Value based on Changes ... + isSucceed = qResult > 0; + } + + // + // Notify Event ... + if ( + isSucceed && + !baseRepositoryEvents.IsNull() + ) + { + // + baseRepositoryEvents + .UpdateEvent(new XBaseEventModel(item)); + } + + // + // Return result base on action Succeed ... + return isSucceed ? + item : + null; + } + + /// + /// Update a range of Entities ... + /// + /// + /// + /// + /// + public async Task UpdateRangeAsync( + IEnumerable items, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + var result = false; + var isValid = !items.IsNull() && items.HasChild(); + if (!isValid) + { + return result; + } + + // + foreach (var item in items) + { + // + var updated = await UpdateAsync( + item: item, + id: item.Id, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + if (!updated.IsNullOrDefault() && !result) + { + result = true; + } + } + + // + return result; + } + #endregion + + // + #region Remove ... + /// + /// remove an Entity by it's Id ... + /// + /// + /// + /// + /// + /// + public async Task RemoveAsync( + TKey id, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + // Retrieve Item ... + var item = await GetAsync( + id: id, + ignoreSoftDeleteds: true, + cancellationToken: cancellationToken + ); + if (item.IsNull()) + { + return null; + } + + // + // Filter ... + var filter = Builders.Filter.Eq(i => i.Id, id); + + // + // Check SoftDelete ... + if (softDelete && configuration.EnableSoftDelete) + { + // + // Set Soft Delete ... + item.Deleted = true; + + // + // Update ... + // Add Action model to Bulk Collection ... + bulkCollection.Add(new ReplaceOneModel(filter, item) { IsUpsert = true }); + } + else + { + // + // Delete ... + // Add Action model to Bulk Collection ... + bulkCollection.Add(new DeleteOneModel(filter)); + } + + // + // Check action is Succeeded or not ... + var isSucceed = false; + if (saveChanges) + { + // + // Get Modified Count ... + var qResult = await SaveChangesAsync(cancellationToken); + + // + // set isSucceed Value based on Changes ... + isSucceed = qResult > 0; + } + + // + // Notify Event ... + if ( + isSucceed && + !baseRepositoryEvents.IsNull() + ) + { + // + baseRepositoryEvents + .RemoveEvent(new XBaseEventModel(item)); + } + + // + // Return result base on action Succeed ... + return isSucceed ? + item : + null; + } + + /// + /// remove an Entity ... + /// + /// + /// + /// + /// + /// + public async Task RemoveAsync( + T item, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + var result = await RemoveAsync( + id: item.Id, + softDelete: softDelete, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// remove a range of exists Entities ... + /// + /// + /// + /// + /// + /// + public async Task RemoveRangeAsync( + IEnumerable items, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + // loop through items ... + foreach (var item in items) + { + // + // Filter ... + var filter = Builders.Filter.Eq(i => i.Id, item.Id); + + // + // Check Soft Delete ... + if (softDelete && configuration.EnableSoftDelete) + { + // + // Set Soft Delete ... + item.Deleted = true; + + // + // Update ... + // Add Action model to Bulk Collection ... + bulkCollection.Add(new ReplaceOneModel(filter, item) { IsUpsert = true }); + } + else + { + // + // Delete ... + // Add Action model to Bulk Collection ... + bulkCollection.Add(new DeleteOneModel(filter)); + } + } + + // + // Check action is Succeeded or not ... + var isSucceed = false; + if (saveChanges) + { + // + // Get Modified Count ... + var qResult = await SaveChangesAsync(cancellationToken); + + // + // set isSucceed Value based on Changes ... + isSucceed = qResult > 0; + } + + // + // Notify Event ... + if ( + isSucceed && + !baseRepositoryEvents.IsNull() + ) + { + // + baseRepositoryEvents + .RemoveManyEvent(new XBaseEventModel>(null)); + } + } + #endregion + + // + #region Count ... + /// + /// count all exists Entities ... + /// + /// + /// + /// + public async Task CountAsync( + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + orderBuilder: null, + includeBuilder: null, + predicate: predicator + ) + .CountAsync(cancellationToken); + + // + return result; + } + + /// + /// count all exists Entities Pages by providing page size ... + /// + /// + /// + /// + /// + /// + public async Task PagesCountAsync( + int pageSize, + int? totalItems = null, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + int count = totalItems.HasValue ? totalItems.Value : await CountAsync( + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + int pagesCount = count / pageSize; + + // + if (count % pageSize > 0) + { + pagesCount++; + } + + // + return pagesCount; + } + #endregion + + // + #region Retrieve ... + /// + /// retrieve whole items as queryable ... + /// + /// a flag for Tracking behaviour + /// an Expression for Filter Items ... + /// an Order Builder expression for Ordering Query ... + /// an Include Builder expression for Including Navigation Properties ... + /// + public IQueryable AsQueryable( + bool asNoTracking = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null + ) + { + // + var result = collection + .AsQueryable(); + + // + // Apply Predicate ... + if (!predicate.IsNull()) + { + result = result + .Where(predicate); + } + + // + // Apply Includes ... + if (!includeBuilder.IsNull()) + { + // + // Since Mongo Queryable doesnt Support Includes ... + // we ignore this ... + result = (IMongoQueryable)includeBuilder(result); + } + + // + // Apply Ordering ... + if (!orderBuilder.IsNull()) + { + result = (IMongoQueryable)orderBuilder(result); + } + + // + return result; + } + + /// + /// retrieve an Entity by it's Id ... + /// + /// + /// + /// + /// + /// + public async Task GetAsync( + TKey id, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + orderBuilder: null, + predicate: predicator, + includeBuilder: includeBuilder + ) + .Where(x => GetKey(x).ToString() == id.ToString()) + .FirstOrDefaultAsync(cancellationToken); + + // + return result; + } + + /// + /// retrieve all exists Entities ... + /// + /// + /// + /// + /// + /// + public async Task> GetAllAsync( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + predicate: predicator, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder + ) + .ToListAsync(cancellationToken); + + // + return result; + } + + /// + /// retrieve all exists Entities + /// as Async Enumerable ... + /// + /// + /// + /// + /// + public IAsyncEnumerable GetAllAsAsyncEnumerable( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = AsQueryable( + asNoTracking: true, + predicate: predicator, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder + ) + .AsAsyncEnumerable(); + + // + return result; + + } + + /// + /// find an Entity by providing a Conditional Expression ... + /// + /// + /// + /// + /// + /// + public async Task FindOneAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + orderBuilder: null, + predicate: predicator, + includeBuilder: includeBuilder + ) + .Where(predicate) + .FirstOrDefaultAsync(cancellationToken); + + // + return result; + } + + /// + /// find a collection of Entities by proving a Conditional Expression ... + /// + /// + /// + /// + /// + /// + /// + public async Task> FindManyAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + predicate: predicator, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder + ) + .Where(predicate) + .ToListAsync(cancellationToken); + + // + return result; + } + + /// + /// retrieve Entities based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + /// + /// + public async Task> QueryAsync( + XQuery query, + bool ignoreSoftDeleteds = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + // Normalize Query ... + query = query.NormalizeQuery(configuration); + + // + var items = await GetAllAsync(); + var totalItemsCount = items.Count(); + + // + // Apply Filter ... + if (!query.Filter.IsNullOrEmpty()) + { + // + items = items + .ApplyFilter(query.Filter); + } + int filteredItemsCount = items.Count(); + + // + // Count Pages ... + var totalPagesCount = query.CountPages(totalItemsCount); + var filteredPagesCount = query.CountPages(filteredItemsCount); + + // + // Apply Paging and Sorting ... + if (totalItemsCount > 0 && + filteredItemsCount > 0) + { + // + // Apply Sorting ... + items = items + .ToList() + .ApplySorting( + query.SortBy, + query.IsAscending + ); + + // + // Apply Paging ... + items = items + .ToList() + .ApplyPaging( + query.Page, + query.PageSize + ); + } + + // + // Prepare Result ... + var result = new XQueryResult + { + Page = query.Page, + Items = items.ToList(), + PageSize = query.PageSize, + TotalPages = totalPagesCount, + TotalItems = totalItemsCount, + TotalFilteredPages = filteredPagesCount, + TotalFilteredItems = filteredItemsCount + }; + + // + return result; + } + #endregion + + // + #region Exists ... + /// + /// Check an Entity exists or not ... + /// + /// + /// + /// + /// + public async Task IsExistsAsync( + TKey id, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + // Prepare Ignore Soft Deleted Predicator ... + Expression> predicator = null; + if (ignoreSoftDeleteds) + { + predicator = x => !x.Deleted; + } + var result = await AsQueryable( + asNoTracking: true, + orderBuilder: null, + includeBuilder: null, + predicate: predicator + ) + .AnyAsync( + cancellationToken: cancellationToken, + predicate: x => GetKey(x).ToString() == id.ToString()); + + // + return result; + } + #endregion + + // + #region Unit Of Work ... + /// + /// Save all unsaved Transactions on DbContext ... + /// used fo Unit Of Works Design Pattern ... + /// + /// + /// + public async Task SaveChangesAsync( + CancellationToken cancellationToken = default + ) + { + // + try + { + // + // Write all Bulk Models which stored inside bulkCollection ... + var result = await collection + .BulkWriteAsync( + options: null, + requests: bulkCollection, + cancellationToken: cancellationToken + ); + + // + // Clear Bulk Collection ... + bulkCollection.Clear(); + + // + // Return number of Modified Documents ... + var resultCount = (int)result.ModifiedCount + + (int)result.InsertedCount + + (int)result.DeletedCount; + + // + return resultCount; + } + catch (Exception ex) + { + // + // Log Thrown Exception ... + Console.WriteLine($"XMongo Repository Exception: {ex.Message} ..."); + + // + // Return less than zero value ... + return -1; + } + } + #endregion + + // + #region Key ... + /// + /// Retrieve Key of Entity ... + /// + /// + /// + public TKey GetKey(T item) + { + // + var props = item.GetType().GetProperties(); + var keyProp = props.FirstOrDefault(p => p.Name == "Id"); + + // + var keyString = string.Empty; + if (keyProp.IsNull()) + { + keyString = string.Empty; + } + else + { + keyString = keyProp.GetValue(item).ToString(); + } + + // + if (keyString.IsNullOrEmpty()) + { + return default(TKey); + } + + // + // Prevent Deserializing issues throug JsonReader ... + if (keyString.IsGuid() && typeof(TKey) == typeof(Guid)) + { + return item.Id; + } + + // + return keyString.FromJSON(); + } + + /// + /// Set Key of Entity ... + /// + /// + /// + public void SetKey( + ref T item, + TKey id + ) + { + // + var props = item.GetType().GetProperties(); + var keyProp = props.FirstOrDefault(p => p.Name == "Id"); + if (keyProp.IsNull()) + { + return; + } + + // + Type t = Nullable.GetUnderlyingType(keyProp.PropertyType) ?? keyProp.PropertyType; + object safeValue = (id == null) ? null : Convert.ChangeType(id, t); + keyProp.SetValue(item, safeValue, null); + } + + /// + /// Handle Checking Key ... + /// + /// + /// + /// + public async Task HandleKeyAsync( + T item, + CancellationToken cancellationToken = default + ) + { + // + var keyType = typeof(TKey); + + // + // Handle Guid Key Type ... + // Since EFCore has AutoIncrement on int Ids, there is no need to handle int Key types ... + if ( + ( + keyType == typeof(Guid) || + keyType == typeof(string) + ) && + keyGenerator.IsEmpty(item.Id) + ) + { + // + var newKey = await keyGenerator.GenerateKey(this); + + // + SetKey(ref item, newKey); + } + + // + return item; + } + #endregion + + // + #region Detach ... + /// + /// Detach an Entity ... + /// + /// + public void Detach(T item) { } + + /// + /// Detach an Enumerable of Entities ... + /// + /// + public void Detach(IEnumerable items) { } + + /// + /// Detach a Query Result of Entity ... + /// + /// + public void Detach(XQueryResult query) { } + #endregion + #endregion + + // + #region Others ... + public void Dispose() { } + + public string GetPropValues(T item) + { + // + var props = item.GetType().GetProperties(); + var vals = props.Select(p => p.GetValue(p.Name)); + + // + return vals.ToJSON(); + } + #endregion + + // + #region Private ... + #endregion + } } \ No newline at end of file diff --git a/Providers/XBaseRepositoryService.cs b/Providers/XBaseRepositoryService.cs new file mode 100644 index 0000000..3968e03 --- /dev/null +++ b/Providers/XBaseRepositoryService.cs @@ -0,0 +1,745 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using AutoMapper; +using Microsoft.EntityFrameworkCore.Query; +using xCommons.Extensions; +using xDataService.Interfaces; +using xModels.Base; +using xModels.Dtos; + +namespace xDataService.Providers +{ + /// + /// a Base Repository Service Implementation which Use a Repository Patterns Implementatin + /// and provides Data Manipulation Methods using Dtos instead of Entities ... + /// + /// it can be use of following Repository Patterns Implementation: + /// > + /// > + /// > + /// + /// + /// + /// + public abstract class XBaseRepositoryService : IXBaseRepositoryService + where TEntity : XBaseEntity + where TDto : XBaseEntityDto + { + // + #region Properties ... + /// + /// Mapper Instance ... + /// + public IMapper Mapper { get; } + + /// + /// Repository Implementation for Data Manipulations ... + /// + public IXBaseRepository Repository { get; } + + /// + /// Mapping Profile Configuration ... + /// + /// this Configuration used for Mapping TEntity to Dto and reverse ... + /// default mapping prepared on Constructing time, other custom maps + /// must handled manually if required ... + /// + public MapperConfiguration MapperConfiguration { get; } + #endregion + + // + #region Constructor ... + public XBaseRepositoryService( + IXBaseRepository repository, + IEnumerable mapperProfiles = null + ) + { + // + Repository = repository; + + // + // Preparing Mapping Configurations ... + MapperConfiguration = new MapperConfiguration(cfg => + { + // + // Add Default Profiles ... + + // + // Entity To Dto ... + cfg.CreateMap() + .ForMember(dest => dest.Deleted, opt => opt.Ignore()); + + // + // Dto to Entity ... + cfg.CreateMap() + .ForMember(dest => dest.Deleted, opt => opt.Ignore()); + + // + // Add Provided Profiles if Provided ... + // if Same Profiles Provided, here resolved ... + if (!mapperProfiles.IsNull() && + mapperProfiles.HasChild()) + { + cfg.AddProfiles(mapperProfiles); + } + }); + + // + // Make Mapper Instance using prepared Configuration ... + Mapper = MapperConfiguration.CreateMapper(); + } + #endregion + + // + #region Actions ... + // + #region Add ... + /// + /// add a new Dto ... + /// + /// + /// + /// + /// + public async Task AddAsync( + TDto item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + TDto result = null; + + // + // Validate ... + bool isValid = !item.IsNull(); + if (!isValid) + { + return result; + } + + // + try + { + // + var entity = Mapper.Map(item); + + // + entity = await Repository.AddAsync( + item: entity, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + result = Mapper.Map(entity); + } + catch + { } + + // + return result; + } + + /// + /// add or update a Dto (add if not exists/update if exists) ... + /// + /// + /// + /// + /// + public async Task AddOrUpdateAsync( + TDto item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + TDto result = null; + + // + // Validate ... + bool isValid = !item.IsNull(); + if (!isValid) + { + return result; + } + + // + try + { + // + var entity = Mapper.Map(item); + + // + entity = await Repository.AddOrUpdateAsync( + item: entity, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + result = Mapper.Map(entity); + } + catch + { } + + // + return result; + } + + /// + /// add a range of new Dtos ... + /// + /// + /// + /// + /// + public async Task AddRangeAsync( + IEnumerable items, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + TDto result = null; + + // + // Validate ... + bool isValid = + !items.IsNull() && + items.HasChild(); + if (!isValid) + { + return; + } + + // + try + { + // + var entities = Mapper.Map>(items); + + // + await Repository.AddRangeAsync( + items: entities, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + } + catch + { } + } + #endregion + + // + #region Update ... + /// + /// Update an Dto values ... + /// + /// + /// + /// + /// + /// + public async Task UpdateAsync( + TKey id, + TDto item, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + TDto result = null; + + // + try + { + // + var entity = Mapper.Map(item); + + // + entity = await Repository.UpdateAsync( + id: item.Id, + item: entity, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + result = Mapper.Map(entity); + } + catch + { } + + // + return result; + } + + /// + /// Update a range of Dtos ... + /// + /// + /// + /// + /// + public async Task UpdateRangeAsync( + IEnumerable items, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var result = + !items.IsNull() && + items.HasChild(); + if (!result) + { + return result; + } + + // + try + { + // + var entitis = Mapper.Map>(items); + + // + result = await Repository.UpdateRangeAsync( + items: entitis, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + } + catch + { + result = false; + } + + // + return result; + } + #endregion + + // + #region Remove ... + /// + /// remove an Dto by it's Id ... + /// + /// + /// + /// + /// + /// + public async Task RemoveAsync( + TKey id, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + TDto result = null; + + // + try + { + // + var entity = await Repository.RemoveAsync( + id: id, + softDelete: softDelete, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + result = Mapper.Map(entity); + } + catch + { } + + // + return result; + } + + /// + /// remove an Dto ... + /// + /// + /// + /// + /// + /// + public async Task RemoveAsync( + TDto item, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + TDto result = null; + + // + try + { + // + var entity = Mapper.Map(item); + + // + entity = await Repository.RemoveAsync( + item: entity, + softDelete: softDelete, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + + // + result = Mapper.Map(entity); + } + catch + { } + + // + return result; + } + + /// + /// remove a range of exists Dtos ... + /// + /// + /// + /// + /// + /// + public async Task RemoveRangeAsync( + IEnumerable items, + bool softDelete = true, + bool saveChanges = true, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var isValid = + !items.IsNull() && + items.HasChild(); + if (!isValid) + { + return; + } + + // + var entities = Mapper.Map>(items); + + // + try + { + // + await Repository.RemoveRangeAsync( + items: entities, + softDelete: softDelete, + saveChanges: saveChanges, + cancellationToken: cancellationToken + ); + } + catch + { } + } + #endregion + + // + #region Count ... + /// + /// count all exists Dtos ... + /// + /// + /// + /// + public async Task CountAsync( + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + var result = await Repository.CountAsync( + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + + /// + /// count all exists Entities Pages by providing page size ... + /// + /// + /// + /// + /// + /// + public async Task PagesCountAsync( + int pageSize, + int? totalItems = null, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + var result = await Repository.PagesCountAsync( + pageSize: pageSize, + totalItems: totalItems, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + #endregion + + // + #region Exists ... + /// + /// Check a Dto exists or not ... + /// + /// + /// + /// + /// + public async Task IsExistsAsync( + TKey id, + bool ignoreSoftDeleteds = true, + CancellationToken cancellationToken = default + ) + { + // + var result = await Repository.IsExistsAsync( + id: id, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + return result; + } + #endregion + + // + #region Retrieve ... + /// + /// retrieve an Dto by it's Id ... + /// + /// + /// + /// + /// + /// + public async Task GetAsync( + TKey id, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + TDto result = null; + + // + try + { + // + var entity = await Repository.GetAsync( + id: id, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + result = Mapper.Map(entity); + } + catch + { } + + // + return result; + } + + /// + /// retrieve all exists Dtos ... + /// + /// + /// + /// + /// + /// + public async Task> GetAllAsync( + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + IEnumerable result = Enumerable.Empty(); + + // + try + { + // + var entities = await Repository.GetAllAsync( + orderBuilder: orderBuilder, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + result = Mapper.Map>(entities); + } + catch + { } + + // + return result; + } + + /// + /// find an Dto by providing a Conditional Expression ... + /// + /// + /// + /// + /// + /// + public async Task FindOneAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + TDto result = null; + + // + try + { + // + var entity = await Repository.FindOneAsync( + predicate: predicate, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + result = Mapper.Map(entity); + } + catch + { } + + // + return result; + } + + /// + /// find a collection of Dtos by proving a Conditional Expression ... + /// + /// + /// + /// + /// + /// + /// + public async Task> FindManyAsync( + Expression> predicate, + bool ignoreSoftDeleteds = true, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + IEnumerable result = Enumerable.Empty(); + + // + try + { + // + var entities = await Repository.FindManyAsync( + predicate: predicate, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + result = Mapper.Map>(entities); + } + catch + { } + + // + return result; + } + + /// + /// retrieve Dtos based on XQuery Pagination structure ... + /// + /// + /// + /// + /// + /// + /// + /// + public async Task> QueryAsync( + XQuery query, + bool ignoreSoftDeleteds = true, + Expression> predicate = null, + Func, IOrderedQueryable> orderBuilder = null, + Func, IIncludableQueryable> includeBuilder = null, + CancellationToken cancellationToken = default + ) + { + // + XQueryResult result = null; + + // + try + { + // + var entityResult = await Repository.QueryAsync( + query: query, + predicate: predicate, + orderBuilder: orderBuilder, + includeBuilder: includeBuilder, + cancellationToken: cancellationToken, + ignoreSoftDeleteds: ignoreSoftDeleteds + ); + + // + result = Mapper.Map>(entityResult); + } + catch + { } + + // + return result; + } + #endregion + + public virtual void Dispose() + { } + #endregion + } +} \ No newline at end of file diff --git a/Providers/XGuidKeyGenerator.cs b/Providers/XGuidKeyGenerator.cs index 9c0cc40..4df0ea2 100644 --- a/Providers/XGuidKeyGenerator.cs +++ b/Providers/XGuidKeyGenerator.cs @@ -1,41 +1,52 @@ using System; +using System.Threading; using System.Threading.Tasks; using xCommons.Extensions; using xDataService.Interfaces; using xModels.Base; -namespace xDataService.Providers { +namespace xDataService.Providers +{ public class XGuidKeyGenerator : IXKeyGenerator - where TEntity : XBaseEntity { - private readonly IXSequentialGuid sequentialGuid; + where TEntity : XBaseEntity + { + private readonly IXSequentialGuid sequentialGuid; - public XGuidKeyGenerator ( - IXSequentialGuid sequentialGuid = null - ) { - this.sequentialGuid = sequentialGuid; - } - - public async Task GenerateKey ( - IXBaseRepository repository - ) { - // - await Task.CompletedTask; + public XGuidKeyGenerator( + IXSequentialGuid sequentialGuid = null + ) + { + this.sequentialGuid = sequentialGuid; + } + public async Task GenerateKey( + IXBaseRepository repository, + CancellationToken cancellationToken = default + ) + { + // + return await Task.Run(() => + { // // Check if provided SequentialGuid ... - if (sequentialGuid.IsNull ()) { - return Guid.NewGuid (); - } else { - return sequentialGuid.Next (); + if (sequentialGuid.IsNull()) + { + return Guid.NewGuid(); } - } - - public bool IsEmpty (Guid id) { - // - var result = id.IsNull () || id.IsDefaultGuid (); - - // - return result; - } + else + { + return sequentialGuid.Next(); + } + }, cancellationToken: cancellationToken); } + + public bool IsEmpty(Guid id) + { + // + var result = id.IsNull() || id.IsDefaultGuid(); + + // + return result; + } + } } \ No newline at end of file diff --git a/Providers/XIntKeyGenerator.cs b/Providers/XIntKeyGenerator.cs index 33b68d2..e73aa93 100644 --- a/Providers/XIntKeyGenerator.cs +++ b/Providers/XIntKeyGenerator.cs @@ -1,33 +1,41 @@ using System.Linq; +using System.Threading; using System.Threading.Tasks; using xCommons.Extensions; using xDataService.Extensions; using xDataService.Interfaces; using xModels.Base; -namespace xDataService.Providers { +namespace xDataService.Providers +{ public class XIntKeyGenerator : IXKeyGenerator - where TEntity : XBaseEntity { - public async Task GenerateKey (IXBaseRepository repository) { - // - var items = (await repository.GetAllAsync ()) - .OrderByDescending (nameof (XBaseEntity.Id)); - var last = items - .FirstOrDefault (); + where TEntity : XBaseEntity + { + public async Task GenerateKey( + IXBaseRepository repository, + CancellationToken cancellationToken = default + ) + { + // + var items = (await repository.GetAllAsync()) + .OrderByDescending(nameof(XBaseEntity.Id)); + var last = items + .FirstOrDefault(); - // - var result = last.IsNull () ? 1 : last.Id + 1; + // + var result = last.IsNull() ? 1 : last.Id + 1; - // - return result; - } - - public bool IsEmpty (int id) { - // - var result = id <= 0; - - // - return result; - } + // + return result; } + + public bool IsEmpty(int id) + { + // + var result = id <= 0; + + // + return result; + } + } } \ No newline at end of file diff --git a/Providers/XSequentialGuid.cs b/Providers/XSequentialGuid.cs index b4e4423..4d0b6bb 100644 --- a/Providers/XSequentialGuid.cs +++ b/Providers/XSequentialGuid.cs @@ -1,15 +1,20 @@ using System; using xDataService.Interfaces; -namespace xDataService.Helpers { +namespace xDataService.Helpers +{ /// /// this service provide Sequential GUID mechanism for Entity Ids ... /// - public class XSequentialGuid : IXSequentialGuid { + public class XSequentialGuid : IXSequentialGuid + { private static int[] sqlOrderMap = null; - private static int[] SQLORDERMAP { - get { - if (sqlOrderMap == null) { + private static int[] SQLORDERMAP + { + get + { + if (sqlOrderMap == null) + { sqlOrderMap = new int[16] { 3, 2, @@ -37,24 +42,29 @@ namespace xDataService.Helpers { private Guid currentGuid; - public XSequentialGuid () { - currentGuid = Guid.NewGuid (); + public XSequentialGuid() + { + currentGuid = Guid.NewGuid(); } - public Guid GetCurrentGuid () { + public Guid GetCurrentGuid() + { return currentGuid; } - public Guid Next () { - byte[] bytes = currentGuid.ToByteArray (); - for (int mapIndex = 0; mapIndex < 16; mapIndex++) { + public Guid Next() + { + byte[] bytes = currentGuid.ToByteArray(); + for (int mapIndex = 0; mapIndex < 16; mapIndex++) + { int bytesIndex = SQLORDERMAP[mapIndex]; bytes[bytesIndex]++; - if (bytes[bytesIndex] != 0) { + if (bytes[bytesIndex] != 0) + { break; // No need to increment more significant bytes } } - currentGuid = new Guid (bytes); + currentGuid = new Guid(bytes); return currentGuid; } } diff --git a/Providers/XStringKeyGenerator.cs b/Providers/XStringKeyGenerator.cs index f910d57..f47f68b 100644 --- a/Providers/XStringKeyGenerator.cs +++ b/Providers/XStringKeyGenerator.cs @@ -1,31 +1,43 @@ using System; +using System.Threading; using System.Threading.Tasks; using xCommons.Extensions; using xDataService.Interfaces; using xModels.Base; -namespace xDataService.Providers { - public class XStringKeyGenerator : IXKeyGenerator, string> { +namespace xDataService.Providers +{ + public class XStringKeyGenerator : IXKeyGenerator, string> + { private readonly IXSequentialGuid sequentialGuid; - public XStringKeyGenerator (IXSequentialGuid sequentialGuid = null) { + public XStringKeyGenerator(IXSequentialGuid sequentialGuid = null) + { this.sequentialGuid = sequentialGuid; } - public async Task GenerateKey (IXBaseRepository, string> repository) { + public async Task GenerateKey( + IXBaseRepository, string> repository, + CancellationToken cancellationToken = default + ) + { // await Task.CompletedTask; // - if (sequentialGuid.IsNull ()) { - return Guid.NewGuid ().ToString (); - } else { - return sequentialGuid.Next ().ToString (); + if (sequentialGuid.IsNull()) + { + return Guid.NewGuid().ToString(); + } + else + { + return sequentialGuid.Next().ToString(); } } - public bool IsEmpty (string id) { - return id.IsNullOrEmpty (); + public bool IsEmpty(string id) + { + return id.IsNullOrEmpty(); } } } \ No newline at end of file diff --git a/xDataService.csproj b/xDataService.csproj index 7432ec4..bc90a28 100644 --- a/xDataService.csproj +++ b/xDataService.csproj @@ -27,8 +27,8 @@ - - + +