Update Lang Version to 12 ...

Fix and Complete XCategory and XSubCategory Services and Service Implementations ...
This commit is contained in:
2026-06-12 02:18:44 +03:30
parent 45e6bf1362
commit f1dbbc8f1d
6 changed files with 1649 additions and 25 deletions
+75 -1
View File
@@ -1,15 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using xCategoryService.Models.Dtos;
using xCategoryService.Models.Entities;
using xCategoryService.Providers.Dtos;
using xIdentityModels.Models;
using xModels.Dtos;
using xPushService.Interfaces;
namespace xCategoryService.Interfaces
{
public interface IXCategoryProvider : IXBaseProvided<XCategory, XCategoryDto, int, XCategoryDtoHub>
{
/// <summary>
/// Check Has Specified Locale or not ...
/// </summary>
/// <param name="id"></param>
/// <param name="language"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<bool> HasLocale(
int id,
string language,
CancellationToken cancellationToken = default
);
/// <summary>
/// Load an Item by all of it's Related Translations ...
/// </summary>
@@ -65,11 +83,67 @@ namespace xCategoryService.Interfaces
/// <param name="userInfo"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<XCategoryResourceDto> RefereshResource(
Task<XCategoryResourceDto> Referesh(
XCategoryResourceDto resource,
string connectionId = null,
XUserClaimsInfoDto userInfo = null,
CancellationToken cancellationToken = default
);
/// <summary>
/// Get All Resources ...
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<IEnumerable<XCategoryResourceDto>> GetAllResources(
CancellationToken cancellationToken = default
);
/// <summary>
/// Find One Resource ...
/// </summary>
/// <param name="predicate"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<XCategoryResourceDto> FindOne(
Expression<Func<XCategoryResourceDto, bool>> predicate = null,
CancellationToken cancellationToken = default
);
/// <summary>
/// Find Many Resource ...
/// </summary>
/// <param name="predicate"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<IEnumerable<XCategoryResourceDto>> FindMany(
Expression<Func<XCategoryResourceDto, bool>> predicate = null,
Func<IQueryable<XCategoryResourceDto>, IOrderedQueryable<XCategoryResourceDto>> orderBuilder = null,
CancellationToken cancellationToken = default
);
/// <summary>
/// Retrieve Resources by Query Pattern ...
/// </summary>
/// <param name="query"></param>
/// <param name="predicate"></param>
/// <param name="orderBuilder"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<XQueryResult<XCategoryResourceDto>> QueryResources(
XQuery query = null,
Expression<Func<XCategoryResourceDto, bool>> predicate = null,
Func<IQueryable<XCategoryResourceDto>, IOrderedQueryable<XCategoryResourceDto>> orderBuilder = null,
CancellationToken cancellationToken = default
);
/// <summary>
/// Get All Resources as Async Enumerable ...
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
IAsyncEnumerable<XCategoryResourceDto> GetAllResourcesAsAsyncEnumerable(
CancellationToken cancellationToken = default
);
}
}
+140 -1
View File
@@ -1,10 +1,149 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using xCategoryService.Models.Dtos;
using xCategoryService.Models.Entities;
using xCategoryService.Providers.Dtos;
using xIdentityModels.Models;
using xModels.Dtos;
using xPushService.Interfaces;
namespace xCategoryService.Interfaces
{
public interface IXSubCategoryProvider : IXBaseProvided<XSubCategory, XSubCategoryDto, int, XSubCategoryDtoHub>
{ }
{
/// <summary>
/// Check Has Specified Locale or not ...
/// </summary>
/// <param name="id"></param>
/// <param name="language"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<bool> HasLocale(
int id,
string language,
CancellationToken cancellationToken = default
);
/// <summary>
/// Load an Item by all of it's Related Translations ...
/// </summary>
/// <param name="id"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<XSubCategoryResourceDto> GetResource(
int id,
CancellationToken cancellationToken = default
);
/// <summary>
/// Add Category Item for Specified Language ...
/// </summary>
/// <param name="item"></param>
/// <param name="langauge"></param>
/// <param name="connectionId"></param>
/// <param name="userInfo"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<XSubCategoryResourceDto> Add(
XSubCategoryDto item,
string langauge = null,
string connectionId = null,
XUserClaimsInfoDto userInfo = null,
CancellationToken cancellationToken = default
);
/// <summary>
/// Update a Category Item for Specified Language ...
/// </summary>
/// <param name="id"></param>
/// <param name="item"></param>
/// <param name="language"></param>
/// <param name="connectionId"></param>
/// <param name="userInfo"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<XSubCategoryResourceDto> Update(
int id,
XSubCategoryDto item,
string language = null,
string connectionId = null,
XUserClaimsInfoDto userInfo = null,
CancellationToken cancellationToken = default
);
/// <summary>
/// Referesh a Resource ...
/// </summary>
/// <param name="resource"></param>
/// <param name="connectionId"></param>
/// <param name="userInfo"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<XSubCategoryResourceDto> Referesh(
XSubCategoryResourceDto resource,
string connectionId = null,
XUserClaimsInfoDto userInfo = null,
CancellationToken cancellationToken = default
);
/// <summary>
/// Get All Resources ...
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<IEnumerable<XSubCategoryResourceDto>> GetAllResources(
CancellationToken cancellationToken = default
);
/// <summary>
/// Find One Resource ...
/// </summary>
/// <param name="predicate"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<XSubCategoryResourceDto> FindOne(
Expression<Func<XSubCategoryResourceDto, bool>> predicate = null,
CancellationToken cancellationToken = default
);
/// <summary>
/// Find Many Resource ...
/// </summary>
/// <param name="predicate"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<IEnumerable<XSubCategoryResourceDto>> FindMany(
Expression<Func<XSubCategoryResourceDto, bool>> predicate = null,
Func<IQueryable<XSubCategoryResourceDto>, IOrderedQueryable<XSubCategoryResourceDto>> orderBuilder = null,
CancellationToken cancellationToken = default
);
/// <summary>
/// Retrieve Resources by Query Pattern ...
/// </summary>
/// <param name="query"></param>
/// <param name="predicate"></param>
/// <param name="orderBuilder"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<XQueryResult<XSubCategoryResourceDto>> QueryResources(
XQuery query = null,
Expression<Func<XSubCategoryResourceDto, bool>> predicate = null,
Func<IQueryable<XSubCategoryResourceDto>, IOrderedQueryable<XSubCategoryResourceDto>> orderBuilder = null,
CancellationToken cancellationToken = default
);
/// <summary>
/// Get All Resources as Async Enumerable ...
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
IAsyncEnumerable<XSubCategoryResourceDto> GetAllResourcesAsAsyncEnumerable(
CancellationToken cancellationToken = default
);
}
}
+13
View File
@@ -0,0 +1,13 @@
using xCategoryService.Models.Dtos;
using xModels.Base;
using xModels.Dtos;
namespace xCategoryService.Providers.Dtos
{
public class XSubCategoryResourceDto : XBaseDto
{
public XSubCategoryDto Item { get; set; }
public XResourceDto Titles { get; set; }
public XResourceDto Descriptions { get; set; }
}
}
+494 -19
View File
@@ -1,4 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using xCategoryService.Interfaces;
@@ -11,8 +15,10 @@ using xCommons.Extensions;
using xExceptions.Constants;
using xIdentityModels.Models;
using xModels.Dtos;
using xDataService.Extensions;
using xPushService.Constants;
using xPushService.Providers;
using xDataService.Configuration;
namespace xCategoryService.Providers
{
@@ -20,12 +26,14 @@ namespace xCategoryService.Providers
{
private readonly IXCategoryServiceProvider provider;
private readonly XAppConfiguration appConfiguration;
private readonly XDataServiceConfiguration dataServiceConfiguration;
private readonly IXCategoryTitleResourceProvider titleResourceProvider;
private readonly IXCategoryDescriptionResourceProvider descriptionResourceProvider;
public XCategoryProvider(
XAppConfiguration appConfiguration,
IXCategoryServiceProvider provider,
XDataServiceConfiguration dataServiceConfiguration,
IXCategoryTitleResourceProvider titleResourceProvider,
IXCategoryDescriptionResourceProvider descriptionResourceProvider
) : base(
@@ -37,9 +45,12 @@ namespace xCategoryService.Providers
this.provider = provider;
this.appConfiguration = appConfiguration;
this.titleResourceProvider = titleResourceProvider;
this.dataServiceConfiguration = dataServiceConfiguration;
this.descriptionResourceProvider = descriptionResourceProvider;
}
//
#region Overrides ...
public override bool IsOwned(
XCategoryDto item,
XUserClaimsInfoDto userInfo
@@ -55,9 +66,12 @@ namespace xCategoryService.Providers
{
return !item.IsNullOrDefault();
}
#endregion
//
#region Actions ...
/// <summary>
/// Check a Category Has Specified Locale or not ...
/// Check Has Specified Locale or not ...
/// </summary>
/// <param name="id"></param>
/// <param name="language"></param>
@@ -91,9 +105,6 @@ namespace xCategoryService.Providers
var result =
!resource.IsNullOrDefault() &&
resource.Titles.Locales
.Any(x => x.Language
.Equals(language, System.StringComparison.InvariantCultureIgnoreCase)) &&
resource.Descriptions.Locales
.Any(x => x.Language
.Equals(language, System.StringComparison.InvariantCultureIgnoreCase));
@@ -113,7 +124,7 @@ namespace xCategoryService.Providers
)
{
//
var category = await Get(
var item = await Get(
id: id,
includeBuilder: null,
cancellationToken: cancellationToken
@@ -130,7 +141,7 @@ namespace xCategoryService.Providers
//
var result = new XCategoryResourceDto
{
Item = category,
Item = item,
Titles = titleResource,
Descriptions = descriptionResource
};
@@ -140,7 +151,7 @@ namespace xCategoryService.Providers
}
/// <summary>
/// Add Category Item for Specified Language ...
/// Add an Item for Specified Language ...
/// </summary>
/// <param name="item"></param>
/// <param name="langauge"></param>
@@ -171,6 +182,14 @@ namespace xCategoryService.Providers
XException.InvalidArgs.Throw();
}
//
// Check Permission ...
isValid = HasPermision(userInfo);
if (!isValid)
{
XException.NotAllowed.Throw();
}
//
var id = -1;
try
@@ -271,7 +290,7 @@ namespace xCategoryService.Providers
}
/// <summary>
/// Update a Category Item for Specified Language ...
/// Update an Item for Specified Language ...
/// </summary>
/// <param name="id"></param>
/// <param name="item"></param>
@@ -337,10 +356,10 @@ namespace xCategoryService.Providers
isValid =
!dto.IsNullOrDefault() &&
!userInfo.IsNullOrDefault() &&
IsOwned(
(IsOwned(
item: dto,
userInfo: userInfo
);
) || HasPermision(userInfo));
if (!isValid)
{
XException.NotAllowed.Throw();
@@ -364,7 +383,7 @@ namespace xCategoryService.Providers
.ForEach(tl =>
{
//
if (tl.Language.Equals(language, System.StringComparison.InvariantCultureIgnoreCase))
if (tl.Language.Equals(language, StringComparison.InvariantCultureIgnoreCase))
{
//
titleUpdated = true;
@@ -379,7 +398,7 @@ namespace xCategoryService.Providers
.ForEach(dl =>
{
//
if (dl.Language.Equals(language, System.StringComparison.InvariantCultureIgnoreCase))
if (dl.Language.Equals(language, StringComparison.InvariantCultureIgnoreCase))
{
//
descriptionUpdated = true;
@@ -396,7 +415,7 @@ namespace xCategoryService.Providers
}
//
resource = await RefereshResource(
resource = await Referesh(
resource: resource,
userInfo: userInfo,
connectionId: connectionId,
@@ -415,7 +434,7 @@ namespace xCategoryService.Providers
/// <param name="userInfo"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<XCategoryResourceDto> RefereshResource(
public async Task<XCategoryResourceDto> Referesh(
XCategoryResourceDto resource,
string connectionId = null,
XUserClaimsInfoDto userInfo = null,
@@ -425,20 +444,29 @@ namespace xCategoryService.Providers
//
// Validate Args ...
var isValid =
!resource.IsNull() &&
!resource.Item.IsNullOrDefault();
!resource.IsNull();
if (!isValid)
{
XException.InvalidArgs.Throw();
}
//
// Check Permission ...
isValid = HasPermision(userInfo);
if (!isValid)
{
XException.NotAllowed.Throw();
}
//
var id = -1;
XCategoryResourceDto result = null;
//
// Check Item Exists ...
XCategoryDto dto =
!resource.Item.Id.IsValidIntId()
(resource.Item.IsNullOrDefault() ||
!resource.Item.Id.IsValidIntId())
? null
: await Get(
id: resource.Item.Id,
@@ -451,20 +479,215 @@ namespace xCategoryService.Providers
// Do Referesh Based on Existance ...
var isNew =
!isExists &&
!resource.Item.Id.IsValidIntId();
(resource.Item.IsNullOrDefault() ||
!resource.Item.Id.IsValidIntId());
if (isNew)
{
//
// Add ...
// In this Senario, for Add a Category by Providing Resource
// the item must clean, either Title and Description, and tge Locales Provided by Resource
// ust Contains Values ...
// the Add Starts by Titles and only Items Add Which has Titles, since Title is Mandatory
// and Description is Optional ...
isValid =
//
// Resource ...
(resource.Item.IsNullOrDefault() ||
(!resource.Item.Id.IsValidIntId() &&
resource.Item.Title.IsNullOrEmpty() &&
resource.Item.Description.IsNullOrEmpty())) &&
//
// Titles ...
!resource.Titles.IsNullOrDefault() &&
resource.Titles.Locales.HasChild();
if (!isValid)
{
XException.InvalidArgs.Throw();
}
//
// Add Temp Category ...
var unique = Guid.NewGuid();
var temp = new XCategoryDto
{
Title = $"TmpT_{unique}",
Description = $"TmpD_{unique}",
};
temp = await provider.AddAsync(
item: temp,
saveChanges: true,
connectionId: null,
cancellationToken: cancellationToken
);
isValid =
!temp.IsNullOrDefault() &&
temp.Id.IsValidIntId();
if (!isValid)
{
XException.ActionFailed.Throw();
}
id = temp.Id;
//
// Update Title and Description ...
var titleResource = titleResourceProvider.GetResourceTitle(id);
var descriptionResource = descriptionResourceProvider.GetResourceTitle(id);
temp.Title = titleResource;
temp.Description = descriptionResource;
temp = await provider.UpdateAsync(
id: id,
item: temp,
saveChanges: true,
connectionId: null,
cancellationToken: cancellationToken
);
//
// Loop through Titles ...
foreach (var locale in resource.Titles.Locales)
{
//
// Add Or Update Locale ...
await titleResourceProvider.AddOrUpdateLocale(
locale: locale,
identifier: id,
connectionId: null,
cancellationToken: cancellationToken
);
var desLocale = resource.Descriptions.Locales.FirstOrDefault(
dl =>
dl.Language == locale.Language);
if (!desLocale.IsNullOrDefault())
{
//
// Add Descriptions ...
await descriptionResourceProvider.AddOrUpdateLocale(
identifier: id,
locale: locale,
connectionId: null,
cancellationToken: cancellationToken
);
}
}
}
else if (isExists)
{
//
// Re State ...
id = resource.Item.Id;
//
// Try to Refresh Resource State ...
//
#region Remove ...
//
// Titles ...
var removeTitles = result.Titles.Locales.Where(
l => !resource.Titles.Locales.Any(r => r.Language.Equals(l.Language, System.StringComparison.InvariantCultureIgnoreCase))
);
foreach (var locale in removeTitles)
{
//
await titleResourceProvider.RemoveLocale(
identifier: id,
connectionId: null,
language: locale.Language,
cancellationToken: cancellationToken
);
}
//
// Descriptions ...
var removeDescriptions = result.Descriptions.Locales.Where(
l => !resource.Descriptions.Locales.Any(r => r.Language.Equals(l.Language, System.StringComparison.InvariantCultureIgnoreCase))
);
foreach (var locale in removeDescriptions)
{
//
await descriptionResourceProvider.RemoveLocale(
identifier: id,
connectionId: null,
language: locale.Language,
cancellationToken: cancellationToken
);
}
#endregion
//
#region Update ...
//
// Titles ...
var updateTitles = result.Titles.Locales.Where(
l => resource.Titles.Locales.Any(r => r.Language.Equals(l.Language, System.StringComparison.InvariantCultureIgnoreCase))
);
foreach (var locale in updateTitles)
{
//
await titleResourceProvider.AddOrUpdateLocale(
identifier: id,
locale: locale,
connectionId: null,
cancellationToken: cancellationToken
);
}
//
// Descriptions ...
var updateDescriptions = result.Descriptions.Locales.Where(
l => resource.Descriptions.Locales.Any(r => r.Language.Equals(l.Language, System.StringComparison.InvariantCultureIgnoreCase))
);
foreach (var locale in updateDescriptions)
{
//
await descriptionResourceProvider.AddOrUpdateLocale(
identifier: id,
locale: locale,
connectionId: null,
cancellationToken: cancellationToken
);
}
#endregion
//
#region New ...
//
// Titles ...
var newTitles = resource.Titles.Locales.Where(
l => !result.Titles.Locales.Any(r => r.Language.Equals(l.Language, System.StringComparison.InvariantCultureIgnoreCase))
);
foreach (var locale in newTitles)
{
//
await titleResourceProvider.AddOrUpdateLocale(
identifier: id,
locale: locale,
connectionId: null,
cancellationToken: cancellationToken
);
}
//
// Descriptions ...
var newDescriptions = resource.Descriptions.Locales.Where(
l => !result.Descriptions.Locales.Any(r => r.Language.Equals(l.Language, System.StringComparison.InvariantCultureIgnoreCase))
);
foreach (var locale in newDescriptions)
{
//
await descriptionResourceProvider.AddOrUpdateLocale(
identifier: id,
locale: locale,
connectionId: null,
cancellationToken: cancellationToken
);
}
#endregion
}
//
// Validate Action ...
isValid = id.IsValidIntId();
if (!isValid)
{
@@ -473,13 +696,265 @@ namespace xCategoryService.Providers
//
// Get Result ...
var result = await GetResource(
result = await GetResource(
id: id,
cancellationToken: cancellationToken
);
isValid = !result.IsNullOrDefault();
//
// Send Notification ...
if (isValid)
{
//
await provider.SendPush(
connectionId: connectionId,
cancellationToken: cancellationToken,
payLoad: result.ToJSON(camelCase: true),
action: isNew
? XBaseEntityHubAction.Add.GetStringValue()
: XBaseEntityHubAction.Update.GetStringValue()
);
}
//
return result;
}
/// <summary>
/// Get All Resources as Async Enumerable ...
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async IAsyncEnumerable<XCategoryResourceDto> GetAllResourcesAsAsyncEnumerable(
[EnumeratorCancellation] CancellationToken cancellationToken = default
)
{
//
var enumerable = provider.GetAllAsAsyncEnumerable(
includeBuilder: null,
ignoreSoftDeleteds: true,
cancellationToken: cancellationToken,
orderBuilder: x => x.OrderBy(y => y.Id)
);
//
await foreach (var dto in enumerable)
{
//
if (cancellationToken.IsCancellationRequested)
{
yield break;
}
//
var resource = await GetResource(
id: dto.Id,
cancellationToken: cancellationToken
);
yield return resource;
}
}
/// <summary>
/// Get All Resources ...
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<IEnumerable<XCategoryResourceDto>> GetAllResources(
CancellationToken cancellationToken = default
)
{
//
var result = new List<XCategoryResourceDto>();
//
var enumerable = GetAllResourcesAsAsyncEnumerable(cancellationToken);
await foreach (var item in enumerable)
{
//
if (cancellationToken.IsCancellationRequested)
{
break;
}
//
result.Add(item);
}
//
return result;
}
/// <summary>
/// Find One Resource ...
/// </summary>
/// <param name="predicate"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<XCategoryResourceDto> FindOne(
Expression<Func<XCategoryResourceDto, bool>> predicate = null,
CancellationToken cancellationToken = default
)
{
//
var isApproved = false;
XCategoryResourceDto result = null;
var enumerable = GetAllResourcesAsAsyncEnumerable(cancellationToken);
await foreach (var item in enumerable)
{
//
// Check Cancellation Token ...
if (cancellationToken.IsCancellationRequested)
{
break;
}
//
// Detect Predicate Condition ...
isApproved = predicate.Compile()(item);
if (isApproved)
{
//
result = item;
break;
}
}
//
return result;
}
/// <summary>
/// Find Many Resource ...
/// </summary>
/// <param name="predicate"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<IEnumerable<XCategoryResourceDto>> FindMany(
Expression<Func<XCategoryResourceDto, bool>> predicate = null,
Func<IQueryable<XCategoryResourceDto>, IOrderedQueryable<XCategoryResourceDto>> orderBuilder = null,
CancellationToken cancellationToken = default
)
{
//
var isApproved = false;
var result = new List<XCategoryResourceDto>();
var enumerable = GetAllResourcesAsAsyncEnumerable(cancellationToken);
await foreach (var item in enumerable)
{
//
// Check Cancellation Token ...
if (cancellationToken.IsCancellationRequested)
{
break;
}
//
// Detect Predicate Condition ...
isApproved = predicate.Compile()(item);
if (isApproved)
{
result.Add(item);
}
}
//
// Apply Order ...
if (!orderBuilder.IsNull())
{
//
result =
orderBuilder(result.AsQueryable())
.ToList();
}
//
return result;
}
/// <summary>
/// Retrieve Resources by Query Pattern ...
/// </summary>
/// <param name="query"></param>
/// <param name="predicate"></param>
/// <param name="orderBuilder"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<XQueryResult<XCategoryResourceDto>> QueryResources(
XQuery query = null,
Expression<Func<XCategoryResourceDto, bool>> predicate = null,
Func<IQueryable<XCategoryResourceDto>, IOrderedQueryable<XCategoryResourceDto>> orderBuilder = null,
CancellationToken cancellationToken = default
)
{
//
// Normalize Query ...
query = query.NormalizeQuery(dataServiceConfiguration);
//
var items = await FindMany(
predicate: predicate,
orderBuilder: orderBuilder,
cancellationToken: cancellationToken
);
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<XCategoryResourceDto>
{
Page = query.Page,
Items = [.. items],
PageSize = query.PageSize,
TotalPages = totalPagesCount,
TotalItems = totalItemsCount,
TotalFilteredPages = filteredPagesCount,
TotalFilteredItems = filteredItemsCount
};
//
return result;
}
#endregion
}
}
+923 -2
View File
@@ -1,24 +1,55 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using xCategoryService.Interfaces;
using xCategoryService.Interfaces.Dtos;
using xCategoryService.Models.Dtos;
using xCategoryService.Models.Entities;
using xCategoryService.Providers.Dtos;
using xCommons.Configurations;
using xCommons.Extensions;
using xDataService.Configuration;
using xExceptions.Constants;
using xIdentityModels.Models;
using xModels.Dtos;
using xDataService.Extensions;
using xPushService.Constants;
using xPushService.Providers;
namespace xCategoryService.Providers
{
public class XSubCategoryProvider : XBaseProvided<XSubCategory, XSubCategoryDto, int, XSubCategoryDtoHub>, IXSubCategoryProvider
{
private readonly XAppConfiguration appConfiguration;
private readonly IXSubCategoryServiceProvider provider;
private readonly XDataServiceConfiguration dataServiceConfiguration;
private readonly IXSubCategoryTitleResourceProvider titleResourceProvider;
private readonly IXSubCategoryDescriptionResourceProvider descriptionResourceProvider;
public XSubCategoryProvider(
IXSubCategoryServiceProvider provider
XAppConfiguration appConfiguration,
IXSubCategoryServiceProvider provider,
XDataServiceConfiguration dataServiceConfiguration,
IXSubCategoryTitleResourceProvider titleResourceProvider,
IXSubCategoryDescriptionResourceProvider descriptionResourceProvider
) : base(
provider,
permittedRoles: new string[] { }
)
{ }
{
this.appConfiguration = appConfiguration;
this.provider = provider;
this.dataServiceConfiguration = dataServiceConfiguration;
this.titleResourceProvider = titleResourceProvider;
this.descriptionResourceProvider = descriptionResourceProvider;
}
//
#region Overrides ...
public override bool IsOwned(
XSubCategoryDto item,
XUserClaimsInfoDto userInfo
@@ -34,5 +65,895 @@ namespace xCategoryService.Providers
{
return !item.IsNullOrDefault();
}
#endregion
//
#region Actions ...
/// <summary>
/// Check Has Specified Locale or not ...
/// </summary>
/// <param name="id"></param>
/// <param name="language"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<bool> HasLocale(
int id,
string language,
CancellationToken cancellationToken = default
)
{
//
// Validate ...
var isValid =
id.IsValidIntId() &&
!language.IsNullOrEmpty();
if (isValid)
{
XException.InvalidArgs.Throw();
}
//
// Retrieve Resource ...
var resource = await GetResource(
id: id,
cancellationToken: cancellationToken
);
//
// Check Result ...
var result =
!resource.IsNullOrDefault() &&
resource.Titles.Locales
.Any(x => x.Language
.Equals(language, System.StringComparison.InvariantCultureIgnoreCase));
//
return result;
}
/// <summary>
/// Load an Item by all of it's Related Translations ...
/// </summary>
/// <param name="id"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<XSubCategoryResourceDto> GetResource(
int id,
CancellationToken cancellationToken = default
)
{
//
var item = await Get(
id: id,
includeBuilder: null,
cancellationToken: cancellationToken
);
var titleResource = await titleResourceProvider.GetResource(
identifier: id,
cancellationToken: cancellationToken
);
var descriptionResource = await titleResourceProvider.GetResource(
identifier: id,
cancellationToken: cancellationToken
);
//
var result = new XSubCategoryResourceDto
{
Item = item,
Titles = titleResource,
Descriptions = descriptionResource
};
//
return result;
}
/// <summary>
/// Add an Item for Specified Language ...
/// </summary>
/// <param name="item"></param>
/// <param name="langauge"></param>
/// <param name="connectionId"></param>
/// <param name="userInfo"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<XSubCategoryResourceDto> Add(
XSubCategoryDto item,
string langauge = null,
string connectionId = null,
XUserClaimsInfoDto userInfo = null,
CancellationToken cancellationToken = default
)
{
//
// Normalize ...
langauge =
!langauge.IsNullOrEmpty()
? langauge
: appConfiguration.DefaultLanguage;
//
// Validate ...
var isValid = !item.IsNullOrDefault();
if (!isValid)
{
XException.InvalidArgs.Throw();
}
//
// Check Permission ...
isValid = HasPermision(userInfo);
if (!isValid)
{
XException.NotAllowed.Throw();
}
//
var id = -1;
try
{
//
var title = item.Title;
var description = item.Description;
//
// Add Item Pure for ID Generating ...
item = await base.Add(
item: item,
userInfo: userInfo,
connectionId: null, // Since here We dont want notification ...
cancellationToken: cancellationToken
);
isValid = !item.IsNullOrDefault();
if (!isValid)
{
XException.ActionFailed.Throw();
}
//
id = item.Id;
var titleResource = await titleResourceProvider
.AddOrUpdateLocale(
identifier: id,
locale: new XLocaleResourceDto
{
Value = title,
Language = langauge
},
connectionId: connectionId,
cancellationToken: cancellationToken
);
var descriptionResource = await descriptionResourceProvider
.AddOrUpdateLocale(
identifier: id,
locale: new XLocaleResourceDto
{
Value = description,
Language = langauge
},
connectionId: connectionId,
cancellationToken: cancellationToken
);
//
// Update Category Dto ...
item.Title = titleResource.Resource;
item.Description = descriptionResource.Resource;
item = await base.Update(
id: id,
item: item,
userInfo: userInfo,
connectionId: null,
cancellationToken: cancellationToken
);
}
catch
{
XException.InvalidArgs.Throw();
}
//
// Validate Id ...
isValid = id.IsValidIntId();
if (!isValid)
{
XException.ActionFailed.Throw();
}
//
// Retrieve Category Resourv=ce ...
var result = await GetResource(
id: id,
cancellationToken: cancellationToken
);
//
// Sending Push Notification ...
isValid =
isValid &&
!result.IsNullOrDefault();
if (isValid)
{
//
await provider.SendPush(
connectionId: connectionId,
cancellationToken: cancellationToken,
payLoad: result.ToJSON(camelCase: true),
action: XBaseEntityHubAction.Add.GetStringValue()
);
}
//
return result;
}
/// <summary>
/// Update an Item for Specified Language ...
/// </summary>
/// <param name="id"></param>
/// <param name="item"></param>
/// <param name="language"></param>
/// <param name="connectionId"></param>
/// <param name="userInfo"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<XSubCategoryResourceDto> Update(
int id,
XSubCategoryDto item,
string language = null,
string connectionId = null,
XUserClaimsInfoDto userInfo = null,
CancellationToken cancellationToken = default
)
{
//
// Validate ...
var isValid =
id.IsValidIntId() &&
!item.IsNullOrDefault() &&
!language.IsNullOrEmpty() &&
!item.Title.IsNullOrEmpty();
if (!isValid)
{
XException.InvalidArgs.Throw();
}
//
// Check Category Exists ...
isValid = await IsExists(
id: id,
cancellationToken: cancellationToken
);
if (!isValid)
{
XException.NotFound.Throw();
}
//
// Check Locale Exists ...
isValid = await HasLocale(
id: id,
language: language,
cancellationToken: cancellationToken
);
if (!isValid)
{
XException.NotFound.Throw();
}
//
// Retrieve Category Dto for Validate Owining ...
var dto = await Get(
id: id,
includeBuilder: null,
cancellationToken: cancellationToken
);
//
// Check Permission ...
isValid =
!dto.IsNullOrDefault() &&
!userInfo.IsNullOrDefault() &&
(IsOwned(
item: dto,
userInfo: userInfo
) || HasPermision(userInfo));
if (!isValid)
{
XException.NotAllowed.Throw();
}
//
// Retrieve Exists Item Resource ...
var resource = await GetResource(
id: id,
cancellationToken: cancellationToken
);
//
var titleUpdated = false;
var descriptionUpdated = false;
//
// Update Title ...
resource.Titles.Locales
.ToList()
.ForEach(tl =>
{
//
if (tl.Language.Equals(language, StringComparison.InvariantCultureIgnoreCase))
{
//
titleUpdated = true;
tl.Value = item.Title;
}
});
//
// Update Description ...
resource.Descriptions.Locales
.ToList()
.ForEach(dl =>
{
//
if (dl.Language.Equals(language, StringComparison.InvariantCultureIgnoreCase))
{
//
descriptionUpdated = true;
dl.Value = item.Description;
}
});
//
// Validate Title and Description Updated ...
isValid = titleUpdated && descriptionUpdated;
if (!isValid)
{
XException.ActionFailed.Throw();
}
//
resource = await Referesh(
resource: resource,
userInfo: userInfo,
connectionId: connectionId,
cancellationToken: cancellationToken
);
//
return resource;
}
/// <summary>
/// Referesh a Resource ...
/// </summary>
/// <param name="resource"></param>
/// <param name="connectionId"></param>
/// <param name="userInfo"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<XSubCategoryResourceDto> Referesh(
XSubCategoryResourceDto resource,
string connectionId = null,
XUserClaimsInfoDto userInfo = null,
CancellationToken cancellationToken = default
)
{
//
// Validate Args ...
var isValid =
!resource.IsNull();
if (!isValid)
{
XException.InvalidArgs.Throw();
}
//
// Check Permission ...
isValid = HasPermision(userInfo);
if (!isValid)
{
XException.NotAllowed.Throw();
}
//
var id = -1;
XSubCategoryResourceDto result = null;
//
// Check Item Exists ...
XSubCategoryDto dto =
(resource.Item.IsNullOrDefault() ||
!resource.Item.Id.IsValidIntId())
? null
: await Get(
id: resource.Item.Id,
includeBuilder: null,
cancellationToken: cancellationToken
);
var isExists = !dto.IsNullOrDefault();
//
// Do Referesh Based on Existance ...
var isNew =
!isExists &&
(resource.Item.IsNullOrDefault() ||
!resource.Item.Id.IsValidIntId());
if (isNew)
{
//
// Add ...
// In this Senario, for Add a Category by Providing Resource
// the item must clean, either Title and Description, and tge Locales Provided by Resource
// ust Contains Values ...
// the Add Starts by Titles and only Items Add Which has Titles, since Title is Mandatory
// and Description is Optional ...
isValid =
//
// Resource ...
(resource.Item.IsNullOrDefault() ||
(!resource.Item.Id.IsValidIntId() &&
resource.Item.Title.IsNullOrEmpty() &&
resource.Item.Description.IsNullOrEmpty())) &&
//
// Titles ...
!resource.Titles.IsNullOrDefault() &&
resource.Titles.Locales.HasChild();
if (!isValid)
{
XException.InvalidArgs.Throw();
}
//
// Add Temp Category ...
var unique = Guid.NewGuid();
var temp = new XSubCategoryDto
{
Title = $"TmpT_{unique}",
Description = $"TmpD_{unique}",
};
temp = await provider.AddAsync(
item: temp,
saveChanges: true,
connectionId: null,
cancellationToken: cancellationToken
);
isValid =
!temp.IsNullOrDefault() &&
temp.Id.IsValidIntId();
if (!isValid)
{
XException.ActionFailed.Throw();
}
id = temp.Id;
//
// Update Title and Description ...
var titleResource = titleResourceProvider.GetResourceTitle(id);
var descriptionResource = descriptionResourceProvider.GetResourceTitle(id);
temp.Title = titleResource;
temp.Description = descriptionResource;
temp = await provider.UpdateAsync(
id: id,
item: temp,
saveChanges: true,
connectionId: null,
cancellationToken: cancellationToken
);
//
// Loop through Titles ...
foreach (var locale in resource.Titles.Locales)
{
//
// Add Or Update Locale ...
await titleResourceProvider.AddOrUpdateLocale(
locale: locale,
identifier: id,
connectionId: null,
cancellationToken: cancellationToken
);
var desLocale = resource.Descriptions.Locales.FirstOrDefault(
dl =>
dl.Language == locale.Language);
if (!desLocale.IsNullOrDefault())
{
//
// Add Descriptions ...
await descriptionResourceProvider.AddOrUpdateLocale(
identifier: id,
locale: locale,
connectionId: null,
cancellationToken: cancellationToken
);
}
}
}
else if (isExists)
{
//
// Re State ...
id = resource.Item.Id;
//
// Try to Refresh Resource State ...
//
#region Remove ...
//
// Titles ...
var removeTitles = result.Titles.Locales.Where(
l => !resource.Titles.Locales.Any(r => r.Language.Equals(l.Language, System.StringComparison.InvariantCultureIgnoreCase))
);
foreach (var locale in removeTitles)
{
//
await titleResourceProvider.RemoveLocale(
identifier: id,
connectionId: null,
language: locale.Language,
cancellationToken: cancellationToken
);
}
//
// Descriptions ...
var removeDescriptions = result.Descriptions.Locales.Where(
l => !resource.Descriptions.Locales.Any(r => r.Language.Equals(l.Language, System.StringComparison.InvariantCultureIgnoreCase))
);
foreach (var locale in removeDescriptions)
{
//
await descriptionResourceProvider.RemoveLocale(
identifier: id,
connectionId: null,
language: locale.Language,
cancellationToken: cancellationToken
);
}
#endregion
//
#region Update ...
//
// Titles ...
var updateTitles = result.Titles.Locales.Where(
l => resource.Titles.Locales.Any(r => r.Language.Equals(l.Language, System.StringComparison.InvariantCultureIgnoreCase))
);
foreach (var locale in updateTitles)
{
//
await titleResourceProvider.AddOrUpdateLocale(
identifier: id,
locale: locale,
connectionId: null,
cancellationToken: cancellationToken
);
}
//
// Descriptions ...
var updateDescriptions = result.Descriptions.Locales.Where(
l => resource.Descriptions.Locales.Any(r => r.Language.Equals(l.Language, System.StringComparison.InvariantCultureIgnoreCase))
);
foreach (var locale in updateDescriptions)
{
//
await descriptionResourceProvider.AddOrUpdateLocale(
identifier: id,
locale: locale,
connectionId: null,
cancellationToken: cancellationToken
);
}
#endregion
//
#region New ...
//
// Titles ...
var newTitles = resource.Titles.Locales.Where(
l => !result.Titles.Locales.Any(r => r.Language.Equals(l.Language, System.StringComparison.InvariantCultureIgnoreCase))
);
foreach (var locale in newTitles)
{
//
await titleResourceProvider.AddOrUpdateLocale(
identifier: id,
locale: locale,
connectionId: null,
cancellationToken: cancellationToken
);
}
//
// Descriptions ...
var newDescriptions = resource.Descriptions.Locales.Where(
l => !result.Descriptions.Locales.Any(r => r.Language.Equals(l.Language, System.StringComparison.InvariantCultureIgnoreCase))
);
foreach (var locale in newDescriptions)
{
//
await descriptionResourceProvider.AddOrUpdateLocale(
identifier: id,
locale: locale,
connectionId: null,
cancellationToken: cancellationToken
);
}
#endregion
}
//
// Validate Action ...
isValid = id.IsValidIntId();
if (!isValid)
{
XException.ActionFailed.Throw();
}
//
// Get Result ...
result = await GetResource(
id: id,
cancellationToken: cancellationToken
);
isValid = !result.IsNullOrDefault();
//
// Send Notification ...
if (isValid)
{
//
await provider.SendPush(
connectionId: connectionId,
cancellationToken: cancellationToken,
payLoad: result.ToJSON(camelCase: true),
action: isNew
? XBaseEntityHubAction.Add.GetStringValue()
: XBaseEntityHubAction.Update.GetStringValue()
);
}
//
return result;
}
/// <summary>
/// Get All Resources as Async Enumerable ...
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async IAsyncEnumerable<XSubCategoryResourceDto> GetAllResourcesAsAsyncEnumerable(
[EnumeratorCancellation] CancellationToken cancellationToken = default
)
{
//
var enumerable = provider.GetAllAsAsyncEnumerable(
includeBuilder: null,
ignoreSoftDeleteds: true,
cancellationToken: cancellationToken,
orderBuilder: x => x.OrderBy(y => y.Id)
);
//
await foreach (var dto in enumerable)
{
//
if (cancellationToken.IsCancellationRequested)
{
yield break;
}
//
var resource = await GetResource(
id: dto.Id,
cancellationToken: cancellationToken
);
yield return resource;
}
}
/// <summary>
/// Get All Resources ...
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<IEnumerable<XSubCategoryResourceDto>> GetAllResources(
CancellationToken cancellationToken = default
)
{
//
var result = new List<XSubCategoryResourceDto>();
//
var enumerable = GetAllResourcesAsAsyncEnumerable(cancellationToken);
await foreach (var item in enumerable)
{
//
if (cancellationToken.IsCancellationRequested)
{
break;
}
//
result.Add(item);
}
//
return result;
}
/// <summary>
/// Find One Resource ...
/// </summary>
/// <param name="predicate"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<XSubCategoryResourceDto> FindOne(
Expression<Func<XSubCategoryResourceDto, bool>> predicate = null,
CancellationToken cancellationToken = default
)
{
//
var isApproved = false;
XSubCategoryResourceDto result = null;
var enumerable = GetAllResourcesAsAsyncEnumerable(cancellationToken);
await foreach (var item in enumerable)
{
//
// Check Cancellation Token ...
if (cancellationToken.IsCancellationRequested)
{
break;
}
//
// Detect Predicate Condition ...
isApproved = predicate.Compile()(item);
if (isApproved)
{
//
result = item;
break;
}
}
//
return result;
}
/// <summary>
/// Find Many Resource ...
/// </summary>
/// <param name="predicate"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<IEnumerable<XSubCategoryResourceDto>> FindMany(
Expression<Func<XSubCategoryResourceDto, bool>> predicate = null,
Func<IQueryable<XSubCategoryResourceDto>, IOrderedQueryable<XSubCategoryResourceDto>> orderBuilder = null,
CancellationToken cancellationToken = default
)
{
//
var isApproved = false;
var result = new List<XSubCategoryResourceDto>();
var enumerable = GetAllResourcesAsAsyncEnumerable(cancellationToken);
await foreach (var item in enumerable)
{
//
// Check Cancellation Token ...
if (cancellationToken.IsCancellationRequested)
{
break;
}
//
// Detect Predicate Condition ...
isApproved = predicate.Compile()(item);
if (isApproved)
{
result.Add(item);
}
}
//
// Apply Order ...
if (!orderBuilder.IsNull())
{
//
result =
orderBuilder(result.AsQueryable())
.ToList();
}
//
return result;
}
/// <summary>
/// Retrieve Resources by Query Pattern ...
/// </summary>
/// <param name="query"></param>
/// <param name="predicate"></param>
/// <param name="orderBuilder"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<XQueryResult<XSubCategoryResourceDto>> QueryResources(
XQuery query = null,
Expression<Func<XSubCategoryResourceDto, bool>> predicate = null,
Func<IQueryable<XSubCategoryResourceDto>, IOrderedQueryable<XSubCategoryResourceDto>> orderBuilder = null,
CancellationToken cancellationToken = default
)
{
//
// Normalize Query ...
query = query.NormalizeQuery(dataServiceConfiguration);
//
var items = await FindMany(
predicate: predicate,
orderBuilder: orderBuilder,
cancellationToken: cancellationToken
);
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<XSubCategoryResourceDto>
{
Page = query.Page,
Items = [.. items],
PageSize = query.PageSize,
TotalPages = totalPagesCount,
TotalItems = totalItemsCount,
TotalFilteredPages = filteredPagesCount,
TotalFilteredItems = filteredItemsCount
};
//
return result;
}
#endregion
}
}
+4 -2
View File
@@ -2,11 +2,13 @@
<!-- Runtime Definition -->
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<PackageId>xDashboard.xCategoryService</PackageId>
<Version>1.0.0</Version>
<LangVersion>12.0</LangVersion>
<Authors>Hadi Khazaee Asl</Authors>
<Company>SaherElm IT Center</Company>
<TargetFramework>netstandard2.0</TargetFramework>
<PackageId>xDashboard.xCategoryService</PackageId>
<Description>
it is a Part of xDashboard on SaherElm IT Center which provides Category Futures ...
</Description>