Files
xModels/Providers/XBaseInMemoryStore.cs

676 lines
18 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 xModels.Base;
using xModels.Interfaces;
namespace xModels.Providers
{
public abstract class XBaseInMemoryStore<T, TKey> : IXBaseStore<T, TKey>
where T : XBaseStorableDto<TKey>
{
/// <summary>
/// this is main store of items ...
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
private static ConcurrentBag<T> STORE = new ConcurrentBag<T>();
//
private readonly IXBaseStoreEvents<T, TKey> events;
//
#region Constructor ...
protected XBaseInMemoryStore(
IXBaseStoreEvents<T, TKey> events = null
)
{
this.events = events;
}
#endregion
//
#region Retrieve ...
/// <summary>
/// Retrieve all Exists Items ...
/// </summary>
/// <returns></returns>
public Task<IEnumerable<T>> GetAll()
{
//
var result = STORE.AsEnumerable();
return Task.FromResult(result);
}
/// <summary>
/// Retrieve specific Item by key ...
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public Task<T> Get(TKey key)
{
//
return IsExistsByKey(key)
.ContinueWith(isExistsTask =>
{
//
var isExists = isExistsTask
.RunTask();
//
if (!isExists)
{
return null;
}
//
try
{
var result = STORE
.FirstOrDefault(i => GetKey(i).Equals(key));
return result;
}
catch
{
return null;
}
});
}
/// <summary>
/// retrieve items based on specific condition ...
/// </summary>
/// <param name="condition"></param>
/// <returns></returns>
public Task<IEnumerable<T>> FindMany(Expression<Func<T, bool>> condition)
{
//
// Validate Args ...
if (condition.IsNull())
{
return Task.FromResult(new List<T>().AsEnumerable());
}
//
var whereFunc = condition.Compile();
var result = STORE.Where(whereFunc);
//
return Task.FromResult(result);
}
/// <summary>
/// retrieve item based on specific condition ...
/// </summary>
/// <param name="condition"></param>
/// <returns></returns>
public Task<T> FindOne(Expression<Func<T, bool>> condition)
{
//
// Validate Args ...
if (condition.IsNull())
{
return null;
}
//
var whereFunc = condition.Compile();
var result = STORE.FirstOrDefault(whereFunc);
//
return Task.FromResult(result);
}
#endregion
//
#region Add ...
/// <summary>
/// add specific item to Store ...
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public Task<bool> Add(T item)
{
//
// Validate Args ...
if (item.IsNull() || GetKey(item).IsNull())
{
return Task.FromResult(false);
}
//
// Try to add item ...
return IsExistsByKey(GetKey(item))
.ContinueWith(isExistsTask =>
{
//
// Check item exists in Store or not ...
var isExists = isExistsTask.RunTask();
if (isExists)
{
return false;
}
//
// Add Item to Store ...
STORE.Add(item);
OnAdd(item);
//
return true;
});
}
/// <summary>
/// Add a Collection of Items on Store ...
/// </summary>
/// <param name="items"></param>
/// <returns></returns>
public Task<bool> AddMany(XBaseRangeRequest<T> items)
{
//
// Validate Args ...
if (
items.IsNull() ||
!items.Items.HasChild()
)
{
return Task.FromResult(false);
}
//
// Retrieve Must Add Items ...
var mustAddItems = items.Items
.Where(i => !GetKey(i)
.IsNull() && !STORE
.Any(si => GetKey(i).Equals(GetKey(si)))
);
if (!mustAddItems.HasChild())
{
return Task.FromResult(false);
}
//
// Add Items ...
try
{
//
mustAddItems
.ToList()
.ForEach(mi => STORE.Add(mi));
//
OnAddMany(mustAddItems);
//
return Task.FromResult(true);
}
catch
{
return Task.FromResult(false);
}
}
#endregion
//
#region Remove ...
/// <summary>
/// Remove an item from store ...
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public Task<bool> Remove(T item)
{
//
// Validate Args ...
if (
item.IsNull() ||
!STORE
.Any(i => GetKey(i)
.Equals(GetKey(item)))
)
{
return Task.FromResult(false);
}
//
try
{
//
RemoveItemFromStore(item);
return Task.FromResult(true);
}
catch
{
return Task.FromResult(false);
}
}
/// <summary>
/// remove an item by it's Key ...
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public Task<bool> RemoveByKey(TKey key)
{
return Get(key)
.ContinueWith(getTask =>
{
//
// retrieve item by it's Key ...
var item = getTask
.RunTask();
//
// Check item exists ...
if (item.IsNull())
{
return false;
}
//
// Try to remove item ...
var result = false;
try
{
//
result = Remove(item)
.RunTask();
}
catch
{
result = false;
}
//
return result;
});
}
/// <summary>
/// remove a collection of items at once ...
/// </summary>
/// <param name="items"></param>
/// <returns></returns>
public Task<bool> RemoveMany(XBaseRangeRequest<T> items)
{
//
// Validate Args ...
if (
items.IsNull() ||
!items.Items.HasChild()
)
{
return Task.FromResult(false);
}
//
var mustRemovedItems = items.Items
.Where(i => STORE
.Any(si => GetKey(si).Equals(GetKey(i)))
);
if (!mustRemovedItems.HasChild())
{
return Task.FromResult(false);
}
//
try
{
//
RemoveItemsFromStore(mustRemovedItems);
return Task.FromResult(true);
}
catch
{
return Task.FromResult(false);
}
}
#endregion
//
#region Update ...
/// <summary>
/// Update Exists Item in store ...
/// </summary>
/// <param name="item"></param>
/// <param name="propertyWhiteList"></param>
/// <param name="propertyBlackList"></param>
/// <param name="propertyValueProviders"></param>
/// <param name="updateWithNullOrEmptyValues"></param>
/// <returns></returns>
public Task<T> Update(
T item,
ICollection<string> propertyWhiteList = null,
ICollection<string> propertyBlackList = null,
ICollection<KeyValuePair<string, Func<T, object>>> propertyValueProviders = null,
bool updateWithNullOrEmptyValues = false
)
{
return Get(GetKey(item))
.ContinueWith(getTask =>
{
//
var existsItem = getTask
.RunTask();
//
// Validate item Exists ...
if (existsItem.IsNull())
{
return null;
}
//
try
{
//
var updatedItem = existsItem;
updatedItem.UpdateData(
updateWith: item,
propertyWhiteList: propertyWhiteList,
propertyBlackList: propertyBlackList,
propertyValueProviders: propertyValueProviders,
updateWithNullOrEmptyValues: updateWithNullOrEmptyValues,
throwExceptionOnFails: true
);
//
RemoveItemFromStore(existsItem);
STORE.Add(updatedItem);
OnUpdate(item);
//
return updatedItem;
}
catch
{
return null;
}
});
}
/// <summary>
/// Update a Collection of Exists Items ...
/// </summary>
/// <param name="items"></param>
/// <param name="propertyWhiteList"></param>
/// <param name="propertyBlackList"></param>
/// <param name="propertyValueProviders"></param>
/// <param name="updateWithNullOrEmptyValues"></param>
/// <returns></returns>
public Task<bool> UpdateMany(
XBaseRangeRequest<T> items,
ICollection<string> propertyWhiteList = null,
ICollection<string> propertyBlackList = null,
ICollection<KeyValuePair<string, Func<T, object>>> propertyValueProviders = null,
bool updateWithNullOrEmptyValues = false
)
{
//
// Validate Args ...
if (
items.IsNull() ||
!items.Items.HasChild()
)
{
return Task.FromResult(false);
}
//
// retrieve must updated items ...
var mustUpdateItems = items
.Items
.Where(i => STORE
.Any(
si => GetKey(i).Equals(GetKey(si)))
);
if (!mustUpdateItems.HasChild())
{
return Task.FromResult(false);
}
//
// Do Tasks ...
var mustUpdateTasks = mustUpdateItems.Select(i => Update(
i,
propertyWhiteList: propertyWhiteList,
propertyBlackList: propertyBlackList,
propertyValueProviders: propertyValueProviders,
updateWithNullOrEmptyValues: updateWithNullOrEmptyValues
));
return Task.WhenAll(mustUpdateTasks)
.ContinueWith(updateTasks =>
{
//
var updatedItems = updateTasks
.RunTask();
//
if (!updatedItems.HasChild())
{
return false;
}
else
{
//
OnUpdateMany(mustUpdateItems);
return true;
}
});
}
#endregion
//
#region Exists ...
/// <summary>
/// retrieve item(s) exists based on specific condition ...
/// </summary>
/// <param name="condition"></param>
/// <returns></returns>
public Task<bool> IsExists(Expression<Func<T, bool>> condition)
{
//
// Validate Args ...
if (condition.IsNull())
{
return Task.FromResult(false);
}
//
var whereFunc = condition.Compile();
var result = STORE.Any(whereFunc);
//
return Task.FromResult(result);
}
/// <summary>
/// is exists an item by providing it's key ...
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public Task<bool> IsExistsByKey(TKey key)
{
return Task.Run(() =>
{
return STORE.Any(i => GetKey(i).Equals(key));
});
}
#endregion
//
#region Count ...
/// <summary>
/// Count Exists items ...
/// </summary>
/// <returns></returns>
public Task<int> Count()
{
//
var result = STORE.Count();
return Task.FromResult(result);
}
#endregion
//
#region Keys ...
/// <summary>
/// set Object Key ...
/// </summary>
/// <param name="item"></param>
/// <param name="id"></param>
public void SetKey(
ref T 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);
}
/// <summary>
/// Get Object Key ...
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public TKey GetKey(T 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;
}
//
TKey result;
try
{
result = keyString.FromJSON<TKey>();
}
catch
{
result = keyString.ConvertTo<TKey>();
}
return result;
}
#endregion
//
#region Private ...
private void RemoveItemFromStore(T item)
{
//
STORE = new ConcurrentBag<T>(
STORE.Except(new[] { item })
);
//
OnRemove(item);
}
private void RemoveItemsFromStore(IEnumerable<T> items)
{
//
STORE = new ConcurrentBag<T>(
STORE.Except(items)
);
//
OnRemoveMany(items);
}
//
#region Event Notifiers ...
private void OnAdd(T item)
{
//
if (!events.IsNull())
{
events.AddEvent(new XBaseStorableDtoEventModel<T>(item));
}
}
private void OnUpdate(T item)
{
//
if (!events.IsNull())
{
events.UpdateEvent(new XBaseStorableDtoEventModel<T>(item));
}
}
private void OnRemove(T item)
{
//
if (!events.IsNull())
{
events.RemoveEvent(new XBaseStorableDtoEventModel<T>(item));
}
}
private void OnAddMany(IEnumerable<T> items)
{
//
if (!events.IsNull())
{
events.AddManyEvent(new XBaseStorableDtoEventModel<IEnumerable<T>>(items));
}
}
private void OnUpdateMany(IEnumerable<T> items)
{
//
if (!events.IsNull())
{
events.UpdateManyEvent(new XBaseStorableDtoEventModel<IEnumerable<T>>(items));
}
}
private void OnRemoveMany(IEnumerable<T> items)
{
//
if (!events.IsNull())
{
events.RemoveManyEvent(new XBaseStorableDtoEventModel<IEnumerable<T>>(items));
}
}
#endregion
#endregion
}
}