From c5d69522a94c634c77ee850489eef486cace21bf Mon Sep 17 00:00:00 2001 From: Hadi Khazaee Asl Date: Thu, 25 Jan 2024 04:37:38 +0330 Subject: [PATCH] Initial Commit ... --- .gitignore | 8 + Base/XBaseClass.cs | 101 +++++ Base/XBaseDescriptor.cs | 7 + Base/XBaseDto.cs | 7 + Base/XBaseEntity.cs | 23 ++ Base/XBaseGuidIDEntity.cs | 8 + Base/XBaseIntIDEntity.cs | 8 + Base/XBaseRangeRequest.cs | 11 + Base/XBaseStorableDto.cs | 5 + Base/XBaseStringIDEntity.cs | 8 + Dtos/XDeviceDto.cs | 32 ++ Dtos/XFileDto.cs | 30 ++ Dtos/XObjectResponseResult.cs | 16 + Dtos/XPageRequest.cs | 14 + Dtos/XPageResponse.cs | 16 + Dtos/XQuery.cs | 45 ++ Dtos/XQueryResult.cs | 46 +++ Dtos/XValidateRevisionRequest.cs | 12 + Extensions/DIExtensions.cs | 40 ++ Interfaces/IXAuditable.cs | 13 + Interfaces/IXBaseStore.cs | 143 +++++++ Interfaces/IXEntityControllerActions.cs | 80 ++++ Providers/XBaseInMemoryStore.cs | 521 ++++++++++++++++++++++++ README.md | 24 ++ nuget.config | 7 + xModels.csproj | 27 ++ 26 files changed, 1252 insertions(+) create mode 100644 .gitignore create mode 100644 Base/XBaseClass.cs create mode 100644 Base/XBaseDescriptor.cs create mode 100644 Base/XBaseDto.cs create mode 100644 Base/XBaseEntity.cs create mode 100644 Base/XBaseGuidIDEntity.cs create mode 100644 Base/XBaseIntIDEntity.cs create mode 100644 Base/XBaseRangeRequest.cs create mode 100644 Base/XBaseStorableDto.cs create mode 100644 Base/XBaseStringIDEntity.cs create mode 100644 Dtos/XDeviceDto.cs create mode 100644 Dtos/XFileDto.cs create mode 100644 Dtos/XObjectResponseResult.cs create mode 100644 Dtos/XPageRequest.cs create mode 100644 Dtos/XPageResponse.cs create mode 100644 Dtos/XQuery.cs create mode 100644 Dtos/XQueryResult.cs create mode 100644 Dtos/XValidateRevisionRequest.cs create mode 100644 Extensions/DIExtensions.cs create mode 100644 Interfaces/IXAuditable.cs create mode 100644 Interfaces/IXBaseStore.cs create mode 100644 Interfaces/IXEntityControllerActions.cs create mode 100644 Providers/XBaseInMemoryStore.cs create mode 100644 README.md create mode 100644 nuget.config create mode 100644 xModels.csproj diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1bd9d0b --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +# +# DotNet ... +bin +obj + +# +# Natural Docs ... +Documentation/* diff --git a/Base/XBaseClass.cs b/Base/XBaseClass.cs new file mode 100644 index 0000000..2bac8d6 --- /dev/null +++ b/Base/XBaseClass.cs @@ -0,0 +1,101 @@ +using System; +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xExceptions.Constants; + +namespace xModels.Base { + /// + /// a Base class which carry required things as a Father for it's childs ... + /// + public abstract class XBaseClass { + // + #region Properties ... + /// + /// LogTag which Specified for this class ... + /// + private readonly string LOG_TAG; + + /// + /// logger instance ... + /// + private readonly ILogger logger; + #endregion + + // + #region Constructor ... + public XBaseClass (ILoggerFactory loggerFactory) { + // + // Preparing Log Tag ... + LOG_TAG = this.GetType ().Name; + + // + Type classType = this.GetType (); + logger = loggerFactory.CreateLogger (classType); + } + #endregion + + // + #region Protected ... + /// + /// Extract Prepared Log MEssage ... + /// + /// + /// + protected virtual string GetLogMessage (string message) { + // + // Prepare Log Message ... + message = $"{LOG_TAG} => {message}"; + return message; + } + + /// + /// Logging specific Message ... + /// + /// + /// + protected virtual void LogMessage ( + string message, + LogLevel logLevel = LogLevel.Information + ) { + // + // Validate Args ... + if (message.IsNullOrEmpty ()) { + throw XException.InvalidArgs + .ToException (); + } + + // + // Prepare Log Message ... + message = GetLogMessage (message); + + // + // Logging Message ... + logger.Log ( + message: message, + logLevel: logLevel + ); + } + + /// + /// Logging specific Message to Console ... + /// + /// + protected virtual void ConsoleMessage (string message) { + // + // Validate Args ... + if (message.IsNullOrEmpty ()) { + throw XException.InvalidArgs + .ToException (); + } + + // + // Prepare Log Message ... + message = GetLogMessage (message); + + // + // Logging Message ... + Console.WriteLine (message); + } + #endregion + } +} \ No newline at end of file diff --git a/Base/XBaseDescriptor.cs b/Base/XBaseDescriptor.cs new file mode 100644 index 0000000..ca8fc35 --- /dev/null +++ b/Base/XBaseDescriptor.cs @@ -0,0 +1,7 @@ +namespace xModels.Base { + /// + /// Descriptors classes Map required data for creating Entities on Seeding time ... + /// this is a Base Descriptor class ... + /// + public abstract class XBaseDescriptor { } +} \ No newline at end of file diff --git a/Base/XBaseDto.cs b/Base/XBaseDto.cs new file mode 100644 index 0000000..c687a97 --- /dev/null +++ b/Base/XBaseDto.cs @@ -0,0 +1,7 @@ +namespace xModels.Base { + /// + /// DTO - Data Transfer Objects is a kind of classes which Represent Entities ... + /// this is base Dto class ... + /// + public abstract class XBaseDto { } +} \ No newline at end of file diff --git a/Base/XBaseEntity.cs b/Base/XBaseEntity.cs new file mode 100644 index 0000000..6e7d798 --- /dev/null +++ b/Base/XBaseEntity.cs @@ -0,0 +1,23 @@ +using System.ComponentModel.DataAnnotations; + +namespace xModels.Base { + /// + /// Base Entity Class + /// + /// + public abstract class XBaseEntity { + /// + /// Entity Id + /// + /// entity unique id + [Key] + [Required] + public T Id { get; set; } + + /// + /// Provided Soft Delete Behaviour ... + /// + /// + public bool Deleted { get; set; } + } +} \ No newline at end of file diff --git a/Base/XBaseGuidIDEntity.cs b/Base/XBaseGuidIDEntity.cs new file mode 100644 index 0000000..a3436b9 --- /dev/null +++ b/Base/XBaseGuidIDEntity.cs @@ -0,0 +1,8 @@ +using System; + +namespace xModels.Base { + /// + /// a BaseEntity Class which has Id of Guid type + /// + public class XBaseGuidIDEntity : XBaseEntity { } +} \ No newline at end of file diff --git a/Base/XBaseIntIDEntity.cs b/Base/XBaseIntIDEntity.cs new file mode 100644 index 0000000..26f4006 --- /dev/null +++ b/Base/XBaseIntIDEntity.cs @@ -0,0 +1,8 @@ +using System.ComponentModel.DataAnnotations; + +namespace xModels.Base { + /// + /// a BaseEntity Class which has Id of int type + /// + public abstract class XBaseIntIDEntity : XBaseEntity { } +} \ No newline at end of file diff --git a/Base/XBaseRangeRequest.cs b/Base/XBaseRangeRequest.cs new file mode 100644 index 0000000..be82549 --- /dev/null +++ b/Base/XBaseRangeRequest.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace xModels.Base { + /// + /// Range Request Classes Carry a List of Objects + /// + /// + public class XBaseRangeRequest { + public IEnumerable Items { get; set; } = new HashSet (); + } +} \ No newline at end of file diff --git a/Base/XBaseStorableDto.cs b/Base/XBaseStorableDto.cs new file mode 100644 index 0000000..968facd --- /dev/null +++ b/Base/XBaseStorableDto.cs @@ -0,0 +1,5 @@ +namespace xModels.Base { + public abstract class XBaseStorableDto : XBaseDto { + public abstract TKey Id { get; set; } + } +} \ No newline at end of file diff --git a/Base/XBaseStringIDEntity.cs b/Base/XBaseStringIDEntity.cs new file mode 100644 index 0000000..1917cd4 --- /dev/null +++ b/Base/XBaseStringIDEntity.cs @@ -0,0 +1,8 @@ +using System; + +namespace xModels.Base { + /// + /// a BaseEntity Class which has Id of string type + /// + public class XBaseStringIDEntity : XBaseEntity { } +} \ No newline at end of file diff --git a/Dtos/XDeviceDto.cs b/Dtos/XDeviceDto.cs new file mode 100644 index 0000000..3c12675 --- /dev/null +++ b/Dtos/XDeviceDto.cs @@ -0,0 +1,32 @@ +using xCommons.Constants; +using xModels.Base; + +namespace xModels.Dtos { + public class XDeviceDto : XBaseDto { + /// + /// Operating System + /// + /// device operating system identifier + public string Os { get; set; } + /// + /// Operating System Version + /// + /// which version of operatin system is in use + public string OsVersion { get; set; } + /// + /// Device Browser + /// + /// the Browser identifier + public string Browser { get; set; } + /// + /// Browser Engine Agent + /// + /// browser engine Agent + public string UserAgent { get; set; } + /// + /// Type of client Device + /// + /// represent device type XDeviceType + public XDeviceType DeviceType { get; set; } + } +} \ No newline at end of file diff --git a/Dtos/XFileDto.cs b/Dtos/XFileDto.cs new file mode 100644 index 0000000..4f61c7c --- /dev/null +++ b/Dtos/XFileDto.cs @@ -0,0 +1,30 @@ +using System; +using System.ComponentModel.DataAnnotations; +using xCommons.Constants; +using xModels.Base; + +namespace xModels.Dtos { + public partial class XFileDto : XBaseDto { + [Required] + public string AuthorId { get; set; } + + public XFileType Type { get; set; } + + [Required] + [StringLength (255)] + public string Name { get; set; } + + [StringLength (255)] + public string Thumb { get; set; } + + [Required] + [StringLength (255)] + public string Path { get; set; } + + [StringLength (255)] + public string ThumbPath { get; set; } + + [Required] + public DateTime UploadedOn { get; set; } + } +} \ No newline at end of file diff --git a/Dtos/XObjectResponseResult.cs b/Dtos/XObjectResponseResult.cs new file mode 100644 index 0000000..52ea70b --- /dev/null +++ b/Dtos/XObjectResponseResult.cs @@ -0,0 +1,16 @@ +namespace xModels.Dtos { + /// + /// Some Requests Get Object Response Result of Data ... + /// + public struct XObjectResponseResult { + + public XObjectResponseResult (T responseObject, string responseText) { + this.Object = responseObject; + this.Text = responseText; + } + + public T Object { get; } + + public string Text { get; } + } +} \ No newline at end of file diff --git a/Dtos/XPageRequest.cs b/Dtos/XPageRequest.cs new file mode 100644 index 0000000..e8f2dcb --- /dev/null +++ b/Dtos/XPageRequest.cs @@ -0,0 +1,14 @@ +namespace xModels.Dtos { + /// + /// Request a Page of Data based on Cursor ... + /// this only be Used in GraphQL ... + /// + public class XPageRequest { + public int? First { get; set; } + public int? Last { get; set; } + public string After { get; set; } = ""; + public string Before { get; set; } = ""; + public string SortBy { get; set; } = ""; + public bool DescendingSort { get; set; } + } +} \ No newline at end of file diff --git a/Dtos/XPageResponse.cs b/Dtos/XPageResponse.cs new file mode 100644 index 0000000..79c90b9 --- /dev/null +++ b/Dtos/XPageResponse.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace xModels.Dtos { + /// + /// Result of a Requested Page of Data based on Cursor ... + /// this only be Used in GraphQL ... + /// + /// + public class XPageResponse { + public List Nodes { get; set; } + public int TotalCount { get; set; } + public bool HasNextPage { get; set; } + public bool HasPreviousPage { get; set; } + } + +} \ No newline at end of file diff --git a/Dtos/XQuery.cs b/Dtos/XQuery.cs new file mode 100644 index 0000000..c70ef8f --- /dev/null +++ b/Dtos/XQuery.cs @@ -0,0 +1,45 @@ +using xModels.Base; + +namespace xModels.Dtos { + /// + /// Request a Page of Data Based on Query ... + /// this can used in every Data Providers ... + /// + public class XQuery : XBaseDto { + /// + /// an string for filtering results + /// + /// + public string Filter { get; set; } = null; + + /// + /// determines fully load object with it's relational fields or not + /// + /// + public bool ContainsDetail { get; set; } = false; + + /// + /// specifiy sorting of items + /// + /// + public string SortBy { get; set; } = nameof (XBaseIntIDEntity.Id); + + /// + /// specify sorting direction + /// + /// + public bool IsAscending { get; set; } = true; + + /// + /// specify page number + /// + /// + public int Page { get; set; } = 1; + + /// + /// specify page size + /// + /// + public int PageSize { get; set; } + } +} \ No newline at end of file diff --git a/Dtos/XQueryResult.cs b/Dtos/XQueryResult.cs new file mode 100644 index 0000000..43f3029 --- /dev/null +++ b/Dtos/XQueryResult.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using xModels.Base; + +namespace xModels.Dtos { + /// + /// Result of a Requested Page of Data Based on Query ... + /// this can used in every Data Providers ... + /// + public class XQueryResult : XBaseDto { + /// + /// a collection of items for a page ... + /// + /// + public IEnumerable Items { get; set; } + + /// + /// Current Page Number ... + /// + /// + public long Page { get; set; } + + /// + /// Current Page Size ... + /// + /// + public long PageSize { get; set; } + + /// + /// Total Pages ... + /// + /// + public long TotalPages { get; set; } + + /// + /// Total Filtered Items ... + /// + /// + public long TotalFilteredItems { get; set; } + + /// + /// Total Exists Items ... + /// + /// + public long TotalItems { get; set; } + } +} \ No newline at end of file diff --git a/Dtos/XValidateRevisionRequest.cs b/Dtos/XValidateRevisionRequest.cs new file mode 100644 index 0000000..15aaf1d --- /dev/null +++ b/Dtos/XValidateRevisionRequest.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations; + +namespace xModels.Dtos { + /// + /// in some cases we have to validate a revision of request, this is a model + /// which used to request a revision validator ... + /// + public class XValidateRevisionRequest { + [Required] + public string Revision { get; set; } + } +} \ No newline at end of file diff --git a/Extensions/DIExtensions.cs b/Extensions/DIExtensions.cs new file mode 100644 index 0000000..82234f6 --- /dev/null +++ b/Extensions/DIExtensions.cs @@ -0,0 +1,40 @@ +using Microsoft.Extensions.DependencyInjection; +using xCommons.Extensions; +using xExceptions.Constants; +using xModels.Base; +using xModels.Interfaces; + +namespace xModels.Extensions { + public static class DIExtensions { + /// + /// Register an Specific Store in DI ... + /// + /// DI service collection ... + /// an implementation of inetrface of store ... + /// service lifetime + /// implementation of TType ... + /// an inetrface which implements IXBaseStore + /// type of an instance of XBaseStorable + /// type of key of StorableItem + /// + public static void AddXStore ( + this IServiceCollection services, + TImplementation store, + ServiceLifetime lifeTime = ServiceLifetime.Singleton + ) + where TImplementation : TType + where TType : IXBaseStore + where TStoreItemDto : XBaseStorableDto { + // + // Validate Args ... + if (store.IsNull ()) { + throw XException.InvalidArgs + .ToException (); + } + + // + // Register Service in DI ... + services.Add (new ServiceDescriptor (typeof (TType), typeof (TImplementation), lifeTime)); + } + } +} \ No newline at end of file diff --git a/Interfaces/IXAuditable.cs b/Interfaces/IXAuditable.cs new file mode 100644 index 0000000..5c9a590 --- /dev/null +++ b/Interfaces/IXAuditable.cs @@ -0,0 +1,13 @@ +using System; + +namespace xModels.Interfaces { + /// + /// Some times we need to wrap Entities by some logs or audits ... + /// this class provide Audit fields for an Auditable Entity ... + /// + public interface IXAuditable { + string UserAgent { get; } + string UserIp { get; } + DateTime TransactionTime { get; } + } +} \ No newline at end of file diff --git a/Interfaces/IXBaseStore.cs b/Interfaces/IXBaseStore.cs new file mode 100644 index 0000000..43659e9 --- /dev/null +++ b/Interfaces/IXBaseStore.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Threading.Tasks; +using xModels.Base; + +namespace xModels.Interfaces { + public interface IXBaseStore where T : XBaseStorableDto { + // + #region Retrieve ... + /// + /// Retrieve all Exists Items ... + /// + /// + Task> GetAll (); + + /// + /// Retrieve specific Item by key ... + /// + /// + /// + Task Get (TKey key); + + /// + /// retrieve items based on specific condition ... + /// + /// + /// + Task> FindMany (Expression> condition); + + /// + /// retrieve item based on specific condition ... + /// + /// + /// + Task FindOne (Expression> condition); + #endregion + + // + #region Add ... + /// + /// add specific item to Store ... + /// + /// + /// + Task Add (T item); + + /// + /// Add a Collection of Items on Store ... + /// + /// + /// + Task AddMany (XBaseRangeRequest items); + #endregion + + // + #region Remove ... + /// + /// Remove an item from store ... + /// + /// + /// + Task Remove (T item); + + /// + /// remove an item by it's Key ... + /// + /// + /// + Task RemoveByKey (TKey key); + + /// + /// remove a collection of items at once ... + /// + /// + /// + Task RemoveMany(XBaseRangeRequest items); + #endregion + + // + #region Update ... + /// + /// Update Exists Item in store ... + /// + /// + /// + /// + /// + /// + /// + Task Update ( + T item, + ICollection propertyWhiteList = null, + ICollection propertyBlackList = null, + ICollection>> propertyValueProviders = null, + bool updateWithNullOrEmptyValues = false + ); + + /// + /// Update a Collection of Exists Items ... + /// + /// + /// + /// + /// + /// + /// + Task UpdateMany ( + XBaseRangeRequest items, + ICollection propertyWhiteList = null, + ICollection propertyBlackList = null, + ICollection>> propertyValueProviders = null, + bool updateWithNullOrEmptyValues = false + ); + #endregion + + // + #region Exists ... + /// + /// retrieve item(s) exists based on specific condition ... + /// + /// + /// + Task IsExists (Expression> condition); + + /// + /// is exists an item by providing it's key ... + /// + /// + /// + Task IsExistsByKey (TKey key); + #endregion + + // + #region Count ... + /// + /// Count Exists items ... + /// + /// + Task Count (); + #endregion + } +} \ No newline at end of file diff --git a/Interfaces/IXEntityControllerActions.cs b/Interfaces/IXEntityControllerActions.cs new file mode 100644 index 0000000..c72a409 --- /dev/null +++ b/Interfaces/IXEntityControllerActions.cs @@ -0,0 +1,80 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using xModels.Base; +using xModels.Dtos; + +namespace xModels.Interfaces { + public interface IXEntityControllerActions + where TEntity : XBaseEntity { + // + #region Retrieve ... + Task> Get ( + [FromRoute] TKey id, [FromQuery] bool ignoreSoftDeleteds = true, [FromQuery] bool containsDetail = false + ); + + Task>> GetAll ( + [FromQuery] bool ignoreSoftDeleteds = true, [FromQuery] bool containsDetail = false + ); + + Task> FindOne ( + [FromRoute] string query, [FromQuery] bool ignoreSoftDeleteds = true, [FromQuery] bool containsDetail = false + ); + + Task>> FindMany ( + [FromRoute] string query, [FromQuery] bool ignoreSoftDeleteds = true, [FromQuery] bool containsDetail = false + ); + + Task>> Query ( + [FromQuery] XQuery query, [FromQuery] bool ignoreSoftDeleteds = true + ); + + // + // TODO: Fix this ... + // Task>> RequestPage ( + // [FromQuery] XPageRequest request, [FromQuery] bool ignoreSoftDeleteds = true, [FromQuery] bool containsDetail = false + // ); + #endregion + + // + #region Add ... + Task> Add (TEntity item); + + Task> AddOrUpdate (TEntity item); + + Task AddMany (XBaseRangeRequest request); + #endregion + + // + #region Update ... + Task> Update ( + [FromRoute] TKey id, [FromBody] TEntity item + ); + + Task> UpdateMany ( + XBaseRangeRequest request + ); + #endregion + + // + #region Exists ... + Task> IsExists ( + [FromRoute] TKey id, + bool ignoreSoftDeletedss = true + ); + #endregion + + // + #region Remove ... + Task> Remove ( + [FromRoute] TKey id, + bool softDelete = true + ); + + Task RemoveMany ( + XBaseRangeRequest request, + bool softDelete = true + ); + #endregion + } +} \ No newline at end of file diff --git a/Providers/XBaseInMemoryStore.cs b/Providers/XBaseInMemoryStore.cs new file mode 100644 index 0000000..5f1d250 --- /dev/null +++ b/Providers/XBaseInMemoryStore.cs @@ -0,0 +1,521 @@ +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 : IXBaseStore + 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); + } + + /// + /// 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 + } +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..793b75f --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# xModels + +it is a Part of XProject on SaherElm IT Center which provides: + +- DTO Models. +- Entity Models. +- Navigation Models. +- Configuration Models. +- Some extra Extensions on Models. +- etc. + +this module has following dependencies : + +- xCommons + +there is no need any Configuration for this module. + +## Maintainer + +Hadi Khazaee asl + +[https://www.saherelm.ir](https://www.saherelm.ir) + +[hadi_khazaee_asl@yahoo.com](mailto:hadi_khazaee_asl@yahoo.com) diff --git a/nuget.config b/nuget.config new file mode 100644 index 0000000..87b6eb0 --- /dev/null +++ b/nuget.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/xModels.csproj b/xModels.csproj new file mode 100644 index 0000000..e09b275 --- /dev/null +++ b/xModels.csproj @@ -0,0 +1,27 @@ + + + + netstandard2.0 + xDashboard.xModels + 1.0.0 + Hadi Khazaee Asl + SaherElm IT Center + + provide required models and related tools for using in xDashboard project. + + + + icon.png + + + + + + + + + + + + + \ No newline at end of file