From 82a1936a9dff0ed8dc323a14c184cb545da41879 Mon Sep 17 00:00:00 2001 From: Hadi Khazaee Asl Date: Wed, 17 Dec 2025 14:46:07 +0330 Subject: [PATCH] Refactoring XReference Mechanism ... --- Extensions/xTagServiceExtension.cs | 5 +- Interfaces/IXBaseTagProvider.cs | 245 +--- Models/Dtos/XTagDto.cs | 3 +- Providers/XBaseTagProvider.cs | 1676 ++++++++++++---------------- 4 files changed, 783 insertions(+), 1146 deletions(-) diff --git a/Extensions/xTagServiceExtension.cs b/Extensions/xTagServiceExtension.cs index 9b2e4d0..ea5d28f 100644 --- a/Extensions/xTagServiceExtension.cs +++ b/Extensions/xTagServiceExtension.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using xCommons.Extensions; +using xDataService.Extensions; using xTagService.Models.Dtos; using xTagService.Models.Entities; @@ -38,7 +39,7 @@ namespace xTagService.Extensions { // result.References = source.References - .ParseListString() + .ParseXReferenceList() .ToList(); } } @@ -73,7 +74,7 @@ namespace xTagService.Extensions // if (!source.References.IsNull() && source.References.HasChild()) { - result.References = source.References.ToListString(); + result.References = source.References.ToXReferenceString(); } } diff --git a/Interfaces/IXBaseTagProvider.cs b/Interfaces/IXBaseTagProvider.cs index b093104..e423dba 100644 --- a/Interfaces/IXBaseTagProvider.cs +++ b/Interfaces/IXBaseTagProvider.cs @@ -1,258 +1,81 @@ -using System; -using System.Collections.Generic; -using System.Linq.Expressions; using System.Threading.Tasks; -using xModels.Dtos; +using xDataService.Interfaces; +using xDataService.Models; using xTagService.Models.Dtos; -using xTagService.Models.Entities; namespace xTagService.Interfaces { - public interface IXBaseTagProvider + /// + /// Implementing XBaseTagProvider ... + /// + /// + public interface IXBaseTagProvider : IXBaseReferencedProvider { // #region Props ... /// - /// Specified Provider ... + /// Provider Identifier ... /// - string ProvideFor { get; } - - /// - /// Specified Provider Repositroy ... - /// - IXTagProvider Provider { get; } + IXTagProvider TagProvider { get; } #endregion // - #region Helper ... + #region Tools ... /// - /// Get Specified Identifier ... - /// - /// - /// - string GetIdentifier(TKey forProvidedId); - #endregion - - // - #region Implementation ... - /// - /// Attach Specified Label to Specified Provided Model ... + /// Add Reference a Tag to Specific Provided ID ... /// /// - /// + /// + /// /// /// - Task Attach( + Task AddReference( string tag, - TKey forProvidedId, + TKey providedForId, + int forIndex = 0, string connectionId = null ); /// - /// Detach Specified Label From Specified Provided Model ... + /// Add Reference a Tag to Specific XRefence ... /// /// - /// + /// /// /// - Task Detach( + Task AddReference( string tag, - TKey forProvidedId, + XReference reference, string connectionId = null - ); + ); /// - /// Add Specified Tag ... + /// Remove Reference a Tag for Specific Provided ID ... /// - /// - /// + /// + /// + /// if null, removes all /// /// - Task Add( - XTagDto model, - TKey forProvidedId, - string connectionId = null - ); - - /// - /// Retrieve Specified Item as Dto ... - /// - /// - /// - Task Get(int id); - - /// - /// Retrieve Specified Tag by it's Label ... - /// - /// - /// - Task GetTag(string tag); - - /// - /// Retrieve Specified Tag by it's Label ... - /// - /// - /// - /// - Task GetTagFor( + Task RemoveReference( string tag, - TKey forProvidedId - ); - - /// - /// Retrieve All Items as Dto ... - /// - /// - Task> GetAll(); - - /// - /// Retrieve All Items as Dto ... - /// - /// - /// - Task> GetTags(TKey forProvidedId); - - /// - /// Find Specified Item by Condition ... - /// - /// - /// - Task FindOne(Expression> whereClause); - - /// - /// Find Many Items based on Conditions ... - /// - /// - /// - Task> FindMany(Expression> whereClause); - - /// - /// Find Many Items based on Conditions ... - /// - /// - /// - /// - Task> FindManyFor( - Expression> whereClause, - TKey forProvidedId - ); - - /// - /// Query Model retrieving Dtos ... - /// - /// - /// - Task> Query(XQuery query); - - /// - /// Query Model retrieving Dtos ... - /// - /// - /// - /// - Task> QueryTagsFor( - XQuery query, - TKey forProvidedId - ); - - /// - /// Query Conditional Retrieving Dtos ... - /// - /// - /// - /// - Task> ConditionalQuery( - XQuery query, - Expression> whereClause - ); - - /// - /// Query Conditional Retrieving Dtos ... - /// - /// - /// - /// - /// - Task> ConditionalQueryTagsFor( - XQuery query, - Expression> whereClause, - TKey forProvidedId - ); - - /// - /// Remove Specified Dto ... - /// - /// - /// - /// - Task Remove( - int id, + TKey providedForId, + int? forIndex = null, string connectionId = null ); /// - /// Remove All Specified Tags ... - /// - /// - /// - /// - Task RemoveTagsFor( - TKey forProvidedId, - string connectionId = null - ); - - /// - /// Update Specified Dto ... - /// - /// - /// - /// - /// - /// - Task Update( - int id, - XTagDto model, - TKey forProvidedId, - string connectionId = null - ); - - /// - /// Count Exists Entities ... - /// - /// - Task Count(); - - /// - /// Check Entity Exists or not ... - /// - /// - /// - Task IsExists(int id); - - /// - /// Check Tag Exists by Label ... + /// Remove Reference a Tag for Specific XRefence ... /// /// + /// + /// /// - Task IsTagExists(string tag); - - /// - /// Check Tag Exists by Label ... - /// - /// - /// - /// - Task IsTagExistsFor( + Task RemoveReference( string tag, - TKey forProvidedId + XReference reference, + string connectionId = null ); - - /// - /// Check Specified Model has Tag or not ... - /// - /// - /// - Task IsExistsFor(TKey forProvidedId); #endregion } } \ No newline at end of file diff --git a/Models/Dtos/XTagDto.cs b/Models/Dtos/XTagDto.cs index 7f62031..60270f8 100644 --- a/Models/Dtos/XTagDto.cs +++ b/Models/Dtos/XTagDto.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using xDataService.Models; using xModels.Base; namespace xTagService.Models.Dtos @@ -7,6 +8,6 @@ namespace xTagService.Models.Dtos { public int Id { get; set; } public string Tag { get; set; } - public IList References = new List(); + public IList> References = new List>(); } } \ No newline at end of file diff --git a/Providers/XBaseTagProvider.cs b/Providers/XBaseTagProvider.cs index 41c563c..f88ab70 100644 --- a/Providers/XBaseTagProvider.cs +++ b/Providers/XBaseTagProvider.cs @@ -1,12 +1,13 @@ -using System; using System.Collections.Generic; using System.Linq; -using System.Linq.Expressions; +using System.Runtime.InteropServices; using System.Threading.Tasks; using xCommons.Extensions; using xDataService.Extensions; +using xDataService.Models; using xExceptions.Constants; using xModels.Dtos; +using xTagService.Extensions; using xTagService.Interfaces; using xTagService.Models.Dtos; using xTagService.Models.Entities; @@ -18,14 +19,14 @@ namespace xTagService.Providers // #region Props ... /// - /// Specified Provider ... + /// Provider Identifier ... /// - public string ProvideFor { get; } + public string Provider { get; } /// - /// Specified Provider Repositroy ... + /// Tag Provider for Maipulating Tags ... /// - public IXTagProvider Provider { get; } + public IXTagProvider TagProvider { get; } #endregion // @@ -36,858 +37,228 @@ namespace xTagService.Providers ) { // - Provider = tagProvider; - ProvideFor = providedFor; + Provider = providedFor; + TagProvider = tagProvider; } #endregion // - #region Helper ... - /// - /// Get Specified Identifier ... - /// - /// - /// - public string GetIdentifier(TKey forProvidedId) + #region Identifier ... + public async Task LastIndex(TKey providedForId) { - // - var result = string.Empty; - - // - result = $"{ProvideFor}_{forProvidedId}"; - - // - return result; + return await Task.FromResult(GetLastIndex(providedForId)); } #endregion // - #region Implementation ... + #region Referenced Actions ... /// - /// Attach Specified Label to Specified Provided Model ... + /// Get Specified Indexed Reference for Specific Provided ID ... /// - /// - /// - /// + /// + /// /// - public async Task Attach( - string tag, - TKey forProvidedId, - string connectionId = null + public async Task GetReference( + TKey providedForId, + int forIndex = 0 ) { // // Validate ... - var isValid = !tag.IsNullOrEmpty() && - !forProvidedId.IsNull(); - if (!isValid) + if (providedForId.IsNull()) { XException.InvalidArgs.Throw(); } // - // Generate Identifier ... - var identifier = GetIdentifier(forProvidedId); - - // - // Check Tag Exists or not ... - isValid = await Provider.IsTagExists(tag); - - // - XTagDto result = null; - - // - // Add new XTag Entity ... - if (!isValid) + // Normalize ... + if (forIndex < 0) { - // - result = await Provider.Add(new XTagDto - { - Tag = tag, - References = new List { identifier } - }, - connectionId); - } - // - // Update Exists Tag Enttiy ... - else - { - // - result = await Provider.GetTag(tag); - isValid = !result.IsNullOrDefault(); - if (!isValid) - { - XException.ActionFailed.Throw(); - } - - // - result.References.Add(identifier); - result = await Provider.Update( - result.Id, - result, - connectionId - ); - isValid = !result.IsNullOrDefault(); - if (!isValid) - { - XException.ActionFailed.Throw(); - } + forIndex = 0; } // - return result; - } - - /// - /// Detach Specified Label From Specified Provided Model ... - /// - /// - /// - /// - /// - public async Task Detach( - string tag, - TKey forProvidedId, - string connectionId = null - ) - { - // - // Validate ... - var isValid = !tag.IsNullOrEmpty() && - !forProvidedId.IsNull(); - if (!isValid) - { - XException.InvalidArgs.Throw(); - } - - // - // Generate Identifier ... - var identifier = GetIdentifier(forProvidedId); - - // - XTagDto result = null; - - // - // Check Tag Exists or not ... - isValid = await IsTagExistsFor( - tag: tag, - forProvidedId: forProvidedId - ); - if (!isValid) - { - return result; - } - - // - // Remove id From XTag References ... - result = await GetTagFor( - tag: tag, - forProvidedId: forProvidedId - ); - isValid = !result.IsNullOrDefault(); - if (!isValid) + // Retrieve Last Index ... + var lastIndex = GetLastIndex(providedForId); + if (forIndex > lastIndex) { XException.NotFound.Throw(); } // - // Remove identifier ... - result.References = result - .References - .Where(rf => rf.ToNormalString() != identifier.ToNormalString()) - .ToList(); - result = await Provider.Update( - result.Id, - result, - connectionId + var indexedIdentifier = GetIndexedProvidedID( + forIndex: forIndex, + providedForId: providedForId ); + var entity = TagProvider.Repository + .AsQueryable() + .Where(t => t.References.Contains(indexedIdentifier)) + .FirstOrDefault(); + if (entity.IsNullOrDefault()) + { + XException.NotFound.Throw(); + } + + // + var result = await Task.FromResult(entity.ToDto()); + return result; + } + + /// + /// Retrieve all Exists References of Specific Provided ID ... + /// + /// + /// + public async Task> GetAllReferences(TKey providedForId) + { + // + // Validate ... + if (providedForId.IsNull()) + { + XException.InvalidArgs.Throw(); + } + + // + var identifier = GetProvidedID(providedForId); + var entities = TagProvider.Repository + .AsQueryable() + .Where(t => t.References.Contains(identifier)) + .AsEnumerable(); + + // + var dtos = new List(); + foreach (var entity in entities) + { + // + var dto = entity.ToDto(); + if (!dto.IsNullOrDefault()) + { + dtos.Add(dto); + } + } + + // + List result = dtos; + if (dtos.HasChild()) + { + // + // Sorting Result Based on Indexes ... + var lastIndex = GetLastIndex(providedForId); + if (lastIndex > 0) + { + // + result.Clear(); + for (int i = 0; i <= lastIndex; i++) + { + // + var dto = dtos + .FirstOrDefault(d => d.References + .Any(dr => dr.Index == i && dr.ReferencedTo == $"{providedForId}")); + if (!dto.IsNullOrDefault()) + { + result.Add(dto); + } + } + } + } + + // + return await Task.FromResult(result); + } + + /// + /// Retrieve References of Specific Provided ID as Query ... + /// + /// + /// + public async Task> QueryReferences( + XQuery query, + TKey providedForId + ) + { + // + // Validate ... + if (query.IsNull() || providedForId.IsNull()) + { + XException.InvalidArgs.Throw(); + } + + // + // Normalize ... + query = query.NormalizeQuery(TagProvider.DataConfiguration); + + // + var items = await GetAllReferences(providedForId); + 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; } /// - /// Add Specified Tag ... + /// Add Reference a Model to Specific Provided ID ... /// /// - /// + /// + /// /// /// - public async Task Add( + public async Task AddReference( XTagDto model, - TKey forProvidedId, - string connectionId = null - ) - { - // - // Validate ... - var isValid = !model.IsNull() && - !forProvidedId.IsNull(); - if (!isValid) - { - return null; - } - - // - // Refrences ... - var identifier = GetIdentifier(forProvidedId); - if (!model.References.IsNull() && model.References.HasChild()) - { - // - isValid = !identifier.IsNullOrEmpty() && - !model.References.Any(r => r.ToNormalString() == identifier.ToNormalString()); - } - if (isValid) - { - model.References.Add(identifier); - } - - // - model = await Provider.Add(model, connectionId); - - // - return model; - } - - /// - /// Retrieve Specified Item as Dto ... - /// - /// - /// - public async Task Get(int id) - { - // - XTagDto result = null; - - // - var isValid = await Provider.IsExists(id); - if (!isValid) - { - XException.NotFound.Throw(); - } - - // - var dto = await Provider.Get(id); - isValid = !dto.IsNullOrDefault(); - if (!isValid) - { - XException.NotFound.Throw(); - } - - // - // Validate Dto ... - isValid = dto.References.Any(e => e.ToNormalString() - .Contains(ProvideFor.ToNormalString())); - if (!isValid) - { - XException.NotAllowed.Throw(); - } - - // - result = dto; - - // - return result; - } - - /// - /// Retrieve Specified Tag by it's Label ... - /// - /// - /// - public async Task GetTag(string tag) - { - // - var isValid = !tag.IsNullOrEmpty(); - if (!isValid) - { - XException.InvalidArgs.Throw(); - } - - // - var dto = await Provider.GetTag(tag); - isValid = !dto.IsNullOrDefault(); - if (!isValid) - { - XException.NotFound.Throw(); - } - - // - // Check Identifier ... - isValid = !dto.References.IsNull() && - !dto.References.HasChild() && - dto.References.Any(e => e.ToNormalString().Contains(ProvideFor.ToNormalString())); - if (!isValid) - { - XException.NotAllowed.Throw(); - } - - // - return dto; - } - - /// - /// Retrieve Specified Tag by it's Label ... - /// - /// - /// - /// - public async Task GetTagFor( - string tag, - TKey forProvidedId - ) - { - // - XTagDto result = null; - - // - var dtos = await GetTags(forProvidedId); - if (!dtos.IsNull() && dtos.HasChild()) - { - // - result = dtos.Where(dto => dto.Tag.ToNormalString() == tag.ToNormalString()) - .FirstOrDefault(); - } - - // - return result; - } - - /// - /// Retrieve All Items as Dto ... - /// - /// - public async Task> GetAll() - { - // - var result = await Provider.GetAll(); - if (!result.IsNull() && result.HasChild()) - { - result = result - .Where(e => e.References.Any(er => er.ToNormalString().Contains(ProvideFor.ToNormalString()))); - } - - // - return result; - } - - /// - /// Retrieve All Items as Dto ... - /// - /// - /// - public async Task> GetTags(TKey forProvidedId) - { - // - var result = new List(); - - // - string identifier = GetIdentifier(forProvidedId); - var tags = await GetAll(); - if (!tags.IsNull() && tags.HasChild()) - { - // - result = - tags.Where(t => t.References.Any(tr => tr.ToNormalString() == identifier.ToNormalString())) - .ToList(); - } - - // - return result; - } - - /// - /// Find Specified Item by Condition ... - /// - /// - /// - public async Task FindOne(Expression> whereClause) - { - // - var dto = await Provider.FindOne(whereClause); - if (dto.IsNullOrDefault()) - { - XException.NotFound.Throw(); - } - - // - var isValid = dto.References.Any(dr => dr.ToNormalString().Contains(ProvideFor.ToNormalString())); - if (!isValid) - { - XException.NotAllowed.Throw(); - } - - // - return dto; - } - - /// - /// Find Many Items based on Conditions ... - /// - /// - /// - public async Task> FindMany(Expression> whereClause) - { - // - var result = new List(); - var dtos = await Provider.FindMany(whereClause); - if (!dtos.IsNull() && dtos.HasChild()) - { - // - bool isValid = false; - dtos.ToList() - .ForEach(dto => - { - // - isValid = dto.References.HasChild() && - dto.References.Any(r => r.ToNormalString().Contains(ProvideFor.ToNormalString())); - if (isValid) - { - result.Add(dto); - } - }); - } - - // - return result; - } - - /// - /// Find Many Items based on Conditions ... - /// - /// - /// - /// - public async Task> FindManyFor( - Expression> whereClause, - TKey forProvidedId - ) - { - // - var result = new List(); - var dtos = await Provider.FindMany(whereClause); - if (!dtos.IsNull() && dtos.HasChild()) - { - // - bool isValid = false; - var identifier = GetIdentifier(forProvidedId); - dtos.ToList() - .ForEach(dto => - { - // - isValid = dto.References.HasChild() && - dto.References.Any(r => r.ToNormalString() == identifier.ToNormalString()); - if (isValid) - { - result.Add(dto); - } - }); - } - - // - return result; - } - - /// - /// Query Model retrieving Dtos ... - /// - /// - /// - public async Task> Query(XQuery query) - { - // - // Validate ... - if (query.IsNull()) - { - XException.InvalidArgs.Throw(); - } - - // - // Normalize ... - query = query.NormalizeQuery(Provider.DataConfiguration); - - // - var items = (await GetAll()) - .ToList(); - var totalItemsCount = items.Count(); - - // - // Apply Filter ... - if (!query.Filter.IsNullOrEmpty()) - { - // - items = items - .ApplyFilter(query.Filter) - .ToList(); - } - - // - 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 - ) - .ToList(); - - // - // Apply Paging ... - items = items - .ToList() - .ApplyPaging( - query.Page, - query.PageSize - ) - .ToList(); - } - - - // - // 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; - } - - /// - /// Query Model retrieving Dtos ... - /// - /// - /// - /// - public async Task> QueryTagsFor( - XQuery query, - TKey forProvidedId - ) - { - // - // Validate ... - if (query.IsNull() || forProvidedId.IsNull()) - { - XException.InvalidArgs.Throw(); - } - - // - // Normalize ... - query = query.NormalizeQuery(Provider.DataConfiguration); - - // - var items = (await GetTags(forProvidedId)) - .ToList(); - var totalItemsCount = items.Count(); - - // - // Apply Filter ... - if (!query.Filter.IsNullOrEmpty()) - { - // - items = items - .ApplyFilter(query.Filter) - .ToList(); - } - - // - 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 - ) - .ToList(); - - // - // Apply Paging ... - items = items - .ToList() - .ApplyPaging( - query.Page, - query.PageSize - ) - .ToList(); - } - - // - // 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; - } - - /// - /// Query Conditional Retrieving Dtos ... - /// - /// - /// - /// - public async Task> ConditionalQuery( - XQuery query, - Expression> whereClause - ) - { - // - // Validate ... - if (query.IsNull()) - { - XException.InvalidArgs.Throw(); - } - - // - // Normalize ... - query = query.NormalizeQuery(Provider.DataConfiguration); - - // - var items = (await FindMany(whereClause)) - .ToList(); - var totalItemsCount = items.Count(); - - // - // Apply Filter ... - if (!query.Filter.IsNullOrEmpty()) - { - // - items = items - .ApplyFilter(query.Filter) - .ToList(); - } - - // - 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 - ) - .ToList(); - - // - // Apply Paging ... - items = items - .ToList() - .ApplyPaging( - query.Page, - query.PageSize - ) - .ToList(); - } - - - // - // 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; - } - - /// - /// Query Conditional Retrieving Dtos ... - /// - /// - /// - /// - /// - public async Task> ConditionalQueryTagsFor( - XQuery query, - Expression> whereClause, - TKey forProvidedId - ) - { - // - // Validate ... - if (query.IsNull() || forProvidedId.IsNull()) - { - XException.InvalidArgs.Throw(); - } - - // - // Normalize ... - query = query.NormalizeQuery(Provider.DataConfiguration); - - // - var items = (await FindManyFor( - whereClause: whereClause, - forProvidedId: forProvidedId - )) - .ToList(); - var totalItemsCount = items.Count(); - - // - // Apply Filter ... - if (!query.Filter.IsNullOrEmpty()) - { - // - items = items - .ApplyFilter(query.Filter) - .ToList(); - } - - // - 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 - ) - .ToList(); - - // - // Apply Paging ... - items = items - .ToList() - .ApplyPaging( - query.Page, - query.PageSize - ) - .ToList(); - } - - - // - // 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; - } - - /// - /// Remove Specified Dto ... - /// - /// - /// - /// - public async Task Remove( - int id, - string connectionId = null - ) - { - // - var isValid = id.IsValidIntId(); - if (!isValid) - { - XException.InvalidArgs.Throw(); - } - - // - isValid = await IsExists(id); - if (!isValid) - { - XException.NotFound.Throw(); - } - - // - var result = await Provider.Remove(id, connectionId); - if (result.IsNullOrDefault()) - { - XException.ActionFailed.Throw(); - } - - // - return result; - } - - /// - /// Remove All Specified Tags ... - /// - /// - /// - /// - public async Task RemoveTagsFor( - TKey forProvidedId, + TKey providedForId, + int forIndex = 0, string connectionId = null ) { @@ -895,24 +266,552 @@ namespace xTagService.Providers var result = false; // - var dtos = await GetTags(forProvidedId); - result = !dtos.IsNull() && dtos.HasChild(); - if (result) + // Validate ... + var isValid = !model.IsNullOrDefault() && + !model.Tag.IsNullOrEmpty() && + model.Id.IsValidIntId() && + !providedForId.IsNull(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Model Exists ... + isValid = await TagProvider.IsExists(model.Id); + if (!isValid) + { + XException.NotFound.Throw(); + } + + // + // Update Model by Retrieving ... + model = await TagProvider.Get(model.Id); + + // + var lastIndex = GetLastIndex(providedForId); + if (lastIndex == 0) { // - foreach (var dto in dtos) + // This Means there isnot any Reference ... + forIndex = 0; + model.References.Add(new XReference + { + Index = forIndex, + ProvidedFor = Provider, + ReferencedTo = $"{providedForId}" + }); + } + else + { + // + // Check Provided Index Exists or not ... + var isExists = model.References.Any(r => r.Index == forIndex); + if (!isExists) { // - try + // Normalize For Index ... + if (forIndex > lastIndex + 1) { - // - await Detach(dto.Tag, forProvidedId, connectionId); - if (!result) - { - result = true; - } + forIndex = lastIndex + 1; } - catch { } + + // + // Add New Reference ... + model.References.Add(new XReference + { + Index = forIndex, + ProvidedFor = Provider, + ReferencedTo = $"{providedForId}" + }); + } + else + { + } + } + + // + // Update Data Base ... + model = await TagProvider + .Update( + id: model.Id, + model: model, + connectionId: connectionId + ); + result = !model.IsNullOrDefault(); + + // + return result; + } + + /// + /// Add Reference a Model to Specific XRefence ... + /// + /// + /// + /// + /// + public async Task AddReference( + XTagDto model, + XReference reference, + string connectionId = null + ) + { + // + // Validate ... + // Since other Validations Handled in Calling, we Ignore them here ... + if (model.IsNull() || + reference.IsNullOrDefault() || + (!reference.ProvidedFor.IsNullOrEmpty() && reference.ProvidedFor != Provider)) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await AddReference( + model: model, + connectionId: connectionId, + providedForId: reference.ReferencedTo + ); + + // + return result; + } + + /// + /// Remove Reference a Model for Specific Provided ID ... + /// + /// + /// + /// + /// if null, removes all + /// + public async Task RemoveReference( + XTagDto model, + TKey providedForId, + int? forIndex = null, + string connectionId = null + ) + { + // + // Validate ... + if (model.IsNull() || + providedForId.IsNull() || + !model.Id.IsValidIntId()) + { + XException.InvalidArgs.Throw(); + } + + // + // Retrieve Model ... + model = await TagProvider.Get(model.Id); + if (model.IsNullOrDefault()) + { + XException.NotFound.Throw(); + } + + // + // Check Model Reference to ID ... + var isReferenced = model.References + .Any(r => r.ProvidedFor == Provider && + r.ReferencedTo == $"{providedForId}" && + (!forIndex.HasValue || r.Index == forIndex.Value)); + + // + var result = false; + if (isReferenced) + { + // + model.References = model.References + .Where(r => r.ProvidedFor != Provider || + (r.ProvidedFor == Provider && + r.ReferencedTo != $"{providedForId}" && + (!forIndex.HasValue || r.Index == forIndex.Value))) + .ToList(); + + // + model = await TagProvider.Update( + id: model.Id, + model: model, + connectionId: connectionId + ); + result = !model.IsNullOrDefault(); + } + + // + return result; + } + + /// + /// Remove Reference a Model for Specific XRefence ... + /// + /// + /// + /// + /// + public async Task RemoveReference( + XTagDto model, + XReference reference, + string connectionId = null + ) + { + // + if (model.IsNullOrDefault() || + reference.IsNullOrDefault() || + reference.ReferencedTo.IsNull() || + reference.ProvidedFor != Provider) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await RemoveReference( + model: model, + forIndex: reference.Index, + connectionId: connectionId, + providedForId: reference.ReferencedTo + ); + + // + return result; + } + + /// + /// Remove All References to Specified Key ... + /// + /// + /// + /// + public async Task RemoveReferences( + TKey providedForId, + string connectionId = null + ) + { + // + // Validate ... + if (providedForId.IsNull()) + { + XException.InvalidArgs.Throw(); + } + + // + var result = false; + var references = await GetAllReferences(providedForId); + if (!references.IsNull() && references.HasChild()) + { + // + foreach (var reference in references) + { + // + // Remove Reference ... + reference.References = reference.References + .Where(r => + r.ProvidedFor != Provider || + (r.ProvidedFor == Provider && + r.ReferencedTo != $"{providedForId}")) + .ToList(); + var updatedModel = await TagProvider.Update( + id: reference.Id, + model: reference, + connectionId: connectionId + ); + if (!updatedModel.IsNullOrDefault()) + { + result = true; + } + } + } + + // + return result; + } + #endregion + + // + #region Tools ... + /// + /// Add Reference a Tag to Specific Provided ID ... + /// + /// + /// + /// + /// + /// + public async Task AddReference( + string tag, + TKey providedForId, + int forIndex = 0, + string connectionId = null + ) + { + // + var result = false; + + // + // Validate ... + result = !tag.IsNullOrEmpty(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Tag Exists ... + result = IsExists(tag); + if (!result) + { + XException.NotFound.Throw(); + } + + // + var dto = GetTagByTag(tag); + result = !dto.IsNullOrDefault(); + if (!result) + { + XException.ActionFailed.Throw(); + } + + // + result = await AddReference( + model: dto, + forIndex: forIndex, + connectionId: connectionId, + providedForId: providedForId + ); + + // + return result; + } + + /// + /// Add Reference a Tag to Specific XRefence ... + /// + /// + /// + /// + /// + public async Task AddReference( + string tag, + XReference reference, + string connectionId = null + ) + { + // + var result = false; + + // + // Validate ... + result = !tag.IsNullOrEmpty(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Tag Exists ... + result = IsExists(tag); + if (!result) + { + XException.NotFound.Throw(); + } + + // + var dto = GetTagByTag(tag); + result = !dto.IsNullOrDefault(); + if (!result) + { + XException.ActionFailed.Throw(); + } + + // + result = await AddReference( + model: dto, + reference: reference, + connectionId: connectionId + ); + + // + return result; + } + + /// + /// Remove Reference a Tag for Specific Provided ID ... + /// + /// + /// + /// if null, removes all + /// + /// + public async Task RemoveReference( + string tag, + TKey providedForId, + int? forIndex = null, + string connectionId = null + ) + { + // + var result = false; + + // + // Validate ... + result = !tag.IsNullOrEmpty(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Tag Exists ... + result = IsExists(tag); + if (!result) + { + XException.NotFound.Throw(); + } + + // + var dto = GetTagByTag(tag); + result = !dto.IsNullOrDefault(); + if (!result) + { + XException.ActionFailed.Throw(); + } + + // + result = await RemoveReference( + model: dto, + connectionId: connectionId, + providedForId: providedForId + ); + + // + return result; + } + + /// + /// Remove Reference a Tag for Specific XRefence ... + /// + /// + /// + /// + /// + public async Task RemoveReference( + string tag, + XReference reference, + string connectionId = null + ) + { + // + var result = false; + + // + // Validate ... + result = !tag.IsNullOrEmpty(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Tag Exists ... + result = IsExists(tag); + if (!result) + { + XException.NotFound.Throw(); + } + + // + var dto = GetTagByTag(tag); + result = !dto.IsNullOrDefault(); + if (!result) + { + XException.ActionFailed.Throw(); + } + + // + result = await RemoveReference( + model: dto, + reference: reference, + connectionId: connectionId + ); + + // + 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}{providedForId}"; + + // + return result; + } + + private int GetLastIndex(TKey providedForId) + { + // + var result = 0; + + // + // Validate ... + var isValid = !providedForId.IsNull(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + var identifier = GetProvidedID(providedForId); + + // + var all = TagProvider.Repository + .AsQueryable() + .Where(t => t.References + .Contains(identifier)) + .Select(t => t.References) + .AsEnumerable(); + if (all.IsNull() || !all.HasChild()) + { + result = 0; + } + else + { + // + var references = all + .SelectMany(a => a.ParseXReferenceList()) + .OrderByDescending(r => r.Index) + .ToList(); + if (!references.IsNull() && references.HasChild()) + { + result = references[0].Index; + } + else + { + result = 0; } } @@ -920,128 +819,41 @@ namespace xTagService.Providers return result; } - /// - /// Update Specified Dto ... - /// - /// - /// - /// - /// - /// - public async Task Update( - int id, - XTagDto model, - TKey forProvidedId, - string connectionId = null - ) + private XTagDto GetTagByTag(string tag) { // - // Validate ... - var identifier = GetIdentifier(forProvidedId); - bool isValid = id.IsValidIntId() && - !forProvidedId.IsNull() && - !model.IsNullOrDefault() && - !model.References.IsNull() && - !identifier.IsNullOrEmpty() && - model.References.Any(er => er.ToNormalString() == identifier.ToNormalString()); - if (!isValid) - { - XException.InvalidArgs.Throw(); - } + XTagDto result = null; // - var result = await Provider.Update( - id, - model, - connectionId - ); - if (result.IsNullOrDefault()) + if (!tag.IsNullOrEmpty()) { - XException.ActionFailed.Throw(); + // + var entity = TagProvider.Repository + .AsQueryable() + .FirstOrDefault(e => e.Tag == tag); + if (!entity.IsNullOrDefault()) + { + result = entity.ToDto(); + } } // return result; } - /// - /// Count Exists Entities ... - /// - /// - public async Task Count() - { - return (await GetAll()).Count(); - } - - /// - /// Check Entity Exists or not ... - /// - /// - /// - public async Task IsExists(int id) + private bool IsExists(string tag) { // - var result = (await GetAll()).Any(e => e.Id == id); + var result = false; // - return result; - } - - /// - /// Check Tag Exists by Label ... - /// - /// - /// - public async Task IsTagExists(string tag) - { - // - var result = !(await GetTag(tag)) - .IsNullOrDefault(); - - // - return result; - } - - /// - /// Check Specified Model has Tag or not ... - /// - /// - /// - public async Task IsExistsFor(TKey forProvidedId) - { - // - // Validate ... - var result = !forProvidedId.IsNull(); - if (!result) + if (!tag.IsNullOrEmpty()) { - return result; + // + var dto = GetTagByTag(tag); + result = !dto.IsNullOrDefault(); } - // - result = (await GetTags(forProvidedId)).Count() >= 1; - - // - return result; - } - - /// - /// Check Tag Exists by Label ... - /// - /// - /// - /// - public async Task IsTagExistsFor( - string tag, - TKey forProvidedId - ) - { - // - var result = !(await GetTagFor( - tag: tag, - forProvidedId: forProvidedId - )) - .IsNullOrDefault(); - // return result; }