Jelajahi Sumber

Add SettingsService (implemented), Implements ExceptionLibrary (ExceptionFactory).

Dalibor Votruba 3 tahun lalu
induk
melakukan
cd60209d4e

+ 18 - 0
Common/qdr.app.qlbrc.common/ExceptionFactory.cs

@@ -0,0 +1,18 @@
+using Quadarax.Foundation.Core.Exceptions;
+
+namespace Quadarax.Application.QLiberace.Common
+{
+    public static class ExceptionFactory
+    {
+        private static ExceptionLibrary _instance = new ExceptionLibrary();
+
+        public static CodeException CreateException(int code, params string[] messageParams)
+        {
+            return _instance.CreateException(code, messageParams);
+        }
+        public static CodeException CreateException(int code, Exception innerException, params string[] messageParams)
+        {
+            return _instance.CreateException(code, innerException, messageParams);
+        }
+    }
+}

+ 30 - 0
Common/qdr.app.qlbrc.common/ExceptionLibrary.cs

@@ -0,0 +1,30 @@
+using Quadarax.Foundation.Core.Exceptions;
+
+namespace Quadarax.Application.QLiberace.Common
+{
+    public class ExceptionLibrary : ExceptionCodes
+    {
+
+        protected override void OnSetupCodes()
+        {
+            AddCode(ExceptionsCodes.SettingsNotFoundCode, ExceptionsCodes.SettingsNotFoundMsg);
+            AddCode(ExceptionsCodes.SettingsNotFoundForModuleCode, ExceptionsCodes.SettingsNotFoundForModuleMsg);
+            AddCode(ExceptionsCodes.SettingsHasNoPropertyCode, ExceptionsCodes.SettingsHasNoPropertyMsg);
+            AddCode(ExceptionsCodes.SettingsAlreadyExistsCode, ExceptionsCodes.SettingsAlreadyExistsMsg);
+        }
+
+
+        public static class ExceptionsCodes
+        {
+            public const string SettingsNotFoundMsg = "Setting '{0}' not found fo module '{1}'";
+            public const int SettingsNotFoundCode = 10000;
+            public const string SettingsNotFoundForModuleMsg = "Any setting not found fo module '{0}'";
+            public const int SettingsNotFoundForModuleCode = 10001;
+            public const string SettingsHasNoPropertyMsg = "Setting '{0}' has no defined property in class '{1}'";
+            public const int SettingsHasNoPropertyCode = 10002;
+            public const string SettingsAlreadyExistsMsg = "Setting '{0}' for module '{1}' already exists.";
+            public const int SettingsAlreadyExistsCode = 10003;
+        }
+
+    }
+}

+ 32 - 2
Common/qdr.app.qlbrc.common/Settings/ModuleSetting.cs

@@ -1,6 +1,36 @@
-namespace Quadarax.Application.QLiberace.Common.Settings
+using System.Reflection;
+using Quadarax.Foundation.Core.Data.Interface.Entity;
+using Quadarax.Foundation.Core.Object.Extensions;
+
+namespace Quadarax.Application.QLiberace.Common.Settings
 {
-    public abstract class ModuleSetting
+    public abstract class ModuleSetting : IDto
     {
+        public void SetData(IEnumerable<Tuple<string, string, object?>> data)
+        {
+            if (data == null)
+                throw new ArgumentNullException(nameof(data));
+
+            var properties = this.GetType().GetProperties(BindingFlags.Public);
+            foreach (var item in data)
+            {
+                var property = properties.FirstOrDefault(x => string.Equals(x.Name, item.Item1));
+                if (property == null)
+                    throw ExceptionFactory.CreateException(ExceptionLibrary.ExceptionsCodes.SettingsHasNoPropertyCode,
+                        item.Item1, this.GetType().Name);
+
+                property.SetValue(this, item.Item3);
+            }
+
+        }
+
+        public IEnumerable<Tuple<string, string, object?>> GetData()
+        {
+            var properties = this.GetType().GetProperties(BindingFlags.Public);
+            var result = this.GetAllPropertyValues().
+                Select(x=> new Tuple<string, string, object?>(x.Key, properties.First(y=>y.Name == x.Key).PropertyType.Name, properties.First(y=>y.Name == x.Key).GetValue(this)));
+
+            return result;
+        }
     }
 }

+ 22 - 0
Common/qdr.fnd.core.business/AbstractService.cs

@@ -1,9 +1,11 @@
 using System;
+using System.Linq;
 using System.Security.Principal;
 using Microsoft.Extensions.Logging;
 using Quadarax.Foundation.Core.Business.Interface;
 using Quadarax.Foundation.Core.Data.Interface.Entity;
 using Quadarax.Foundation.Core.Data.Interface.Entity.Dto;
+using Quadarax.Foundation.Core.Value;
 
 namespace Quadarax.Foundation.Core.Business
 {
@@ -29,6 +31,26 @@ namespace Quadarax.Foundation.Core.Business
         }
         #endregion
 
+        #region *** Private Operations ***
+        protected TResult TryCatchBlock<TResult>(Func<TResult> fnc) where TResult : ResultDto
+        {
+            if (fnc == null)
+                return null;
+            var result = default(TResult);
+
+            try
+            {
+                result = fnc();
+            }
+            catch (Exception e)
+            {
+                result.AppendException(e);
+            }
+
+            return result;
+        }
+        #endregion
+
         #region *** Public Operations ***
         public IResult Test()
         {

+ 5 - 1
Common/qdr.fnd.core.data.itfc/Entity/Dto/ErrorMessageDto.cs

@@ -14,6 +14,8 @@ namespace Quadarax.Foundation.Core.Data.Interface.Entity.Dto
         /// </summary>
         public int Code { get; set; }
 
+        public string CallStack { get; set; }
+
         public ErrorMessageDto(){}
         public ErrorMessageDto(Exception exception, int code)
         {
@@ -22,15 +24,17 @@ namespace Quadarax.Foundation.Core.Data.Interface.Entity.Dto
 
             Message = exception.Message;
             Code = code;
+            CallStack = exception.StackTrace;
         }
 
-        public ErrorMessageDto(CodeException exception)
+        public ErrorMessageDto(Exception exception)
         {
             if (exception==null)
                 throw new ArgumentNullException(nameof(exception));
 
             Message = exception.Message;
             Code = exception.GetCode();
+            CallStack = exception.StackTrace;
         }
     }
 }

+ 10 - 0
Common/qdr.fnd.core.data.itfc/Entity/Dto/ResultDto.cs

@@ -34,6 +34,16 @@ namespace Quadarax.Foundation.Core.Data.Interface.Entity.Dto
         {
             Errors = new List<ErrorMessageDto>();
         }
+
+        public void AppendException(Exception e)
+        {
+            if (e == null) throw new ArgumentNullException(nameof(e));
+            if (e is CodeException)
+            {
+                Errors = Errors.Append(new ErrorMessageDto(e));
+            }
+        }
+
         public AggregateException ToAggregateException()
         {
             if (IsSuccess)

+ 43 - 3
Common/qdr.fnd.core.data.itfc/Entity/Dto/ResultValueDto.cs

@@ -8,8 +8,7 @@ namespace Quadarax.Foundation.Core.Data.Interface.Entity.Dto
 {
     public class ResultValueDto<TDto> : ResultDto where TDto : IDto, new()
     {
-        [JsonPropertyName("value")]
-        public TDto Value { get; set; }
+        [JsonPropertyName("value")] public TDto Value { get; set; }
 
 
         public ResultValueDto(TDto value) : this()
@@ -21,11 +20,17 @@ namespace Quadarax.Foundation.Core.Data.Interface.Entity.Dto
         {
             Value = new TDto();
         }
+
         public ResultValueDto(CodeException[] exceptions) : base(exceptions.Select(x => new ErrorMessageDto(x)))
         {
         }
 
-        public ResultValueDto(Exception exception) : base( new []{ exception is CodeException codeException ? new ErrorMessageDto(codeException) : new ErrorMessageDto(exception, -1)})
+        public ResultValueDto(Exception exception) : base(new[]
+        {
+            exception is CodeException codeException
+                ? new ErrorMessageDto(codeException)
+                : new ErrorMessageDto(exception, -1)
+        })
         {
         }
 
@@ -33,9 +38,44 @@ namespace Quadarax.Foundation.Core.Data.Interface.Entity.Dto
         {
             return Value;
         }
+
         public TDto GetValueAs()
         {
             return Value;
         }
+
+
+    }
+    public class ResultValueDto: ResultDto
+    {
+        [JsonPropertyName("value")]
+        public object Value { get; set; }
+
+
+        public ResultValueDto(object value) : this()
+        {
+            Value = value;
+        }
+
+        public ResultValueDto() : base(new List<IError>())
+        {
+            Value = null;
+        }
+        public ResultValueDto(CodeException[] exceptions) : base(exceptions.Select(x => new ErrorMessageDto(x)))
+        {
+        }
+
+        public ResultValueDto(Exception exception) : base( new []{ exception is CodeException codeException ? new ErrorMessageDto(codeException) : new ErrorMessageDto(exception, -1)})
+        {
+        }
+
+        public override object GetValue()
+        {
+            return Value;
+        }
+        public object GetValueAs()
+        {
+            return Value;
+        }
     }
 }

+ 25 - 0
Common/qdr.fnd.core.data.itfc/Entity/Dto/ResultsValueDto.cs

@@ -27,4 +27,29 @@ namespace Quadarax.Foundation.Core.Data.Interface.Entity.Dto
             return Values;
         }
     }
+
+    public class ResultsValueDto : ResultDto
+    {
+        [JsonPropertyName("values")]
+        public IEnumerable<object> Values { get; set; }
+
+
+        public ResultsValueDto()
+        {
+            Values = new List<object>();
+        }
+        public ResultsValueDto(IEnumerable<object> values)
+        {
+            Values = values;
+        }
+
+        public override object GetValue()
+        {
+            return Values;
+        }
+        public IEnumerable<object> GetValueAs()
+        {
+            return Values;
+        }
+    }
 }

+ 16 - 0
Common/qdr.fnd.core/Attributes/CodeMessageExceptionItemAttribute.cs

@@ -0,0 +1,16 @@
+using System;
+
+namespace Quadarax.Foundation.Core.Attributes
+{
+    [AttributeUsage(validOn:AttributeTargets.Field)]
+    public class CodeMessageExceptionItemAttribute : Attribute
+    {
+        public int Code { get; }
+
+
+        public CodeMessageExceptionItemAttribute(int code)
+        {
+            Code = code;
+        }
+    }
+}

+ 57 - 18
Common/qdr.fnd.core/Exceptions/CodeException.cs

@@ -1,18 +1,57 @@
-using System;
-
-namespace Quadarax.Foundation.Core.Exceptions
-{
-    [Serializable]
-    public class CodeException : Exception
-    {
-        public CodeException(int code, string message, Exception innerException) : base(message, innerException)
-        {
-            this.SetCode(code);
-        }
-
-        public CodeException(int code, string message) : base(message)
-        {
-            this.SetCode(code);
-        }
-    }
-}
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using Quadarax.Foundation.Core.Attributes;
+using Quadarax.Foundation.Core.Reflection;
+
+namespace Quadarax.Foundation.Core.Exceptions
+{
+    [Serializable]
+    public class CodeException : Exception
+    {
+
+        private static IDictionary<int, string> _codeMessageCache;
+
+        public CodeException(int code, string message, Exception innerException) : base(message, innerException)
+        {
+            this.SetCode(code);
+        }
+
+        public CodeException(int code, string message) : base(message)
+        {
+            this.SetCode(code);
+        }
+
+        public CodeException(int code) : base()
+        {
+            this.SetCode(code);
+
+        }
+
+
+        public static void InitMessageCache(object exceptionCodesStaticClass)
+        {
+            if (_codeMessageCache == null)
+                _codeMessageCache = new Dictionary<int, string>();
+
+
+            var consts = exceptionCodesStaticClass.GetType().GetFields(BindingFlags.Public | BindingFlags.Static);
+            foreach (FieldInfo con in consts)
+            {
+                var attr = AttributeQuery<CodeMessageExceptionItemAttribute>.Get(con);
+                if (attr != null)
+                {
+                    if (!_codeMessageCache.ContainsKey(attr.Code))
+                    {
+                        _codeMessageCache.Add(attr.Code, con.GetValue(exceptionCodesStaticClass)?.ToString());
+                    }
+                    else
+                    {
+                        _codeMessageCache[attr.Code] = con.GetValue(exceptionCodesStaticClass)?.ToString();
+                    }
+                }
+            }
+
+        }
+    }
+}

+ 71 - 0
Common/qdr.fnd.core/Exceptions/ExceptionCodes.cs

@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Quadarax.Foundation.Core.Exceptions
+{
+    public abstract class ExceptionCodes
+    {
+
+        private readonly IDictionary<int, CodeMessageMap> _exceptionCodesCache = new Dictionary<int, CodeMessageMap>();
+
+
+        protected ExceptionCodes()
+        {
+            _exceptionCodesCache.Add(int.MaxValue, new CodeMessageMap(int.MaxValue, "Exception with code '{0}' not found. Cannot create."));
+            OnSetupCodes();
+        }
+
+        protected abstract void OnSetupCodes();
+        
+        protected void AddCode(int code, string message)
+        {
+            if (ExistsCode(code))
+                throw new InvalidOperationException($"Exception code '{code}' with message '{message}' already exists in cache.");
+
+            _exceptionCodesCache.Add(code, new CodeMessageMap(code, message));
+        }
+
+        protected bool ExistsCode(int code)
+        {
+            return _exceptionCodesCache.ContainsKey(code);
+        }
+
+        protected string GetMessage(int code, params string[] messageParams)
+        {
+            return string.Format(_exceptionCodesCache[code].Message, messageParams);
+        }
+
+        public CodeException CreateException(int code, params string[] messageParams)
+        {
+            return CreateException(code, null, messageParams);
+        }
+        public CodeException CreateException(int code, Exception innerException, params string[] messageParams)
+        {
+            if (!ExistsCode(code)) return new CodeException(int.MaxValue, GetMessage(code, code.ToString()));
+
+            return new CodeException(code, GetMessage(code, messageParams));
+        }
+
+        private class CodeMessageMap
+        {
+            public int Code { get;}
+            public string Message { get; }
+
+            public CodeMessageMap(int code, string message)
+            {
+                if (string.IsNullOrEmpty(message)) throw new ArgumentNullException(nameof(message));
+
+                Code = code;
+                Message = message;
+            }
+
+            public override string ToString()
+            {
+                var sb = new StringBuilder();
+                sb.Append(Code).Append(";").Append(Message);
+                return base.ToString();
+            }
+        }
+    }
+}

+ 28 - 2
Common/qdr.fnd.core/Object/Extensions/ObjectExt.cs

@@ -1,7 +1,7 @@
-using System.Collections;
+using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
-using System.Reflection;
 using Quadarax.Foundation.Core.Reflection.Extensions;
 
 namespace Quadarax.Foundation.Core.Object.Extensions
@@ -40,5 +40,31 @@ namespace Quadarax.Foundation.Core.Object.Extensions
             return props;
         }
 
+        
+        public static void SetAllPropertyValues(this object obj,IDictionary<string, object> values,string indentPrefix = null, bool silent = false)
+        {
+            if (obj == null)
+                return;
+            if (values == null || values.Count == 0)
+                return;
+
+            if (string.IsNullOrEmpty(indentPrefix))
+                indentPrefix = string.Empty;
+
+            var type = obj.GetType();
+            var propList = type.GetAllProperties();
+            foreach (var val in values)
+            {
+                var propName = val.Key;
+
+                var prop = propList.FirstOrDefault(x => x.Name == val.Key);
+                if (prop == null)
+                    if (!silent)
+                        throw new InvalidOperationException(
+                        $"Cannot locate property '{val.Key}' in class '{obj.GetType().Name}'.");
+                prop.SetValue(obj,val);
+            }
+        }
+        
     }
 }

+ 25 - 0
Modules/qdr.app.qlbrc.base/Dtos/ResultSettingsValuesListDto.cs

@@ -0,0 +1,25 @@
+using Quadarax.Foundation.Core.Data.Interface.Entity.Dto;
+
+namespace Quadarax.Application.QLiberace.Base.Dtos
+{
+    internal class ResultSettingsValuesListDto : ResultDto
+    {
+        public IDictionary<string, object?> Values { get; set; }
+
+
+        public ResultSettingsValuesListDto()
+        {
+            Values = new Dictionary<string, object?>();
+        }
+
+        public ResultSettingsValuesListDto(IDictionary<string, object?> values)
+        {
+            Values = values;
+        }
+
+        public override object GetValue()
+        {
+            return Values;
+        }
+    }
+}

+ 3 - 3
Modules/qdr.app.qlbrc.base/Entities/Interfaces/IStructSetting.cs

@@ -6,8 +6,8 @@ public interface IStructSetting
     int SequenceNo { get; set; }
     string ModuleCode { get; set; }
     string Code { get; set; }
-    string Description { get; set; }
+    string? Description { get; set; }
     string ValueTypeQualified { get; set; }
-    string Value { get; set; }
-    string IsEnabled { get; set; }
+    string? Value { get; set; }
+    bool IsEnabled { get; set; }
 }

+ 40 - 4
Modules/qdr.app.qlbrc.base/Entities/Setting.cs

@@ -1,19 +1,55 @@
-using Quadarax.Application.QLiberace.Base.Entities.Interfaces;
+using System.ComponentModel;
+using Quadarax.Application.QLiberace.Base.Entities.Interfaces;
 using Quadarax.Application.QLiberace.Common.Entities.Base;
 using System.ComponentModel.DataAnnotations.Schema;
+using Quadarax.Foundation.Core.Reflection;
 
 namespace Quadarax.Application.QLiberace.Base.Entities;
 
 [Table(Constants.Modules.Base.TblSetting,Schema = Constants.Modules.Base.Schema)]
 public class Setting : QlbrcEntityTrackedWithoutId, IStructSetting
 {
+    #region *** Properties ***
     public long ParentId { get; set; }
     public int SequenceNo { get; set; }
     public string ModuleCode { get; set; }
     public string Code { get; set; }
-    public string Description { get; set; }
+    public string? Description { get; set; }
 
     public string ValueTypeQualified { get; set; }
-    public string Value { get; set; }
-    public string IsEnabled { get; set; }
+    public string? Value { get; set; }
+    public bool IsEnabled { get; set; }
+    #endregion
+
+    #region *** Private fields ***
+
+    #endregion
+
+
+    public TValue? GetTypedValue<TValue>()
+    {
+        return (TValue?)GetTypedValue();
+    }
+
+    public object? GetTypedValue()
+    {
+
+        if (string.IsNullOrEmpty(ValueTypeQualified))
+            throw new InvalidDataException(
+                $"Cannot get typed value of setting '{Code}' (module '{ModuleCode}') because ValueTypeQualified is not set.");
+
+
+        if (string.IsNullOrEmpty(Value))
+            return null;
+
+        var type = TypeUtils.GetRemoteType(ValueTypeQualified);
+        var converter = TypeDescriptor.GetConverter(type);
+        if (converter.CanConvertFrom(typeof(string)))
+        {
+            return converter.ConvertFrom(Value);
+        }
+
+        throw new InvalidCastException($"Cannot convert value of setting '{Code}' (module '{ModuleCode}') from type '{typeof(string).Name}' to '{ValueTypeQualified}'.");
+    }
+
 }

+ 26 - 0
Modules/qdr.app.qlbrc.base/Repositories/RepoSetting.cs

@@ -1,6 +1,8 @@
 using Quadarax.Application.QLiberace.Base.Entities;
+using Quadarax.Foundation.Core.Data;
 using Quadarax.Foundation.Core.Data.Repository;
 using Quadarax.Foundation.Core.Data.Domain;
+using Quadarax.Foundation.Core.Data.Interface;
 using Quadarax.Foundation.Core.Data.Interface.Domain;
 
 namespace Quadarax.Application.QLiberace.Base.Repositories
@@ -9,6 +11,30 @@ namespace Quadarax.Application.QLiberace.Base.Repositories
     {
         public RepoSetting(IUnitOfWork<DataDomain> unitOfWork) : base(unitOfWork)
         {
+            
+        }
+
+
+        public Setting? Get(string moduleCode, string valueCode)
+        {
+            var setting = QueryModule(moduleCode, Paging.NoPaging()).FirstOrDefault(x => x.Code == valueCode);
+            return setting;
+        }
+
+        public override IQueryable<Setting> Query(IPaging paging, bool usingUncommited = false)
+        {
+            return base.Query(paging, usingUncommited).Where(x=>x.IsEnabled);
+        }
+
+        public IQueryable<Setting> QueryModule(string moduleCode, IPaging paging)
+        {
+            return Query(paging).Where(x => x.ModuleCode == moduleCode);
+        }
+
+        public string? GetValue(string moduleCode, string valueCode)
+        {
+            var setting = QueryModule(moduleCode, Paging.NoPaging()).FirstOrDefault(x => x.Code == valueCode);
+            return setting?.Value;
         }
     }
 }

+ 193 - 4
Modules/qdr.app.qlbrc.base/Services/SettingsService.cs

@@ -1,9 +1,16 @@
 using System.Security.Principal;
 using Microsoft.Extensions.Logging;
+using Quadarax.Application.QLiberace.Base.Dtos;
+using Quadarax.Application.QLiberace.Base.Entities;
 using Quadarax.Application.QLiberace.Base.Repositories;
+using Quadarax.Application.QLiberace.Common;
+using Quadarax.Application.QLiberace.Common.Attributes;
 using Quadarax.Application.QLiberace.Common.Settings;
 using Quadarax.Foundation.Core.Business;
+using Quadarax.Foundation.Core.Data;
+using Quadarax.Foundation.Core.Data.Interface;
 using Quadarax.Foundation.Core.Data.Interface.Entity.Dto;
+using Quadarax.Foundation.Core.Reflection;
 
 namespace Quadarax.Application.QLiberace.Base.Services
 {
@@ -19,47 +26,229 @@ namespace Quadarax.Application.QLiberace.Base.Services
 
         public ResultDto CreateSettings<TSettings>(ModuleSetting settings) where TSettings : ModuleSetting
         {
+            if (settings == null)
+                throw new ArgumentNullException(nameof(settings));
 
+            return TryCatchBlock(() =>
+            {
+                var attrs = AttributeQuery<ModuleAttribute>.FindAll(typeof(TSettings), true);
+                if (attrs.Length == 0)
+                    throw new InvalidOperationException(
+                        $"ModuleAttribute is not defined on class '{typeof(TSettings).Name}'.");
+                if (attrs.Length > 1)
+                    throw new InvalidOperationException(
+                        "ModuleAttribute is defined on more than one ModuleSetting class.");
+
+
+                var settingsFlat = settings.GetData();
+                foreach (var flat in settingsFlat)
+                {
+                    var setting = Repository.New();
+                    setting.Code = flat.Item1;
+                    setting.ModuleCode = attrs[0].ModuleCode;
+                    setting.IsEnabled = true;
+                    setting.Description = string.Empty;
+                    setting.ValueTypeQualified = flat.Item2;
+                    setting.Value = flat.Item3?.ToString();
+                    Repository.Set(setting);
+                }
+
+                Repository.Commit();
+                return new ResultPlain();
+            });
         }
-        public ResultValueDto<TSettings> GetSettings<TSettings>(string moduleCode) where TSettings : ModuleSetting
+
+        public ResultValueDto<TSettings> GetSettings<TSettings>(string moduleCode) where TSettings : ModuleSetting, new()
         {
+            if (string.IsNullOrEmpty(moduleCode)) throw new ArgumentNullException(nameof(moduleCode));
+
+            return TryCatchBlock(() =>
+            {
+                if (!Repository.QueryModule(moduleCode, Paging.NoPaging()).Any()) 
+                    throw ExceptionFactory.CreateException(ExceptionLibrary.ExceptionsCodes.SettingsNotFoundForModuleCode, moduleCode);
+                
+                var settingDto = default(TSettings);
+                if (settingDto == null) throw new ArgumentNullException(nameof(settingDto));
+
+                var settings = Repository.QueryModule(moduleCode,Paging.NoPaging());
+
+                settingDto.SetData(settings.Select(x=>new Tuple<string, string, object?>(x.Code,x.ValueTypeQualified,x.GetTypedValue())));
+
+                return new ResultValueDto<TSettings>(settingDto);
+            });
 
         }
 
-        public ResultValueDto<TValue> GetSettingsValue<TValue>(string moduleCode, string valueCode)
+        public ResultValueDto GetSettingsValue<TValue>(string moduleCode, string valueCode)
         {
+            if (string.IsNullOrEmpty(moduleCode)) throw new ArgumentNullException(nameof(moduleCode));
+            if (string.IsNullOrEmpty(valueCode)) throw new ArgumentNullException(nameof(valueCode));
+
+            return TryCatchBlock(() =>
+            {
+                var settings = GetSettings(moduleCode, valueCode);
+
+                if (settings == null)
+                    throw ExceptionFactory.CreateException(ExceptionLibrary.ExceptionsCodes.SettingsNotFoundCode, valueCode, moduleCode);
+
+                return new ResultValueDto(settings.GetTypedValue<TValue>());
+
+            });
         }
 
         public ResultBoolDto ExistsSettings(string moduleCode)
         {
+            if (string.IsNullOrEmpty(moduleCode)) throw new ArgumentNullException(nameof(moduleCode));
+
+            return TryCatchBlock(() =>
+            {
+                var result = Repository.QueryModule(moduleCode, Paging.NoPaging()).Any();
+                return new ResultBoolDto(result);
+            });
         }
 
         public ResultBoolDto ExistsSettingsValue(string moduleCode, string valueCode)
         {
+            if (string.IsNullOrEmpty(moduleCode)) throw new ArgumentNullException(nameof(moduleCode));
+            if (string.IsNullOrEmpty(valueCode)) throw new ArgumentNullException(nameof(valueCode));
+
+            return TryCatchBlock(() =>
+            {
+                var result = Repository.QueryModule(moduleCode, Paging.NoPaging()).Any(x => x.Code == valueCode);
+                return new ResultBoolDto(result);
+            });
         }
 
-        public ResultDto AddSettingsValue(string moduleCode, string valueCode, object value)
+        public ResultDto AddSettingsValue(string moduleCode, string valueCode, string valueDescription, object value)
         {
+            if (string.IsNullOrEmpty(moduleCode)) throw new ArgumentNullException(nameof(moduleCode));
+            if (string.IsNullOrEmpty(valueCode)) throw new ArgumentNullException(nameof(valueCode));
+
+            return TryCatchBlock(() =>
+            {
+                var exists = ExistsSettingsValue(moduleCode, valueCode);
+                if (!exists.IsSuccess) return (ResultDto)exists;
+
+                if (exists.Value)
+                    throw ExceptionFactory.CreateException(ExceptionLibrary.ExceptionsCodes.SettingsAlreadyExistsCode, valueCode, moduleCode);
+
+                var settings = Repository.New();
+
+                settings.ModuleCode = moduleCode;
+                settings.Code = valueCode;
+                settings.Description = valueDescription;
+                settings.Value = value?.ToString();
+                settings.IsEnabled = true;
+                Repository.Commit();
+                return new ResultPlain();
+            });
         }
 
         public ResultDto UpdateSettings<TSettings>(ModuleSetting settings) where TSettings : ModuleSetting
         {
+            if (settings == null)
+                throw new ArgumentNullException(nameof(settings));
+
+            return TryCatchBlock(() =>
+            {
+                var attrs = AttributeQuery<ModuleAttribute>.FindAll(typeof(TSettings), true);
+                if (attrs.Length == 0)
+                    throw new InvalidOperationException(
+                        $"ModuleAttribute is not defined on class '{typeof(TSettings).Name}'.");
+                if (attrs.Length > 1)
+                    throw new InvalidOperationException(
+                        "ModuleAttribute is defined on more than one ModuleSetting class.");
+
+                var settingsFlat = settings.GetData();
+                foreach (var flat in settingsFlat)
+                {
+                    var setting = GetSettings(attrs[0].ModuleCode, flat.Item1);
 
+                    if (setting == null)
+                        throw ExceptionFactory.CreateException(ExceptionLibrary.ExceptionsCodes.SettingsNotFoundCode, flat.Item1, attrs[0].ModuleCode);
+                    Repository.Set(setting);
+                }
+
+                Repository.Commit();
+                return new ResultPlain();
+            });
         }
 
         public ResultDto UpdateSettingsValue(string moduleCode, string valueCode, object value)
         {
+            if (string.IsNullOrEmpty(moduleCode)) throw new ArgumentNullException(nameof(moduleCode));
+            if (string.IsNullOrEmpty(valueCode)) throw new ArgumentNullException(nameof(valueCode));
+
+            return TryCatchBlock(() =>
+            {
+                var exists = ExistsSettingsValue(moduleCode, valueCode);
+                if (!exists.IsSuccess) return (ResultDto)exists;
+
+                if (!exists.Value)
+                    throw ExceptionFactory.CreateException(ExceptionLibrary.ExceptionsCodes.SettingsNotFoundCode, valueCode, moduleCode);
+
+                var settings = Repository.Get(moduleCode, valueCode);
+                if (settings==null)
+                    throw ExceptionFactory.CreateException(ExceptionLibrary.ExceptionsCodes.SettingsNotFoundCode, valueCode, moduleCode);
+
+                settings.Value = value?.ToString();
+                Repository.Commit();
+                
+                return new ResultPlain();
+            });
+
         }
 
         public ResultDto AddOrUpdateSettingsValue(string moduleCode, string valueCode, object value)
         {
+            return AddOrUpdateSettingsValue(moduleCode, valueCode, value, string.Empty);
         }
 
-        public ResultValueDto<Dictionary<string,object>> GetSettingsValues(string moduleCode)
+        public ResultDto AddOrUpdateSettingsValue(string moduleCode, string valueCode, object value, string valueDescription)
         {
+            if (string.IsNullOrEmpty(moduleCode)) throw new ArgumentNullException(nameof(moduleCode));
+            if (string.IsNullOrEmpty(valueCode)) throw new ArgumentNullException(nameof(valueCode));
+
+            return TryCatchBlock(() =>
+            {
+                var exists = ExistsSettingsValue(moduleCode, valueCode);
+                if (!exists.IsSuccess) return (ResultDto)exists;
+
+                if (exists.Value)
+                {
+                    return UpdateSettingsValue(moduleCode, valueCode, value);
+                }
+                else
+                {
+                    return AddSettingsValue(moduleCode, valueCode, valueDescription, value);
+                }
+            });
+        }
+
+        public ResultSettingsValuesListDto GetSettingsValues(string moduleCode, IPaging paging)
+        {
+            if (string.IsNullOrEmpty(moduleCode)) throw new ArgumentNullException(nameof(moduleCode));
+            return TryCatchBlock(() =>
+            {
+
+                var result = Repository.QueryModule(moduleCode, paging)
+                    .ToDictionary(x => x.Code, y => y.GetTypedValue<object>());
+
+                return new ResultSettingsValuesListDto(result);
+            });
         }
         #endregion
 
+        #region ** Private Operations ***
+
+        private Setting? GetSettings(string moduleCode, string valueCode)
+        {
+            var settings = Repository.Get(moduleCode, valueCode);
+
+            return settings;
+        }
+
+        #endregion
         
     }
 }

+ 3 - 1
Modules/qdr.app.qlbrc.base/Settings/BaseSetting.cs

@@ -1,7 +1,9 @@
-using Quadarax.Application.QLiberace.Common.Settings;
+using Quadarax.Application.QLiberace.Common.Attributes;
+using Quadarax.Application.QLiberace.Common.Settings;
 
 namespace Quadarax.Application.QLiberace.Base.Settings
 {
+    [Module(Constants.Modules.Base.Code)]
     internal class BaseSetting : ModuleSetting
     {
     }

+ 1 - 0
qdr.app.qlbrc.console/Program.cs

@@ -18,6 +18,7 @@ using (var ctx = new QlbrcDbContext(options))
 }
 */
 
+
 var config = new StartupConfiguration(Quadarax.Application.QLiberace.Console.Constants.General.Name, Assembly.GetExecutingAssembly().GetName().Version);
 config.ConsoleCopyright = Assembly.GetExecutingAssembly().GetCopyright();
 config.AllowInteractive = false;