521 lines
17 KiB
C#
521 lines
17 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> ();
|
|
|
|
//
|
|
#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);
|
|
|
|
//
|
|
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));
|
|
|
|
//
|
|
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);
|
|
|
|
//
|
|
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 {
|
|
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 })
|
|
);
|
|
}
|
|
|
|
private void RemoveItemsFromStore (IEnumerable<T> items) {
|
|
STORE = new ConcurrentBag<T> (
|
|
STORE.Except (items)
|
|
);
|
|
}
|
|
#endregion
|
|
}
|
|
} |