From 9b80aa6b019275e50ed343e1723e5920704f3504 Mon Sep 17 00:00:00 2001 From: nhathoang989 Date: Mon, 16 Sep 2024 17:31:35 +0700 Subject: [PATCH] add simple vm --- .../ViewModel/SimpleViewModelBase.Async.cs | 142 ++++++++++++++++++ .../ViewModel/SimpleViewModelBase.Uow.cs | 76 ++++++++++ .../ViewModel/SimpleViewModelBase.cs | 118 +++++++++++++++ src/mix.heart/ViewModel/ViewModelBase.Uow.cs | 1 + src/mix.heart/ViewModel/ViewModelBase.cs | 5 +- 5 files changed, 338 insertions(+), 4 deletions(-) create mode 100644 src/mix.heart/ViewModel/SimpleViewModelBase.Async.cs create mode 100644 src/mix.heart/ViewModel/SimpleViewModelBase.Uow.cs create mode 100644 src/mix.heart/ViewModel/SimpleViewModelBase.cs diff --git a/src/mix.heart/ViewModel/SimpleViewModelBase.Async.cs b/src/mix.heart/ViewModel/SimpleViewModelBase.Async.cs new file mode 100644 index 0000000..e3a8d58 --- /dev/null +++ b/src/mix.heart/ViewModel/SimpleViewModelBase.Async.cs @@ -0,0 +1,142 @@ +using Mix.Heart.Enums; +using Mix.Heart.Exceptions; +using Mix.Heart.Helpers; +using Mix.Heart.Models; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Mix.Heart.ViewModel +{ + public abstract partial class SimpleViewModelBase + { + #region Async + + public async Task DeleteAsync(CancellationToken cancellationToken = default) + { + try + { + cancellationToken.ThrowIfCancellationRequested(); + BeginUow(); + await DeleteHandlerAsync(cancellationToken); + await CompleteUowAsync(cancellationToken); + } + catch (Exception ex) + { + await HandleExceptionAsync(ex); + } + finally + { + await CloseUowAsync(); + } + } + + protected virtual async Task DeleteHandlerAsync(CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + Repository.SetUowInfo(UowInfo); + await Repository.DeleteAsync(Id, cancellationToken); + ModifiedEntities.Add(new(typeof(TEntity), Id, ViewModelAction.Delete)); + await DeleteEntityRelationshipAsync(cancellationToken); + } + + public async Task SaveAsync(CancellationToken cancellationToken = default) + { + try + { + cancellationToken.ThrowIfCancellationRequested(); + BeginUow(); + IsValid = Validator.TryValidateObject(this, ValidateContext, Errors); + await Validate(cancellationToken); + if (!IsValid) + { + await HandleExceptionAsync(new MixException(MixErrorStatus.Badrequest, Errors.Select(e => e.ErrorMessage).ToArray())); + } + var entity = await SaveHandlerAsync(cancellationToken); + await CompleteUowAsync(cancellationToken); + return entity.Id; + } + catch (Exception ex) + { + await HandleExceptionAsync(ex); + return default; + } + finally + { + await CloseUowAsync(); + } + } + + public async Task SaveFieldsAsync(IEnumerable properties, CancellationToken cancellationToken = default) + { + try + { + cancellationToken.ThrowIfCancellationRequested(); + BeginUow(); + foreach (var property in properties) + { + // check if field name is exist + var lamda = ReflectionHelper.GetLambda(property.PropertyName); + if (lamda != null) + { + ReflectionHelper.SetPropertyValue(this, property); + } + else + { + await HandleExceptionAsync(new MixException(MixErrorStatus.Badrequest, $"Invalid Property {property.PropertyName}")); + } + } + IsValid = Validator.TryValidateObject(this, ValidateContext, Errors); + await Validate(cancellationToken); + var entity = await ParseEntity(cancellationToken); + await Repository.SaveAsync(entity, cancellationToken); + await CompleteUowAsync(cancellationToken); + return entity.Id; + } + catch (Exception ex) + { + await HandleExceptionAsync(ex); + return default; + } + finally + { + await CloseUowAsync(); + } + } + + #region virtual methods + + // Override this method + protected virtual async Task SaveHandlerAsync(CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + var entity = await ParseEntity(cancellationToken); + + ModifiedEntities.Add(new(typeof(TEntity), Id, !Id.Equals(default) ? ViewModelAction.Create : ViewModelAction.Update)); + + await Repository.SaveAsync(entity, cancellationToken); + await SaveEntityRelationshipAsync(entity, cancellationToken); + Id = entity.Id; + return entity; + } + + // Override this method + protected virtual Task SaveEntityRelationshipAsync(TEntity parentEntity, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + protected virtual Task DeleteEntityRelationshipAsync(CancellationToken cancellationToken = default) + { + return Task.CompletedTask; + } + + #endregion + + #endregion + } +} diff --git a/src/mix.heart/ViewModel/SimpleViewModelBase.Uow.cs b/src/mix.heart/ViewModel/SimpleViewModelBase.Uow.cs new file mode 100644 index 0000000..8fae6f3 --- /dev/null +++ b/src/mix.heart/ViewModel/SimpleViewModelBase.Uow.cs @@ -0,0 +1,76 @@ +using Mix.Heart.Enums; +using Mix.Heart.Exceptions; +using Mix.Heart.Services; +using Mix.Heart.UnitOfWork; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Mix.Heart.ViewModel +{ + public abstract partial class SimpleViewModelBase + { + private bool _isRoot; + + protected virtual void BeginUow() + { + if (UowInfo == null) + { + InitRootUow(); + } + + UowInfo.Begin(); + + if (Repository != null) + { + Repository.SetUowInfo(UowInfo); + } + else + { + Repository = GetRepository(UowInfo, CacheService); + } + Repository.SetCacheService(CacheService); + Repository.CacheFolder = CacheFolder; + } + + protected virtual void InitRootUow() + { + UowInfo ??= new UnitOfWorkInfo(InitDbContext()); + _isRoot = true; + } + + protected virtual async Task CloseUowAsync() + { + if (_isRoot) + { + await UowInfo.CloseAsync(); + } + } + + protected virtual async Task CompleteUowAsync(CancellationToken cancellationToken = default) + { + if (_isRoot) + { + await UowInfo.CompleteAsync(cancellationToken); + return; + }; + + _isRoot = false; + } + + protected virtual TDbContext InitDbContext() + { + var dbContextType = typeof(TDbContext); + var contextCtorInfo = dbContextType.GetConstructor(new Type[] { }); + + if (contextCtorInfo == null) + { + throw new MixException( + MixErrorStatus.ServerError, + $"{dbContextType}: Contructor Parameterless Notfound"); + } + + return (TDbContext)contextCtorInfo.Invoke([]); + } + } +} diff --git a/src/mix.heart/ViewModel/SimpleViewModelBase.cs b/src/mix.heart/ViewModel/SimpleViewModelBase.cs new file mode 100644 index 0000000..2388c3e --- /dev/null +++ b/src/mix.heart/ViewModel/SimpleViewModelBase.cs @@ -0,0 +1,118 @@ +using Microsoft.EntityFrameworkCore; +using Mix.Heart.Entities; +using Mix.Heart.Enums; +using Mix.Heart.Exceptions; +using Mix.Heart.Helpers; +using Mix.Heart.UnitOfWork; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Mix.Heart.ViewModel +{ + public abstract partial class SimpleViewModelBase : ViewModelQueryBase + where TPrimaryKey : IComparable + where TEntity : class, IEntity + where TDbContext : DbContext + where TView : SimpleViewModelBase + { + #region Properties + + public TPrimaryKey Id { get; set; } + + #endregion + + #region Constructors + + public SimpleViewModelBase() : base() + { + } + + public SimpleViewModelBase(TDbContext context) : base(context) + { + } + + public SimpleViewModelBase(TEntity entity, UnitOfWorkInfo uowInfo) : base(entity, uowInfo) + { + } + + public SimpleViewModelBase(UnitOfWorkInfo unitOfWorkInfo) : base(unitOfWorkInfo) + { + } + + #endregion + + #region Abstracts + public virtual void InitDefaultValues(string language = null, int? cultureId = null) + { + } + + #endregion + + public virtual async Task Validate(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + if (!IsValid) + { + await HandleExceptionAsync(new MixException(MixErrorStatus.Badrequest, Errors.Select(e => e.ErrorMessage).ToArray())); + } + } + + public void SetDbContext(TDbContext context) + { + UowInfo = new UnitOfWorkInfo(context); + } + + public virtual TEntity InitModel() + { + Type classType = typeof(TEntity); + return (TEntity)Activator.CreateInstance(classType); + } + + public virtual Task ParseEntity(CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (IsDefaultId(Id)) + { + InitDefaultValues(); + } + + var entity = Activator.CreateInstance(); + ReflectionHelper.Map(this as TView, entity); + return Task.FromResult(entity); + } + + public bool IsDefaultId(TPrimaryKey id) + { + return (id.GetType() == typeof(Guid) && Guid.Parse(id.ToString()) == Guid.Empty) + || (id.GetType() == typeof(int) && int.Parse(id.ToString()) == default); + } + + public virtual Task DuplicateAsync(CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.CompletedTask; + } + + public virtual void Duplicate() + { + } + + protected async Task HandleErrorsAsync() + { + await HandleExceptionAsync(new MixException(MixErrorStatus.Badrequest, Errors.Select(e => e.ErrorMessage).ToArray())); + } + + protected virtual async Task HandleExceptionAsync(Exception ex) + { + await Repository.HandleExceptionAsync(ex); + } + + protected virtual void HandleException(Exception ex) + { + Repository.HandleException(ex); + } + } +} diff --git a/src/mix.heart/ViewModel/ViewModelBase.Uow.cs b/src/mix.heart/ViewModel/ViewModelBase.Uow.cs index 56c665b..d602e27 100644 --- a/src/mix.heart/ViewModel/ViewModelBase.Uow.cs +++ b/src/mix.heart/ViewModel/ViewModelBase.Uow.cs @@ -1,5 +1,6 @@ using Mix.Heart.Enums; using Mix.Heart.Exceptions; +using Mix.Heart.Services; using Mix.Heart.UnitOfWork; using System; using System.Threading; diff --git a/src/mix.heart/ViewModel/ViewModelBase.cs b/src/mix.heart/ViewModel/ViewModelBase.cs index e9734be..837efc4 100644 --- a/src/mix.heart/ViewModel/ViewModelBase.cs +++ b/src/mix.heart/ViewModel/ViewModelBase.cs @@ -11,7 +11,7 @@ namespace Mix.Heart.ViewModel { - public abstract partial class ViewModelBase : ViewModelQueryBase + public abstract partial class ViewModelBase : SimpleViewModelBase where TPrimaryKey : IComparable where TEntity : class, IEntity where TDbContext : DbContext @@ -19,8 +19,6 @@ public abstract partial class ViewModelBase