using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using xCategoryService.Extensions; using xCategoryService.Interfaces; using xCategoryService.Interfaces.Dtos; using xCategoryService.Models.Dtos; using xCommons.Extensions; using xDataService.Extensions; using xDataService.Models; using xExceptions.Constants; using xModels.Dtos; namespace xCategoryService.Providers { public abstract class XBaseCategoryProvider : IXBaseCategoryProvider { // #region Props ... /// /// Provider Identifier ... /// public string Provider { get; } /// /// Tag Provider for Maipulating Tags ... /// public IXCategoryServiceProvider CategoryProvider { get; } #endregion // #region Constructor ... public XBaseCategoryProvider( string providedFor, IXCategoryServiceProvider categoryProvider ) { // Provider = providedFor; CategoryProvider = categoryProvider; } #endregion // #region Referenced Actions ... /// /// Get Specified Indexed Reference for Specific Provided ID ... /// /// /// /// /// public async Task GetReference( TKey providedForId, int forIndex = 0, CancellationToken cancellationToken = default ) { // // Validate ... if (providedForId.IsNull()) { XException.InvalidArgs.Throw(); } // // Normalize ... if (forIndex < 0) { forIndex = 0; } // // Retrieve Last Index ... var maxIndex = (await GetReferencingIndexes( providedForId: providedForId, cancellationToken: cancellationToken )) .Max(); if (forIndex > maxIndex) { XException.NotFound.Throw(); } // var indexedIdentifier = GetIndexedProvidedID( forIndex: forIndex, providedForId: providedForId ); var result = await CategoryProvider.FindOneAsync( includeBuilder: null, ignoreSoftDeleteds: true, cancellationToken: cancellationToken, predicate: x => x.References.Contains(indexedIdentifier) ); if (result.IsNullOrDefault()) { XException.NotFound.Throw(); } // return result; } /// /// Retrieve all Exists References of Specific Provided ID ... /// /// /// /// public async Task> GetAllReferences( TKey providedForId, CancellationToken cancellationToken = default ) { // // Validate ... if (providedForId.IsNull()) { XException.InvalidArgs.Throw(); } // var identifier = GetProvidedID(providedForId); var result = await CategoryProvider.FindManyAsync( orderBuilder: null, includeBuilder: null, ignoreSoftDeleteds: true, cancellationToken: cancellationToken, predicate: x => x.References.Contains(identifier) ); // return result; } /// /// Extract all Referencing Models for Specified Key ... /// /// /// /// public async Task>> GetAllReferencings( TKey providedForId, CancellationToken cancellationToken = default ) { // // Validate ... var isValid = !providedForId.IsNull(); if (!isValid) { XException.InvalidArgs.Throw(); } // var identifier = GetProvidedID(providedForId); // var result = (await CategoryProvider.FindManyAsync( orderBuilder: null, includeBuilder: null, ignoreSoftDeleteds: true, cancellationToken: cancellationToken, predicate: x => x.References.Contains(identifier) )) .Select(x => x.References) .ToList() .SelectMany(x => x.ParseXReferenceList()) .Where(x => x.ProvidedFor == Provider && $"{x.ReferencedTo}" == $"{providedForId}") .OrderBy(x => x.Index) .AsEnumerable(); // return result; } /// /// Count References ... /// /// /// /// public async Task CountReferences( TKey providedForId, CancellationToken cancellationToken = default ) { // // Validate ... var isValid = !providedForId.IsNull(); if (!isValid) { XException.InvalidArgs.Throw(); } // var identifier = GetProvidedID(providedForId); var result = (await CategoryProvider.FindManyAsync( orderBuilder: null, includeBuilder: null, ignoreSoftDeleteds: true, cancellationToken: cancellationToken, predicate: x => x.References.Contains(identifier) )) .Count(); // return result; } /// /// Retrieve References of Specific Provided ID as Query ... /// /// /// /// /// public async Task> QueryReferences( XQuery query, TKey providedForId, CancellationToken cancellationToken = default ) { // // Validate ... if (query.IsNull() || providedForId.IsNull()) { XException.InvalidArgs.Throw(); } // var identifier = GetProvidedID(providedForId); var result = await CategoryProvider.QueryAsync( query: query, includeBuilder: null, ignoreSoftDeleteds: true, cancellationToken: cancellationToken, orderBuilder: x => x.OrderBy(y => y.Id), predicate: x => x.References.Contains(identifier) ); // return result; } /// /// Add Reference a Model to Specific Provided ID ... /// /// /// /// /// /// public async Task AddReference( XCategoryDto model, TKey providedForId, string connectionId = null, CancellationToken cancellationToken = default ) { // var result = false; // // Validate ... result = !model.IsNullOrDefault() && model.Id.IsValidIntId() && !providedForId.IsNull(); if (!result) { XException.InvalidArgs.Throw(); } // // Check Model Exists ... result = await CategoryProvider.IsExistsAsync(model.Id); if (!result) { XException.NotFound.Throw(); } // // Update Model by Retrieving ... model = await CategoryProvider.GetAsync( id: model.Id, includeBuilder: null, ignoreSoftDeleteds: true, cancellationToken: cancellationToken ); // // Retrieve all References ... var references = await GetAllReferences( providedForId: providedForId, cancellationToken: cancellationToken ); // // Check isReferenced or not ... result = references .Any(r => r.Id == model.Id && r.GetReferences() .Any(rf => rf.ProvidedFor == Provider && rf.ReferencedTo == $"{providedForId}")); if (result) { return result; } // // Here means there is not any reference to ProvidedForId ... int forIndex = await CountReferences( providedForId: providedForId, cancellationToken: cancellationToken ); var modelReferences = model.GetReferences(); modelReferences.Add(new XReference { Index = forIndex, ProvidedFor = Provider, ReferencedTo = $"{providedForId}" }); modelReferences = modelReferences .OrderBy(r => r.Index) .ToList(); model = model.UpdateReferences(modelReferences); // // Update Data Base ... model = await CategoryProvider.UpdateAsync( id: model.Id, item: model, saveChanges: true, connectionId: connectionId, cancellationToken: cancellationToken ); result = !model.IsNullOrDefault(); // return result; } /// /// Add Reference a Model to Specific XRefence ... /// /// /// /// /// /// public async Task AddReference( XCategoryDto model, XReference reference, string connectionId = null, CancellationToken cancellationToken = default ) { // // Validate ... // Since other Validations Handled in Calling, we Ignore them here ... var result = !model.IsNull() && !reference.IsNullOrDefault() && reference.ProvidedFor == Provider && !reference.ReferencedTo.IsNull(); if (!result) { XException.InvalidArgs.Throw(); } // result = await AddReference( model: model, connectionId: connectionId, cancellationToken: cancellationToken, providedForId: reference.ReferencedTo ); // return result; } /// /// Remove Reference a Model for Specific Provided ID ... /// /// /// /// /// /// public async Task RemoveReference( XCategoryDto model, TKey providedForId, string connectionId = null, CancellationToken cancellationToken = default ) { // // Validate ... var result = !providedForId.IsNull() && !model.IsNullOrDefault() && model.Id.IsValidIntId(); if (!result) { XException.InvalidArgs.Throw(); } // // Retrieve Model ... model = await CategoryProvider.GetAsync( id: model.Id, includeBuilder: null, ignoreSoftDeleteds: true, cancellationToken: cancellationToken ); result = !model.IsNullOrDefault(); if (!result) { XException.NotFound.Throw(); } // // Reading Referencing Indexes ... var indexes = await GetReferencingIndexes( providedForId: providedForId, cancellationToken: cancellationToken ); // // Check Model Reference to ID ... var referencesCount = await CountReferences( providedForId: providedForId, cancellationToken: cancellationToken ); result = referencesCount == 0; if (result) { // // There is not any Reference to Remove ... return result; } // // Check Model is Trully Referenced to ProvidedForId or not ... var reference = model .GetReferences() .FirstOrDefault(r => r.ProvidedFor == Provider && r.ReferencedTo == $"{providedForId}"); result = !reference.IsNullOrDefault(); if (!result) { XException.ActionFailed.Throw(); } // // Remove Referenced Item from Model ... var modelReferences = model .GetReferences() .Where(r => !(r.ProvidedFor == Provider && r.ReferencedTo == $"{providedForId}")) .ToList(); model = model.UpdateReferences(modelReferences); // // Update DataBase ... model = await CategoryProvider.UpdateAsync( item: model, id: model.Id, saveChanges: true, connectionId: connectionId, cancellationToken: cancellationToken ); // result = !model.IsNullOrDefault(); if (!result) { XException.ActionFailed.Throw(); } // // Get Max Exists Indexes ... var maxIndex = indexes.Max(); // // Here We Have to Re Arrange Indexed ... if (reference.Index < maxIndex) { // var startIndex = reference.Index + 1; for (int i = startIndex; i <= maxIndex; i++) { // var dto = await GetReference( forIndex: i, providedForId: providedForId, cancellationToken: cancellationToken ); if (!dto.IsNullOrDefault()) { // var dtoReferences = dto.GetReferences(); dtoReferences .ToList() .ForEach(iref => { // if ( iref.Index == i && iref.ProvidedFor == Provider && iref.ReferencedTo == reference.ReferencedTo ) { iref.Index = i - 1; } }); dto = dto.UpdateReferences(dtoReferences); // dto = await CategoryProvider.UpdateAsync( item: dto, id: dto.Id, saveChanges: true, connectionId: connectionId, cancellationToken: cancellationToken ); } } } // return result; } /// /// Remove Reference a Model for Specific XRefence ... /// /// /// /// /// /// public async Task RemoveReference( XCategoryDto model, XReference reference, string connectionId = null, CancellationToken cancellationToken = default ) { // if (model.IsNullOrDefault() || reference.IsNullOrDefault() || reference.ReferencedTo.IsNull() || reference.ProvidedFor != Provider) { XException.InvalidArgs.Throw(); } // var result = await RemoveReference( model: model, connectionId: connectionId, cancellationToken: cancellationToken, providedForId: reference.ReferencedTo ); // return result; } /// /// Remove All References to Specified Key ... /// /// /// /// /// public async Task RemoveReferences( TKey providedForId, string connectionId = null, CancellationToken cancellationToken = default ) { // // Validate ... if (providedForId.IsNull()) { XException.InvalidArgs.Throw(); } // var result = false; var references = await GetAllReferences( providedForId: providedForId, cancellationToken: cancellationToken ); if (!references.IsNull() && references.HasChild()) { // foreach (var reference in references) { // // Remove Reference ... var itemReferences = reference.GetReferences() .Where(r => r.ProvidedFor != Provider || (r.ProvidedFor == Provider && r.ReferencedTo != $"{providedForId}")) .ToList(); var updatedModel = reference.UpdateReferences(itemReferences); updatedModel = await CategoryProvider.UpdateAsync( saveChanges: true, item: updatedModel, id: updatedModel.Id, connectionId: connectionId, cancellationToken: cancellationToken ); if (!updatedModel.IsNullOrDefault()) { result = true; } } } // return result; } #endregion // #region Private ... private string GetProvidedID( TKey providedForId, char splitter = '_' ) { // // Validate ... if (providedForId.IsNull()) { XException.InvalidArgs.Throw(); } // var result = $"{Provider}{splitter}{providedForId}"; return result; } private string GetIndexedProvidedID( TKey providedForId, int forIndex, char splitter = '_' ) { // var result = $"{GetProvidedID(providedForId, splitter)}{splitter}{forIndex}"; // return result; } private async Task> GetReferencingIndexes( TKey providedForId, CancellationToken cancellationToken = default ) { // // Validate ... var isValid = !providedForId.IsNull(); if (!isValid) { XException.InvalidArgs.Throw(); } // var result = new List(); var referencings = await GetAllReferencings( providedForId: providedForId, cancellationToken: cancellationToken ); isValid = !referencings.IsNull() && referencings.HasChild(); if (!isValid) { return result; } // result = referencings .OrderBy(r => r.Index) .Select(r => r.Index) .ToList(); // return result; } #endregion } }