add XBaseInMemoryRepository for Entitiy Supports ...
This commit is contained in:
@@ -0,0 +1,486 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user