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 : IXBaseRepository where TEntity : XBaseEntity { // #region Props ... private readonly XDataServiceConfiguration configuration; private readonly IXKeyGenerator keyGenerator; private readonly IXBaseRepositoryEvents baseRepositoryEvents; private static readonly ConcurrentDictionary store = new ConcurrentDictionary(); #endregion // #region Constructor ... public XBaseInMemoryRepositoy( XDataServiceConfiguration configuration, IXKeyGenerator keyGenerator = null, IXBaseRepositoryEvents baseRepositoryEvents = null ) { this.configuration = configuration; this.keyGenerator = keyGenerator; this.baseRepositoryEvents = baseRepositoryEvents; } #endregion // #region Add ... public async Task 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 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 items, bool saveChanges = true) { // foreach (var item in items) { var handled = await HandleKeyAsync(item); store[handled.Id] = handled; } } #endregion // #region Remove ... public async Task 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 RemoveAsync(TEntity item, bool softDelete = true, bool saveChanges = true) { return await RemoveAsync(item.Id); } public async Task RemoveRangeAsync(IEnumerable 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 AsQueryable() { return store.Values.AsQueryable(); } public async Task GetAsync(TKey id, bool ignoreSoftDeleteds = true, bool containsDetail = false) { // var isExists = await IsExistsAsync(id); if (!isExists) { return null; } // return store[id]; } public async Task> GetAllAsync(bool ignoreSoftDeleteds = true, bool containsDetail = false) { // var result = store.Values.AsEnumerable(); return await Task.FromResult(result); } public async Task FindOneAsync(Expression> 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> FindManyAsync(Expression> 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> 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 { Page = query.Page, Items = items.ToList(), PageSize = query.PageSize, TotalPages = totalPagesCount, TotalItems = totalItemsCount, TotalFilteredPages = filteredPagesCount, TotalFilteredItems = filteredItemsCount }; // return result; } public async Task> ConditionalQueryAsync(Expression> 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 { 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 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 { nameof(exists.Id) }); store[id] = exists; // return exists; } public async Task UpdateRangeAsync(IEnumerable 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 CountAsync(bool ignoreSoftDeleteds = true) { // var result = store.Count(); return await Task.FromResult(result); } public Task PagesCountAsync(int pageSize, int? totalItems = null) { return Task.FromResult(0); } #endregion // #region Exists ... public async Task 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 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(); } public async Task 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 items) { } public void Detach(XQueryResult query) { } public void Detach(XPageResponse page) { } #endregion // #region Others ... public void Dispose() { } #endregion } }