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