Files
xDataService/InMemRepositories/XBaseInMemoryRepositoy.cs
T

486 lines
14 KiB
C#

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using xCommons.Extensions;
using xDataService.Configuration;
using xDataService.Extensions;
using xDataService.Interfaces;
using xModels.Base;
using xModels.Dtos;
namespace xDataService.InMemRepositories
{
public abstract class XBaseInMemoryRepositoy<TEntity, TKey> : IXBaseRepository<TEntity, TKey>
where TEntity : XBaseEntity<TKey>
{
//
#region Props ...
private readonly XDataServiceConfiguration configuration;
private readonly IXKeyGenerator<TEntity, TKey> keyGenerator;
private readonly IXBaseRepositoryEvents<TEntity> baseRepositoryEvents;
private static readonly ConcurrentDictionary<TKey, TEntity> store = new ConcurrentDictionary<TKey, TEntity>();
#endregion
//
#region Constructor ...
public XBaseInMemoryRepositoy(
XDataServiceConfiguration configuration,
IXKeyGenerator<TEntity, TKey> keyGenerator = null,
IXBaseRepositoryEvents<TEntity> baseRepositoryEvents = null
)
{
this.configuration = configuration;
this.keyGenerator = keyGenerator;
this.baseRepositoryEvents = baseRepositoryEvents;
}
#endregion
//
#region Add ...
public async Task<TEntity> AddAsync(TEntity item, bool saveChanges = true)
{
//
// Handle Key ...
item = await HandleKeyAsync(item);
//
// Add Item to Dictionary ...
store[item.Id] = item;
//
// Return Item ...
return item;
}
public async Task<TEntity> AddOrUpdateAsync(TEntity item, bool saveChanges = true)
{
//
var isExists = await IsExistsAsync(item.Id);
if (isExists)
{
item = await UpdateAsync(item.Id, item);
}
else
{
item = await AddAsync(item);
}
//
return item;
}
public async Task AddRangeAsync(IEnumerable<TEntity> items, bool saveChanges = true)
{
//
foreach (var item in items)
{
var handled = await HandleKeyAsync(item);
store[handled.Id] = handled;
}
}
#endregion
//
#region Remove ...
public async Task<TEntity> 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<TEntity> RemoveAsync(TEntity item, bool softDelete = true, bool saveChanges = true)
{
return await RemoveAsync(item.Id);
}
public async Task RemoveRangeAsync(IEnumerable<TEntity> items, bool softDelete = true, bool saveChanges = true)
{
//
var isValid = !items.IsNull() && items.HasChild();
if (!isValid)
{
return;
}
//
foreach (var item in items)
{
await RemoveAsync(item);
}
}
#endregion
//
#region Retrieve ...
public IQueryable<TEntity> AsQueryable()
{
return store.Values.AsQueryable();
}
public async Task<TEntity> GetAsync(TKey id, bool ignoreSoftDeleteds = true, bool containsDetail = false)
{
//
var isExists = await IsExistsAsync(id);
if (!isExists)
{
return null;
}
//
return store[id];
}
public async Task<IEnumerable<TEntity>> GetAllAsync(bool ignoreSoftDeleteds = true, bool containsDetail = false)
{
//
var result = store.Values.AsEnumerable();
return await Task.FromResult(result);
}
public async Task<TEntity> FindOneAsync(Expression<Func<TEntity, bool>> whereClause, bool ignoreSoftDeleteds = true, bool containsDetail = false)
{
//
var func = whereClause.Compile();
var result = store.Values.FirstOrDefault(x => func(x));
return await Task.FromResult(result);
}
public async Task<IEnumerable<TEntity>> FindManyAsync(Expression<Func<TEntity, bool>> whereClause, bool ignoreSoftDeleteds = true, bool containsDetail = false)
{
//
var func = whereClause.Compile();
var result = store.Values.Where(x => func(x));
return await Task.FromResult(result);
}
public async Task<XQueryResult<TEntity>> QueryAsync(XQuery query, bool ignoreSoftDeleteds = true)
{
//
// 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<TEntity>
{
Page = query.Page,
Items = items.ToList(),
PageSize = query.PageSize,
TotalPages = totalPagesCount,
TotalItems = totalItemsCount,
TotalFilteredPages = filteredPagesCount,
TotalFilteredItems = filteredItemsCount
};
//
return result;
}
public async Task<XQueryResult<TEntity>> ConditionalQueryAsync(Expression<Func<TEntity, bool>> whereClause, XQuery query, bool ignoreSoftDeleteds = true)
{
//
// 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<TEntity>
{
Page = query.Page,
Items = items.ToList(),
PageSize = query.PageSize,
TotalPages = totalPagesCount,
TotalItems = totalItemsCount,
TotalFilteredPages = filteredPagesCount,
TotalFilteredItems = filteredItemsCount
};
//
return result;
}
#endregion
//
#region Update ...
public async Task<TEntity> 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<string> { nameof(exists.Id) });
store[id] = exists;
//
return exists;
}
public async Task<bool> UpdateRangeAsync(IEnumerable<TEntity> 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<int> CountAsync(bool ignoreSoftDeleteds = true)
{
//
var result = store.Count();
return await Task.FromResult(result);
}
public Task<int> PagesCountAsync(int pageSize, int? totalItems = null)
{
return Task.FromResult(0);
}
#endregion
//
#region Exists ...
public async Task<bool> 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<int> 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);
}
public TKey GetKey(TEntity 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<TKey>();
}
public async Task<TEntity> HandleKeyAsync(TEntity item)
{
//
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 ...
public void Detach(TEntity item)
{ }
public void Detach(IEnumerable<TEntity> items)
{ }
public void Detach(XQueryResult<TEntity> query)
{ }
public void Detach(XPageResponse<TEntity> page)
{ }
#endregion
//
#region Others ...
public void Dispose()
{ }
#endregion
}
}