diff --git a/Base/XBaseStorableDtoEventModel.cs b/Base/XBaseStorableDtoEventModel.cs new file mode 100644 index 0000000..9c8aad3 --- /dev/null +++ b/Base/XBaseStorableDtoEventModel.cs @@ -0,0 +1,12 @@ +namespace xModels.Base +{ + public class XBaseStorableDtoEventModel + { + public T Model { get; set; } + + public XBaseStorableDtoEventModel(T model) + { + Model = model; + } + } +} \ No newline at end of file diff --git a/Base/XBaseStoreEvent.cs b/Base/XBaseStoreEvent.cs new file mode 100644 index 0000000..07981e0 --- /dev/null +++ b/Base/XBaseStoreEvent.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using xModels.Interfaces; + +namespace xModels.Base +{ + public abstract class XBaseStoreEvent : IXBaseStoreEvents + where T : XBaseStorableDto + { + // + private readonly ISubject> onAddSubject; + private readonly ISubject> onUpdateSubject; + private readonly ISubject> onRemoveSubject; + + // + private readonly ISubject>> onAddManySubject; + private readonly ISubject>> onUpdateManySubject; + private readonly ISubject>> onRemoveManySubject; + + // + public IObservable> OnAddObservable => onAddSubject.AsObservable(); + + public IObservable> OnUpdateObservable => onUpdateSubject.AsObservable(); + + public IObservable> OnRemoveObservable => onRemoveSubject.AsObservable(); + + // + + public IObservable>> OnAddManyObservable => onAddManySubject.AsObservable(); + + public IObservable>> OnUpdateManyObservable => onUpdateManySubject.AsObservable(); + + public IObservable>> OnRemoveManyObservable => onRemoveManySubject.AsObservable(); + + // + protected XBaseStoreEvent() + { + // + onAddSubject = new ReplaySubject>(1); + onUpdateSubject = new ReplaySubject>(1); + onRemoveSubject = new ReplaySubject>(1); + + // + onAddManySubject = new ReplaySubject>>(1); + onUpdateManySubject = new ReplaySubject>>(1); + onRemoveManySubject = new ReplaySubject>>(1); + } + + // + #region Base Actions ... + public void AddEvent(XBaseStorableDtoEventModel model) + { + onAddSubject.OnNext(model); + } + + public void AddManyEvent(XBaseStorableDtoEventModel> model) + { + onAddManySubject.OnNext(model); + } + + public void RemoveEvent(XBaseStorableDtoEventModel model) + { + onRemoveSubject.OnNext(model); + } + + public void RemoveManyEvent(XBaseStorableDtoEventModel> model) + { + onRemoveManySubject.OnNext(model); + } + + public void UpdateEvent(XBaseStorableDtoEventModel model) + { + onUpdateSubject.OnNext(model); + } + + public void UpdateManyEvent(XBaseStorableDtoEventModel> model) + { + onUpdateManySubject.OnNext(model); + } + #endregion + } +} \ No newline at end of file diff --git a/Interfaces/IXBaseStoreEvents.cs b/Interfaces/IXBaseStoreEvents.cs new file mode 100644 index 0000000..401464f --- /dev/null +++ b/Interfaces/IXBaseStoreEvents.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using xModels.Base; + +namespace xModels.Interfaces +{ + public interface IXBaseStoreEvents where T : XBaseStorableDto + { + // + void AddEvent(XBaseStorableDtoEventModel model); + void UpdateEvent(XBaseStorableDtoEventModel model); + void RemoveEvent(XBaseStorableDtoEventModel model); + + // + void AddManyEvent(XBaseStorableDtoEventModel> model); + void UpdateManyEvent(XBaseStorableDtoEventModel> model); + void RemoveManyEvent(XBaseStorableDtoEventModel> model); + + // + IObservable> OnAddObservable { get; } + IObservable> OnUpdateObservable { get; } + IObservable> OnRemoveObservable { get; } + + // + IObservable>> OnAddManyObservable { get; } + IObservable>> OnUpdateManyObservable { get; } + IObservable>> OnRemoveManyObservable { get; } + + } +} \ No newline at end of file diff --git a/Providers/XBaseInMemoryStore.cs b/Providers/XBaseInMemoryStore.cs index 5f1d250..9911f21 100644 --- a/Providers/XBaseInMemoryStore.cs +++ b/Providers/XBaseInMemoryStore.cs @@ -8,514 +8,669 @@ using xCommons.Extensions; using xModels.Base; using xModels.Interfaces; -namespace xModels.Providers { +namespace xModels.Providers +{ public abstract class XBaseInMemoryStore : IXBaseStore - where T : XBaseStorableDto { - /// - /// this is main store of items ... - /// - /// - /// - private static ConcurrentBag STORE = new ConcurrentBag (); + where T : XBaseStorableDto + { + /// + /// this is main store of items ... + /// + /// + /// + private static ConcurrentBag STORE = new ConcurrentBag(); - // - #region Retrieve ... - /// - /// Retrieve all Exists Items ... - /// - /// - public Task> GetAll () { - // - var result = STORE.AsEnumerable (); - return Task.FromResult (result); - } + // + private readonly IXBaseStoreEvents events; - /// - /// Retrieve specific Item by key ... - /// - /// - /// - public Task 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; - } - }); - } - - /// - /// retrieve items based on specific condition ... - /// - /// - /// - public Task> FindMany (Expression> condition) { - // - // Validate Args ... - if (condition.IsNull ()) { - return Task.FromResult (new List ().AsEnumerable ()); - } - - // - var whereFunc = condition.Compile (); - var result = STORE.Where (whereFunc); - - // - return Task.FromResult (result); - } - - /// - /// retrieve item based on specific condition ... - /// - /// - /// - public Task FindOne (Expression> condition) { - // - // Validate Args ... - if (condition.IsNull ()) { - return null; - } - - // - var whereFunc = condition.Compile (); - var result = STORE.FirstOrDefault (whereFunc); - - // - return Task.FromResult (result); - } - #endregion - - // - #region Add ... - /// - /// add specific item to Store ... - /// - /// - /// - public Task 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; - }); - } - - /// - /// Add a Collection of Items on Store ... - /// - /// - /// - public Task AddMany (XBaseRangeRequest 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 ... - /// - /// Remove an item from store ... - /// - /// - /// - public Task 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); - } - } - - /// - /// remove an item by it's Key ... - /// - /// - /// - public Task 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; - }); - } - - /// - /// remove a collection of items at once ... - /// - /// - /// - public Task RemoveMany (XBaseRangeRequest 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 ... - /// - /// Update Exists Item in store ... - /// - /// - /// - /// - /// - /// - /// - public Task Update ( - T item, - ICollection propertyWhiteList = null, - ICollection propertyBlackList = null, - ICollection>> 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; - } - }); - } - - /// - /// Update a Collection of Exists Items ... - /// - /// - /// - /// - /// - /// - /// - public Task UpdateMany ( - XBaseRangeRequest items, - ICollection propertyWhiteList = null, - ICollection propertyBlackList = null, - ICollection>> 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 ... - /// - /// retrieve item(s) exists based on specific condition ... - /// - /// - /// - public Task IsExists (Expression> condition) { - // - // Validate Args ... - if (condition.IsNull ()) { - return Task.FromResult (false); - } - - // - var whereFunc = condition.Compile (); - var result = STORE.Any (whereFunc); - - // - return Task.FromResult (result); - } - - /// - /// is exists an item by providing it's key ... - /// - /// - /// - public Task IsExistsByKey (TKey key) { - return Task.Run (() => { - return STORE.Any (i => GetKey (i).Equals (key)); - }); - } - #endregion - - // - #region Count ... - /// - /// Count Exists items ... - /// - /// - public Task Count () { - // - var result = STORE.Count (); - return Task.FromResult (result); - } - #endregion - - // - #region Keys ... - /// - /// set Object Key ... - /// - /// - /// - 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); - } - - /// - /// Get Object Key ... - /// - /// - /// - 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 (); - } catch { - result = keyString.ConvertTo (); - } - return result; - } - #endregion - - // - #region Private ... - private void RemoveItemFromStore (T item) { - STORE = new ConcurrentBag ( - STORE.Except (new [] { item }) - ); - } - - private void RemoveItemsFromStore (IEnumerable items) { - STORE = new ConcurrentBag ( - STORE.Except (items) - ); - } - #endregion + // + #region Constructor ... + protected XBaseInMemoryStore( + IXBaseStoreEvents events = null + ) + { + this.events = events; } + #endregion + + // + #region Retrieve ... + /// + /// Retrieve all Exists Items ... + /// + /// + public Task> GetAll() + { + // + var result = STORE.AsEnumerable(); + return Task.FromResult(result); + } + + /// + /// Retrieve specific Item by key ... + /// + /// + /// + public Task 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; + } + }); + } + + /// + /// retrieve items based on specific condition ... + /// + /// + /// + public Task> FindMany(Expression> condition) + { + // + // Validate Args ... + if (condition.IsNull()) + { + return Task.FromResult(new List().AsEnumerable()); + } + + // + var whereFunc = condition.Compile(); + var result = STORE.Where(whereFunc); + + // + return Task.FromResult(result); + } + + /// + /// retrieve item based on specific condition ... + /// + /// + /// + public Task FindOne(Expression> condition) + { + // + // Validate Args ... + if (condition.IsNull()) + { + return null; + } + + // + var whereFunc = condition.Compile(); + var result = STORE.FirstOrDefault(whereFunc); + + // + return Task.FromResult(result); + } + #endregion + + // + #region Add ... + /// + /// add specific item to Store ... + /// + /// + /// + public Task 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; + }); + } + + /// + /// Add a Collection of Items on Store ... + /// + /// + /// + public Task AddMany(XBaseRangeRequest 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 ... + /// + /// Remove an item from store ... + /// + /// + /// + public Task 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); + } + } + + /// + /// remove an item by it's Key ... + /// + /// + /// + public Task 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; + }); + } + + /// + /// remove a collection of items at once ... + /// + /// + /// + public Task RemoveMany(XBaseRangeRequest 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 ... + /// + /// Update Exists Item in store ... + /// + /// + /// + /// + /// + /// + /// + public Task Update( + T item, + ICollection propertyWhiteList = null, + ICollection propertyBlackList = null, + ICollection>> 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; + } + }); + } + + /// + /// Update a Collection of Exists Items ... + /// + /// + /// + /// + /// + /// + /// + public Task UpdateMany( + XBaseRangeRequest items, + ICollection propertyWhiteList = null, + ICollection propertyBlackList = null, + ICollection>> 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 ... + /// + /// retrieve item(s) exists based on specific condition ... + /// + /// + /// + public Task IsExists(Expression> condition) + { + // + // Validate Args ... + if (condition.IsNull()) + { + return Task.FromResult(false); + } + + // + var whereFunc = condition.Compile(); + var result = STORE.Any(whereFunc); + + // + return Task.FromResult(result); + } + + /// + /// is exists an item by providing it's key ... + /// + /// + /// + public Task IsExistsByKey(TKey key) + { + return Task.Run(() => + { + return STORE.Any(i => GetKey(i).Equals(key)); + }); + } + #endregion + + // + #region Count ... + /// + /// Count Exists items ... + /// + /// + public Task Count() + { + // + var result = STORE.Count(); + return Task.FromResult(result); + } + #endregion + + // + #region Keys ... + /// + /// set Object Key ... + /// + /// + /// + 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); + } + + /// + /// Get Object Key ... + /// + /// + /// + 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(); + } + catch + { + result = keyString.ConvertTo(); + } + return result; + } + #endregion + + // + #region Private ... + private void RemoveItemFromStore(T item) + { + // + STORE = new ConcurrentBag( + STORE.Except(new[] { item }) + ); + + // + OnRemove(item); + } + + private void RemoveItemsFromStore(IEnumerable items) + { + // + STORE = new ConcurrentBag( + STORE.Except(items) + ); + + // + OnRemoveMany(items); + } + + // + #region Event Notifiers ... + private void OnAdd(T item) + { + // + if (!events.IsNull()) + { + events.AddEvent(new XBaseStorableDtoEventModel(item)); + } + } + private void OnUpdate(T item) + { + // + if (!events.IsNull()) + { + events.UpdateEvent(new XBaseStorableDtoEventModel(item)); + } + } + + private void OnRemove(T item) + { + // + if (!events.IsNull()) + { + events.RemoveEvent(new XBaseStorableDtoEventModel(item)); + } + } + + private void OnAddMany(IEnumerable items) + { + // + if (!events.IsNull()) + { + events.AddManyEvent(new XBaseStorableDtoEventModel>(items)); + } + } + + private void OnUpdateMany(IEnumerable items) + { + // + if (!events.IsNull()) + { + events.UpdateManyEvent(new XBaseStorableDtoEventModel>(items)); + } + } + + private void OnRemoveMany(IEnumerable items) + { + // + if (!events.IsNull()) + { + events.RemoveManyEvent(new XBaseStorableDtoEventModel>(items)); + } + } + #endregion + #endregion + } } \ No newline at end of file