From 4b71836feaa22223a203ce86cff3b88cd9cac0ba Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Tue, 30 Apr 2024 17:01:26 +0200 Subject: [PATCH] Erweiterung der DTOs und Implementierung der Lokalisierungsdienste MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Neue DTO-Extension-Methoden hinzugefügt, um die Verarbeitung und Zuweisung von Nachrichten und Benachrichtigungen in Ergebnisobjekten zu vereinfachen. - Lokalisierungsunterstützung in der API-Schicht implementiert, einschließlich Cookie-basierter Lokalisierung und Konfiguration unterstützter Kulturen. - Die Integration von StringLocalizer in die API-Schicht wurde durchgeführt, um eine nahtlose Mehrsprachigkeit zu ermöglichen. - Fehlerbehandlung für fehlende Konfigurationseinstellungen verbessert. Die Änderungen verbessern die Flexibilität und Wartbarkeit des Codes und unterstützen eine effizientere Internationalisierung der Anwendung. --- DigitalData.Core.API/ControllerExtensions.cs | 7 +- .../DigitalData.Core.API.csproj | 1 + .../LocalizationExtensions.cs | 4 +- DigitalData.Core.Application/CRUDService.cs | 60 ++--- DigitalData.Core.Application/DIExtensions.cs | 22 -- .../DigitalData.Core.Application.csproj | 4 + .../DirectorySearchService.cs | 16 +- .../EnumerableExtensoins.cs | 23 -- DigitalData.Core.Application/Key.cs | 11 + .../LoggerExtensions.cs | 41 ---- DigitalData.Core.Application/MessageKey.cs | 17 -- .../ResponseService.cs | 211 ------------------ DigitalData.Core.Application/ServiceBase.cs | 16 -- .../ServiceMessage.cs | 138 ------------ .../ServiceMessageExtensions.cs | 153 ------------- DigitalData.Core.Application/ServiceResult.cs | 55 ----- DigitalData.Core.Application/To.cs | 43 ---- .../Application/ICRUDService.cs | 37 +-- .../Application/IDirectorySearchService.cs | 9 +- .../Application/IResponseService.cs | 185 --------------- .../Application/IServiceBase.cs | 6 - .../Application/IServiceMessage.cs | 95 -------- .../Application/IServiceResult.cs | 19 -- .../DigitalData.Core.Contracts.csproj | 4 + DigitalData.Core.Contracts/IFlag.cs | 7 + .../AutoMapperExtension.cs | 2 +- .../CookieConsentSettings.cs | 2 +- DigitalData.Core.DTO/DIExtensions.cs | 22 ++ DigitalData.Core.DTO/DTOExtensions.cs | 110 +++++++++ DigitalData.Core.DTO/DataResult.cs | 10 + .../DigitalData.Core.DTO.csproj | 17 ++ .../Flag.cs | 2 +- DigitalData.Core.DTO/Notice.cs | 11 + DigitalData.Core.DTO/Result.cs | 40 ++++ .../CRUDRepository.cs | 6 + DigitalData.Core.sln | 6 + 36 files changed, 303 insertions(+), 1109 deletions(-) delete mode 100644 DigitalData.Core.Application/EnumerableExtensoins.cs create mode 100644 DigitalData.Core.Application/Key.cs delete mode 100644 DigitalData.Core.Application/LoggerExtensions.cs delete mode 100644 DigitalData.Core.Application/MessageKey.cs delete mode 100644 DigitalData.Core.Application/ResponseService.cs delete mode 100644 DigitalData.Core.Application/ServiceBase.cs delete mode 100644 DigitalData.Core.Application/ServiceMessage.cs delete mode 100644 DigitalData.Core.Application/ServiceMessageExtensions.cs delete mode 100644 DigitalData.Core.Application/ServiceResult.cs delete mode 100644 DigitalData.Core.Application/To.cs delete mode 100644 DigitalData.Core.Contracts/Application/IResponseService.cs delete mode 100644 DigitalData.Core.Contracts/Application/IServiceBase.cs delete mode 100644 DigitalData.Core.Contracts/Application/IServiceMessage.cs delete mode 100644 DigitalData.Core.Contracts/Application/IServiceResult.cs create mode 100644 DigitalData.Core.Contracts/IFlag.cs rename {DigitalData.Core.Application => DigitalData.Core.DTO}/AutoMapperExtension.cs (96%) rename {DigitalData.Core.Application/DTO => DigitalData.Core.DTO}/CookieConsentSettings.cs (98%) create mode 100644 DigitalData.Core.DTO/DIExtensions.cs create mode 100644 DigitalData.Core.DTO/DTOExtensions.cs create mode 100644 DigitalData.Core.DTO/DataResult.cs create mode 100644 DigitalData.Core.DTO/DigitalData.Core.DTO.csproj rename {DigitalData.Core.Application => DigitalData.Core.DTO}/Flag.cs (97%) create mode 100644 DigitalData.Core.DTO/Notice.cs create mode 100644 DigitalData.Core.DTO/Result.cs diff --git a/DigitalData.Core.API/ControllerExtensions.cs b/DigitalData.Core.API/ControllerExtensions.cs index 7e02ea3..4009711 100644 --- a/DigitalData.Core.API/ControllerExtensions.cs +++ b/DigitalData.Core.API/ControllerExtensions.cs @@ -1,4 +1,5 @@ using DigitalData.Core.Contracts.Application; +using DigitalData.Core.DTO; using Microsoft.AspNetCore.Mvc; using System.Text; @@ -50,10 +51,8 @@ namespace DigitalData.Core.API /// Returns an ObjectResult representing an internal server error (status code 500) with optional exception and message details. /// /// The ControllerBase instance representing the controller. - /// Optional. The exception that occurred, if any. - /// Optional. A custom error message to include in the response. - /// /// Optional. A custom error message key to include in the response. + /// Optional. A custom error resul to include in the response. /// An ObjectResult representing an internal server error (status code 500). - public static ObjectResult InnerServiceError(this ControllerBase controllerBase, IServiceMessage? serviceMessage = null) => controllerBase.StatusCode(500, serviceMessage); + public static ObjectResult InnerServiceError(this ControllerBase controllerBase, Result? result = null) => controllerBase.StatusCode(500, result); } } \ No newline at end of file diff --git a/DigitalData.Core.API/DigitalData.Core.API.csproj b/DigitalData.Core.API/DigitalData.Core.API.csproj index 97b79bd..5cd5d03 100644 --- a/DigitalData.Core.API/DigitalData.Core.API.csproj +++ b/DigitalData.Core.API/DigitalData.Core.API.csproj @@ -13,6 +13,7 @@ + diff --git a/DigitalData.Core.API/LocalizationExtensions.cs b/DigitalData.Core.API/LocalizationExtensions.cs index 49befb9..20f548f 100644 --- a/DigitalData.Core.API/LocalizationExtensions.cs +++ b/DigitalData.Core.API/LocalizationExtensions.cs @@ -20,10 +20,10 @@ namespace DigitalData.Core.API public static IServiceCollection AddCookieBasedLocalizer(this IServiceCollection services, string resourcesPath) { // Adds localization services with the specified resources path. - services.AddLocalization(options => options.ResourcesPath = resourcesPath); + services.AddLocalization(options => options.ResourcesPath = resourcesPath) // Adds MVC services with view localization and data annotations localization. - services.AddMvc().AddViewLocalization(Microsoft.AspNetCore.Mvc.Razor.LanguageViewLocationExpanderFormat.Suffix) + .AddMvc().AddViewLocalization(Microsoft.AspNetCore.Mvc.Razor.LanguageViewLocationExpanderFormat.Suffix) .AddDataAnnotationsLocalization(); return services; diff --git a/DigitalData.Core.Application/CRUDService.cs b/DigitalData.Core.Application/CRUDService.cs index ab9c290..70e04a8 100644 --- a/DigitalData.Core.Application/CRUDService.cs +++ b/DigitalData.Core.Application/CRUDService.cs @@ -4,6 +4,7 @@ using AutoMapper; using System.Reflection; using System.ComponentModel.DataAnnotations; using Microsoft.Extensions.Localization; +using DigitalData.Core.DTO; namespace DigitalData.Core.Application { @@ -15,26 +16,29 @@ namespace DigitalData.Core.Application /// The DTO type for update operations. /// The entity type. /// The type of the identifier for the entity. - public class CRUDService : ServiceBase, ICRUDService, IServiceBase + public class CRUDService : ICRUDService where TCRUDRepository : ICRUDRepository where TCreateDto : class where TReadDto : class where TUpdateDto : class where TEntity : class { protected readonly TCRUDRepository _repository; protected readonly IMapper _mapper; protected readonly PropertyInfo? _keyPropertyInfo; + protected readonly IStringLocalizer _localizer; /// /// Initializes a new instance of the CRUDService class with the specified repository, translation service, and mapper. /// /// The CRUD repository for accessing the database. - /// The service for translating messages based on culture. + /// The localizer for translating messages based on culture. /// The AutoMapper instance for mapping between DTOs and entity objects. - public CRUDService(TCRUDRepository repository, IStringLocalizer defaultLocalizer, IMapper mapper) : base(defaultLocalizer) + public CRUDService(TCRUDRepository repository, IStringLocalizer localizer, IMapper mapper) { _repository = repository; _mapper = mapper; _keyPropertyInfo = typeof(TEntity).GetProperties() .FirstOrDefault(prop => Attribute.IsDefined(prop, typeof(KeyAttribute))); + + _localizer = localizer; } /// @@ -42,14 +46,11 @@ namespace DigitalData.Core.Application /// /// The DTO to create an entity from. /// A service result indicating success or failure, including the entity DTO. - public virtual async Task> CreateAsync(TCreateDto createDto) + public virtual async Task> CreateAsync(TCreateDto createDto) { var entity = _mapper.MapOrThrow(createDto); var createdEntity = await _repository.CreateAsync(entity); - if (createdEntity is null) - return Failed(); - else - return Successful(KeyValueOf(createdEntity)); + return createdEntity is null ? Result.Fail() : Result.Success(KeyValueOf(createdEntity)); } /// @@ -57,27 +58,23 @@ namespace DigitalData.Core.Application /// /// The identifier of the entity to read. /// A service result indicating success or failure, including the read DTO if successful. - public virtual async Task> ReadByIdAsync(TId id) + public virtual async Task> ReadByIdAsync(TId id) { var entity = await _repository.ReadByIdAsync(id); - if (entity is null) - { - var translatedMessage = MessageKey.EntityDoesNotExist.LocalizedBy(_localizer); - return Failed(); - } - else - return Successful(_mapper.MapOrThrow(entity)); + return entity is null + ? Result.Fail().Message(_localizer[Key.EntityDoesNotExist]) + : Result.Success(_mapper.MapOrThrow(entity)); } /// /// Asynchronously reads all entities and maps them to read DTOs. /// /// A service result including a collection of read DTOs. - public virtual async Task>> ReadAllAsync() + public virtual async Task>> ReadAllAsync() { var entities = await _repository.ReadAllAsync(); var readDto = _mapper.MapOrThrow>(entities); - return Successful(readDto); + return Result.Success(readDto); } /// @@ -85,17 +82,11 @@ namespace DigitalData.Core.Application /// /// The DTO to update an entity from. /// A service message indicating success or failure. - public virtual async Task UpdateAsync(TUpdateDto updateDto) + public virtual async Task UpdateAsync(TUpdateDto updateDto) { var entity = _mapper.MapOrThrow(updateDto); bool isUpdated = await _repository.UpdateAsync(entity); - if (isUpdated) - return Successful(); - else - { - var translatedMessage = MessageKey.UpdateFailed.LocalizedBy(_localizer); - return Failed(); - } + return isUpdated ? Result.Success() : Result.Fail().Message(_localizer[Key.UpdateFailed]); } /// @@ -103,26 +94,15 @@ namespace DigitalData.Core.Application /// /// The identifier of the entity to delete. /// A service message indicating success or failure. - public virtual async Task DeleteAsyncById(TId id) + public virtual async Task DeleteAsyncById(TId id) { TEntity? entity = await _repository.ReadByIdAsync(id); if (entity is null) - { - var deletionFailedMessage = MessageKey.DeletionFailed.LocalizedBy(_localizer); - var entityDoesNotExistMessage = MessageKey.EntityDoesNotExist.LocalizedBy(_localizer); - return new ServiceMessage(isSuccess: false, deletionFailedMessage, entityDoesNotExistMessage); - } + return Result.Fail().Message(_localizer[Key.DeletionFailed], _localizer[Key.EntityDoesNotExist]); bool isDeleted = await _repository.DeleteAsync(entity); - - if (isDeleted) - return Successful(); - else - { - var deletionFailedMessage = MessageKey.DeletionFailed.LocalizedBy(_localizer); - return Failed(deletionFailedMessage); - } + return isDeleted ? Result.Success() : Result.Fail().Message(_localizer[Key.DeletionFailed]); } /// diff --git a/DigitalData.Core.Application/DIExtensions.cs b/DigitalData.Core.Application/DIExtensions.cs index 45078ce..2000121 100644 --- a/DigitalData.Core.Application/DIExtensions.cs +++ b/DigitalData.Core.Application/DIExtensions.cs @@ -1,5 +1,4 @@ using AutoMapper; -using DigitalData.Core.Application.DTO; using DigitalData.Core.Contracts.Application; using DigitalData.Core.Contracts.Infrastructure; using Microsoft.Extensions.Configuration; @@ -66,30 +65,9 @@ namespace DigitalData.Core.Application return service; } - public static IServiceCollection AddResponseService(this IServiceCollection service) - { - service.AddScoped(); - return service; - } - public static IServiceCollection AddJWTService(this IServiceCollection services, Func tokenDescriptorFactory) { return services.AddScoped, JWTService>(provider => new (tokenDescriptorFactory)); } - - public static IServiceCollection AddCookieConsentSettings(this IServiceCollection services) - { - services.AddSingleton(sp => - { - var configuration = sp.GetRequiredService(); - var settings = configuration.GetSection("CookieConsentSettings").Get(); - if (settings is null) - { - throw new ConfigurationErrorsException("The 'CookieConsentSettings' section is missing or improperly configured in appsettings.json."); - } - return settings; - }); - return services; - } } } \ No newline at end of file diff --git a/DigitalData.Core.Application/DigitalData.Core.Application.csproj b/DigitalData.Core.Application/DigitalData.Core.Application.csproj index c47f59a..f59d953 100644 --- a/DigitalData.Core.Application/DigitalData.Core.Application.csproj +++ b/DigitalData.Core.Application/DigitalData.Core.Application.csproj @@ -21,4 +21,8 @@ + + + + diff --git a/DigitalData.Core.Application/DirectorySearchService.cs b/DigitalData.Core.Application/DirectorySearchService.cs index a45159c..8293a11 100644 --- a/DigitalData.Core.Application/DirectorySearchService.cs +++ b/DigitalData.Core.Application/DirectorySearchService.cs @@ -6,11 +6,12 @@ using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; using System.DirectoryServices.AccountManagement; using Microsoft.Extensions.Localization; +using DigitalData.Core.DTO; namespace DigitalData.Core.Application { [SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "")] - public class DirectorySearchService : ServiceBase, IDirectorySearchService + public class DirectorySearchService : IDirectorySearchService { private readonly IMemoryCache _memoryCache; public string ServerName { get; } @@ -18,8 +19,9 @@ namespace DigitalData.Core.Application public string SearchRootPath { get; } private readonly DateTimeOffset _userCacheExpiration; public Dictionary CustomSearchFilters { get; } + protected readonly IStringLocalizer _localizer; - public DirectorySearchService(IConfiguration configuration, ILogger logger, IMemoryCache memoryCache, IStringLocalizer defaultLocalizer) : base(defaultLocalizer) + public DirectorySearchService(IConfiguration configuration, ILogger logger, IMemoryCache memoryCache, IStringLocalizer localizer) { _memoryCache = memoryCache; @@ -37,6 +39,8 @@ namespace DigitalData.Core.Application _userCacheExpiration = default; else _userCacheExpiration = DateTimeOffset.Now.Date.AddDays(dayCounts); + + _localizer = localizer; } public bool ValidateCredentials(string dirEntryUsername, string dirEntryPassword) @@ -45,7 +49,7 @@ namespace DigitalData.Core.Application return context.ValidateCredentials(dirEntryUsername, dirEntryPassword); } - public IServiceResult> FindAll(DirectoryEntry searchRoot, string filter, SearchScope searchScope = SearchScope.Subtree, int sizeLimit = 5000, params string[] properties) + public DataResult> FindAll(DirectoryEntry searchRoot, string filter, SearchScope searchScope = SearchScope.Subtree, int sizeLimit = 5000, params string[] properties) { List list = new(); @@ -71,17 +75,17 @@ namespace DigitalData.Core.Application list.Add(rpc); } - return Successful>(list); + return Result.Success>(list); } - public IServiceResult> FindAllByUserCache(string username, string filter, SearchScope searchScope = SearchScope.Subtree, int sizeLimit = 5000, params string[] properties) + public DataResult> FindAllByUserCache(string username, string filter, SearchScope searchScope = SearchScope.Subtree, int sizeLimit = 5000, params string[] properties) { List list = new(); _memoryCache.TryGetValue(username, out DirectoryEntry? searchRoot); if (searchRoot is null) - return Failed>(MessageKey.DirSearcherDisconnected.ToString()); + return Result.Fail>().Message(_localizer[Key.DirSearcherDisconnected]); return FindAll(searchRoot, filter, searchScope, sizeLimit, properties); } diff --git a/DigitalData.Core.Application/EnumerableExtensoins.cs b/DigitalData.Core.Application/EnumerableExtensoins.cs deleted file mode 100644 index e3863af..0000000 --- a/DigitalData.Core.Application/EnumerableExtensoins.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace DigitalData.Core.Application -{ - /// - /// Provides extension methods for IEnumerable. - /// - public static class EnumerableExtensions - { - /// - /// Concatenates the members of a collection, using the specified separator between each member. - /// - /// The type of the elements of the enumerable. - /// The IEnumerable whose elements to concatenate. - /// The string to use as a separator. Separator is included in the returned string only between elements of the collection. - /// A string that consists of the elements in the collection delimited by the separator string. If the collection is empty, the method returns String.Empty. - public static string Join(this IEnumerable enumerable, string separator = ". ") => string.Join(separator, enumerable); - } -} \ No newline at end of file diff --git a/DigitalData.Core.Application/Key.cs b/DigitalData.Core.Application/Key.cs new file mode 100644 index 0000000..d8e352f --- /dev/null +++ b/DigitalData.Core.Application/Key.cs @@ -0,0 +1,11 @@ +namespace DigitalData.Core.Application +{ + public static class Key + { + public static readonly string EntityDoesNotExist = "EntityDoesNotExist"; + public static readonly string ReadFailed = "ReadFailed"; + public static readonly string UpdateFailed = "UpdateFailed"; + public static readonly string DeletionFailed = "DeletionFailed"; + public static readonly string DirSearcherDisconnected = "DirSearcherDisconnected"; + } +} \ No newline at end of file diff --git a/DigitalData.Core.Application/LoggerExtensions.cs b/DigitalData.Core.Application/LoggerExtensions.cs deleted file mode 100644 index d18a435..0000000 --- a/DigitalData.Core.Application/LoggerExtensions.cs +++ /dev/null @@ -1,41 +0,0 @@ -using DigitalData.Core.Contracts.Application; -using Microsoft.Extensions.Logging; - -namespace DigitalData.Core.Application -{ - /// - /// Provides extension methods for ILogger to handle logging of message collections and service messages more effectively. - /// - public static class LoggerExtensions - { - /// - /// Logs a list of messages at the specified log level. - /// - /// The extended ILogger instance. - /// The severity level of the log entry. - /// The collection of messages to log. - /// The separator used to join messages. Default is newline. - /// Additional arguments used for formatting the log message. - public static void LogMessageList(this ILogger logger, LogLevel logLevel, IEnumerable messages, string separator = "\n", params object?[] args) - { - if (messages.Any()) - logger.Log(logLevel: logLevel, message: string.Join(separator, messages), args); - } - - /// - /// Logs all messages from a service message instance, categorized by message type to appropriate log levels. - /// - /// The extended ILogger instance. - /// The service message instance containing categorized messages. - /// The separator used to join messages within each category. Default is newline. - public static void LogServiceMessage(this ILogger logger, IServiceMessage serviceMessage, string separator = "\n") - { - logger.LogMessageList(LogLevel.Trace, serviceMessage.TraceMessages, separator); - logger.LogMessageList(LogLevel.Debug, serviceMessage.DebugMessages, separator); - logger.LogMessageList(LogLevel.Information, serviceMessage.InformationMessages, separator); - logger.LogMessageList(LogLevel.Warning, serviceMessage.WarningMessages, separator); - logger.LogMessageList(LogLevel.Error, serviceMessage.ErrorMessages, separator); - logger.LogMessageList(LogLevel.Critical, serviceMessage.CriticalMessages, separator); - } - } -} \ No newline at end of file diff --git a/DigitalData.Core.Application/MessageKey.cs b/DigitalData.Core.Application/MessageKey.cs deleted file mode 100644 index 8fa7767..0000000 --- a/DigitalData.Core.Application/MessageKey.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace DigitalData.Core.Application -{ - public enum MessageKey - { - EntityDoesNotExist, - ReadFailed, - UpdateFailed, - DeletionFailed, - DirSearcherDisconnected - } -} \ No newline at end of file diff --git a/DigitalData.Core.Application/ResponseService.cs b/DigitalData.Core.Application/ResponseService.cs deleted file mode 100644 index 4ccd1c9..0000000 --- a/DigitalData.Core.Application/ResponseService.cs +++ /dev/null @@ -1,211 +0,0 @@ -using DigitalData.Core.Contracts.Application; -using Microsoft.Extensions.Localization; - -namespace DigitalData.Core.Application -{ - public class ResponseService : IResponseService - { - protected readonly IStringLocalizer _localizer; - - public ResponseService(IStringLocalizer defaultLocalizer) - { - _localizer = defaultLocalizer; - } - - #region WITHOUT_MESSAGE - - /// - /// Creates a service message indicating success or failure without additional messages. - /// - /// Indicates if the operation was successful. - /// A service message reflecting the operation outcome. - public IServiceMessage CreateMessage(bool isSuccess = false) => new ServiceMessage() - { - IsSuccess = isSuccess - }; - - /// - /// Creates a service result containing the provided data and a success flag, without additional messages. - /// - /// The type of the data included in the result. - /// The data to include in the result. - /// Indicates if the operation was successful. - /// A service result with the specified data and outcome. - public IServiceResult CreateResult(T? data = default, bool isSuccess = false) => new ServiceResult() - { - IsSuccess = isSuccess, - Data = data - }; - - /// - /// Creates a service message indicating a successful operation without additional messages. - /// - /// A service message indicating a successful operation. - public IServiceMessage Successful() => CreateMessage(true); - - /// - /// Creates a service message indicating a failed operation without additional messages. - /// - /// A service message indicating a failed operation. - public IServiceMessage Failed() => CreateMessage(false); - - /// - /// Creates a successful service result with the specified data, without additional messages. - /// - /// The type of data included in the result. - /// The data to include in the result. - /// A successful service result containing the specified data. - public IServiceResult Successful(T data) => CreateResult(data, true); - - /// - /// Creates a failed service result with optional data, without additional messages. - /// - /// The type of data the service result can contain. - /// Optional data to include in the result. - /// A failed service result, which may or may not contain the specified data. - public IServiceResult Failed(T? data = default) => CreateResult(data, false); - - #endregion - #region WITH_STRING_MESSAGE - [Obsolete("Deprecated: Use ClientMessages instead.")] - /// - /// Creates a service message with the specified success flag and messages. - /// - /// Indicates if the operation was successful. - /// An array of messages associated with the operation. - /// A new instance of reflecting the operation outcome. - public virtual IServiceMessage CreateMessage(bool isSuccess, params string[] messages) => new ServiceMessage(isSuccess, messages); - - [Obsolete("Deprecated: Use ClientMessages instead.")] - /// - /// Creates a service result containing the provided data, success flag, and messages. - /// - /// The type of the data included in the result. - /// The data to include in the result. - /// Indicates if the operation was successful. - /// An array of messages associated with the operation. - /// A new instance of with the specified data and outcome. - public virtual IServiceResult CreateResult(T? data = default, bool isSuccess = true, params string[] messages) => new ServiceResult(data, isSuccess, messages); - - [Obsolete("Deprecated: Use ClientMessages instead.")] - /// - /// Creates a successful service message. - /// - /// An array of success messages. - /// A successful service message. - public virtual IServiceMessage Successful(params string[] messages) => CreateMessage(true, messages); - - [Obsolete("Deprecated: Use ClientMessages instead.")] - /// - /// Creates a failed service message. - /// - /// An array of failure messages. - /// A failed service message. - public virtual IServiceMessage Failed(params string[] messages) => CreateMessage(false, messages); - - [Obsolete("Deprecated: Use ClientMessages instead.")] - /// - /// Creates a successful service result with the provided data. - /// - /// The type of data included in the result. - /// The data to include in the result. - /// An array of success messages. - /// A successful service result containing the specified data. - public virtual IServiceResult Successful(T data, params string[] messages) => CreateResult(data, true, messages); - - [Obsolete("Deprecated: Use ClientMessages instead.")] - /// - /// Creates a failed service result, optionally including data. - /// - /// The type of data the service result can contain. - /// Optional data to include in the failed result. - /// An array of failure messages. - /// A failed service result, which may or may not contain the specified data. - public virtual IServiceResult Failed(T? data = default, params string[] messages) => CreateResult(data, false, messages); - - [Obsolete("Deprecated: Use ClientMessages instead.")] - /// - /// Creates a failed service result using only failure messages, without explicitly including data. - /// - /// - /// This method provides a convenient way to create a failed service result when the failure does not pertain to any specific data, - /// or when the inclusion of data is not necessary to convey the failure reason. The data part of the result will be set to its default value. - /// - /// The type of data the service result can contain. The result will contain the default value for this type. - /// An array of failure messages that provide details about the reasons for the operation's failure. - /// A failed service result. The data part of the result will be set to the default value for the specified type, - /// and it will include the provided failure messages. - public virtual IServiceResult Failed(params string[] messages) => Failed(default, messages); - - #endregion - #region WITH_ENUM_MESSAGE - - [Obsolete("Deprecated: Use ClientMessages instead.")] - /// - /// Creates a service message with the specified success flag and enumeration messages. - /// - /// Indicates if the operation was successful. - /// An array of enumeration values associated with the operation. - /// A new instance of reflecting the operation outcome with enumeration messages. - public IServiceMessage CreateMessage(bool isSuccess, params Enum[] messages) => CreateMessage(isSuccess, messages.Select(m => m.ToString()).ToArray()); - - [Obsolete("Deprecated: Use ClientMessages instead.")] - /// - /// Creates a service result containing the provided data, success flag, and enumeration messages. - /// - /// The type of the data included in the result. - /// The data to include in the result. - /// Indicates if the operation was successful. - /// An array of enumeration values associated with the operation. - /// A new instance of with the specified data and outcome using enumeration messages. - public IServiceResult CreateResult(T? data, bool isSuccess, params Enum[] messages) => CreateResult(data, isSuccess, messages.Select(m => m.ToString()).ToArray()); - - [Obsolete("Deprecated: Use ClientMessages instead.")] - /// - /// Creates a successful service message using enumeration messages. - /// - /// An array of success enumeration values. - /// A successful service message. - public IServiceMessage Successful(params Enum[] messages) => CreateMessage(true, messages.Select(m => m.ToString()).ToArray()); - - [Obsolete("Deprecated: Use ClientMessages instead.")] - /// - /// Creates a failed service message using enumeration messages. - /// - /// An array of failure enumeration values. - /// A failed service message. - public IServiceMessage Failed(params Enum[] messages) => CreateMessage(false, messages.Select(m => m.ToString()).ToArray()); - - [Obsolete("Deprecated: Use ClientMessages instead.")] - /// - /// Creates a successful service result with the provided data using enumeration messages. - /// - /// The type of data included in the result. - /// The data to include in the result. - /// An array of success enumeration values. - /// A successful service result containing the specified data. - public IServiceResult Successful(T data, params Enum[] messages) => CreateResult(data, true, messages.Select(m => m.ToString()).ToArray()); - - [Obsolete("Deprecated: Use ClientMessages instead.")] - /// - /// Creates a failed service result, optionally including data, using enumeration messages. - /// - /// The type of data the service result can contain. - /// Optional data to include in the failed result. - /// An array of failure enumeration values. - /// A failed service result, which may or may not contain the specified data. - public IServiceResult Failed(T? data = default, params Enum[] messages) => CreateResult(data, false, messages.Select(m => m.ToString()).ToArray()); - - [Obsolete("Deprecated: Use ClientMessages instead.")] - /// - /// Creates a failed service result using only failure messages, without explicitly including data. - /// - /// The type of data the service result can contain. The result will contain the default value for this type. - /// An array of failure enumeration values that provide details about the reasons for the operation's failure. - /// A failed service result. The data part of the result will be set to the default value for the specified type, - /// and it will include the provided failure messages. - public IServiceResult Failed(params Enum[] messages) => Failed(default(T), messages); - - #endregion - } -} \ No newline at end of file diff --git a/DigitalData.Core.Application/ServiceBase.cs b/DigitalData.Core.Application/ServiceBase.cs deleted file mode 100644 index 73c8ca6..0000000 --- a/DigitalData.Core.Application/ServiceBase.cs +++ /dev/null @@ -1,16 +0,0 @@ -using AutoMapper; -using DigitalData.Core.Contracts.Application; -using Microsoft.Extensions.Localization; - -namespace DigitalData.Core.Application -{ - /// - /// Provides a base implementation of . - /// - public class ServiceBase : ResponseService, IServiceBase, IResponseService - { - public ServiceBase(IStringLocalizer defaultLocalizer) : base(defaultLocalizer) - { - } - } -} \ No newline at end of file diff --git a/DigitalData.Core.Application/ServiceMessage.cs b/DigitalData.Core.Application/ServiceMessage.cs deleted file mode 100644 index 96bedb1..0000000 --- a/DigitalData.Core.Application/ServiceMessage.cs +++ /dev/null @@ -1,138 +0,0 @@ -using DigitalData.Core.Contracts.Application; -using System.Text.Json.Serialization; - -namespace DigitalData.Core.Application -{ - /// - /// Represents the outcome of a service operation, encapsulating the success or failure status, - /// and any associated messages. It also supports optional translation of message keys for localization purposes. - /// - public class ServiceMessage : IServiceMessage - { - /// - /// Initializes a new instance of the ServiceMessage class. - /// - public ServiceMessage() - { - } - - /// - /// Initializes a new instance of the ServiceMessage class, specifying the success status. - /// Optionally, a function for translating message keys can be provided. - /// If a translation function is provided, it will be used for both string and enum message keys. - /// - /// Indicates whether the service operation was successful. - /// If provided, it will also be adapted for translating enum keys by converting them to strings first. - [Obsolete("Deprecated: initialize objects using object initializer.")] - public ServiceMessage(bool isSuccess) - { - IsSuccess = isSuccess; - } - - /// - /// Initializes a new instance of the ServiceMessage class with specified success status, and messages. - /// - /// Indicates whether the service operation was successful. - /// An array of messages related to the operation's outcome. - [Obsolete("Deprecated: initialize objects using object initializer.")] - public ServiceMessage(bool isSuccess, params string[] messages) - { - IsSuccess = isSuccess; - Messages = messages.ToList(); - } - - /// - /// Gets or sets a value indicating whether the service operation was successful. - /// - public bool IsSuccess { get; set; } = false; - - /// - /// Represents the list of flags that indicate specific types of statuses or conditions in a service operation. - /// These flags help in categorizing the state of the operation more granularly, allowing for multiple conditions to be represented simultaneously. - /// - [JsonIgnore] - public ICollection Flags { get; } = new List(); - - /// - /// Checks if any of the current service message's flags matches the specified flag based on their string representations. - /// This method is useful for conditional logic where the exact string representation of the flag values is crucial. - /// - /// The flag to check against the current service message's flags. - /// true if a flag with a matching string representation exists; otherwise, false. - public bool HasFlag(Enum flag) => Flags.Any(f => f.ToString() == flag.ToString()); - - /// - /// [Obsolete("Deprecated: Use ClientMessages instead.")] - /// Gets a collection of messages associated with the service operation. These messages can be error descriptions, - /// success notifications, or other relevant information related to the operation's outcome. - /// - [Obsolete("Deprecated: Use ClientMessages instead.")] - public ICollection Messages { get; init; } = new List(); - - /// - /// Gets a collection of messages intended for client display. This replaces the deprecated 'Messages' property. - /// - public ICollection ClientMessages { get; init; } = new List(); - - /// - /// Gets a collection of messages used for tracing program execution at a fine-grained level. These are typically voluminous and detailed. - /// - [JsonIgnore] - public ICollection TraceMessages { get; init; } = new List(); - - /// - /// Gets a collection of messages helpful for debugging during development. These messages are often diagnostic. - /// - [JsonIgnore] - public ICollection DebugMessages { get; init; } = new List(); - - /// - /// Gets a collection of informational messages, less critical than warnings, generally used for non-critical notifications. - /// - [JsonIgnore] - public ICollection InformationMessages { get; init; } = new List(); - - /// - /// Gets a collection of messages indicating potential issues that are not necessarily errors, but which may require attention. - /// - [JsonIgnore] - public ICollection WarningMessages { get; init; } = new List(); - - /// - /// Gets a collection of error messages indicating failures or problems within the service. - /// - [JsonIgnore] - public ICollection ErrorMessages { get; init; } = new List(); - - /// - /// Gets a collection of messages indicating critical issues that require immediate attention. - /// - [JsonIgnore] - public ICollection CriticalMessages { get; init; } = new List(); - - /// - /// Adds a new message to the collection of messages associated with the service operation. - /// - /// The message to add. - /// The current instance of ServiceMessage, allowing for method chaining. - [Obsolete("Deprecated: Use ClientMessages instead.")] - public IServiceMessage WithMessage(string message) - { - Messages.Add(message); - return this; - } - - /// - /// Adds a message corresponding to the specified message key to the collection of messages associated with the service operation. - /// This method uses the string representation of the enum value as the message. - /// - /// The enum value representing the message key. - /// The current instance of ServiceMessage, allowing for method chaining. - [Obsolete("Deprecated: Use ClientMessages instead.")] - public IServiceMessage WithMessageKey(Enum messageKey) - { - Messages.Add(messageKey.ToString()); - return this; - } - } -} \ No newline at end of file diff --git a/DigitalData.Core.Application/ServiceMessageExtensions.cs b/DigitalData.Core.Application/ServiceMessageExtensions.cs deleted file mode 100644 index 4fa7858..0000000 --- a/DigitalData.Core.Application/ServiceMessageExtensions.cs +++ /dev/null @@ -1,153 +0,0 @@ -using DigitalData.Core.Contracts.Application; -using Microsoft.Extensions.Localization; - -namespace DigitalData.Core.Application -{ - /// - /// Provides extension methods for IServiceMessage to enhance the usability of service messages - /// by allowing easy addition of various types of messages including client, trace, debug, information, - /// warning, error, and critical messages. These methods support both direct messages and enum-based keys - /// for messages, facilitating localized or custom-formatted messages. - /// - public static class ServiceMessageExtensions - { - #region Flag - /// - /// Sets the specified flag on the service message or result, allowing for the categorization of the message based on specific conditions or statuses. - /// - /// The type of IServiceMessage. - /// The service message instance to modify. - /// The flag to set, indicating a specific condition or status. - /// The service message instance with the updated flag, facilitating fluent chaining of methods. - public static T WithFlag(this T serviceMessage, Enum flag) where T : IServiceMessage - { - serviceMessage.Flags.Add(flag); - return serviceMessage; - } - - /// - /// Determines whether the service message has a flag indicating a security breach. - /// - /// The service message instance to check. - /// True if the service message has the security breach flag; otherwise, false. - public static bool HasSecurityBreachFlag(this IServiceMessage serviceMessage) => serviceMessage.HasFlag(Flag.SecurityBreach); - - /// - /// Determines whether the service message has a flag indicating a data integrity issue. - /// - /// The service message instance to check. - /// True if the service message has the data integrity issue flag; otherwise, false. - public static bool HasDataIntegrityIssueFlag(this IServiceMessage serviceMessage) => serviceMessage.HasFlag(Flag.DataIntegrityIssue); - - /// - /// Determines whether the service message has a flag indicating either a security breach or a data integrity issue, or both. - /// This flag is used when it is not sure whether the problem is security or data integrity. In this case, data integrity should be checked first. - /// - /// The service message instance to check. - /// True if the service message has the flag indicating either or both issues; otherwise, false. - public static bool HasSecurityBreachOrDataIntegrityFlag(this IServiceMessage serviceMessage) => serviceMessage.HasFlag(Flag.SecurityBreachOrDataIntegrity); - #endregion - - #region StringLocalizer - /// - /// Retrieves the localized string for the specified key. - /// - /// The key for the localized string. - /// The localizer to use for retrieving the localized string. - /// The localized string associated with the specified key. - public static string LocalizedBy(this string key, IStringLocalizer localizer) => localizer[key]; - - /// - /// Retrieves the localized string for the specified enumeration key. - /// - /// The enumeration key for the localized string. - /// The localizer to use for retrieving the localized string. - /// The localized string associated with the specified enumeration key. - public static string LocalizedBy(this Enum key, IStringLocalizer localizer) => localizer[key.ToString()]; - #endregion - - #region ClientMessage - /// - /// Adds a single client message to the service message. - /// - /// The service message to modify. - /// The message to add. - /// The modified service message instance. - public static T WithClientMessage(this T serviceMessage, string message) where T : IServiceMessage - { - serviceMessage.ClientMessages.Add(message); - return serviceMessage; - } - #endregion - - #region TraceMessages - /// - /// Adds a trace message to the service message. - /// - /// The service message to modify. - /// The trace message to add. - /// The modified service message instance. - public static T WithTraceMessage(this T serviceMessage, string message) where T : IServiceMessage - { - serviceMessage.TraceMessages.Add(message); - return serviceMessage; - } - #endregion - - #region DebugMessages - /// - /// Adds a debug message to the service message. - /// - /// The service message to modify. - /// The debug message to add. - /// The modified service message instance. - public static T WithDebugMessage(this T serviceMessage, string message) where T : IServiceMessage - { - serviceMessage.DebugMessages.Add(message); - return serviceMessage; - } - #endregion - - #region WarningMessages - /// - /// Adds a warning message to the service message. - /// - /// The service message to modify. - /// The warning message to add. - /// The modified service message instance. - public static T WithWarningMessage(this T serviceMessage, string message) where T : IServiceMessage - { - serviceMessage.WarningMessages.Add(message); - return serviceMessage; - } - #endregion - - #region ErrorMessages - /// - /// Adds an error message to the service message. - /// - /// The service message to modify. - /// The error message to add. - /// The modified service message instance. - public static T WithErrorMessage(this T serviceMessage, string message) where T : IServiceMessage - { - serviceMessage.ErrorMessages.Add(message); - return serviceMessage; - } - #endregion - - #region CriticalMessages - /// - /// Adds a critical message to the service message. - /// - /// The service message to modify. - /// The critical message to add. - /// The modified service message instance. - public static T WithCriticalMessage(this T serviceMessage, string message) where T : IServiceMessage - { - serviceMessage.CriticalMessages.Add(message); - return serviceMessage; - } - #endregion - } -} \ No newline at end of file diff --git a/DigitalData.Core.Application/ServiceResult.cs b/DigitalData.Core.Application/ServiceResult.cs deleted file mode 100644 index 1f3ad81..0000000 --- a/DigitalData.Core.Application/ServiceResult.cs +++ /dev/null @@ -1,55 +0,0 @@ -using DigitalData.Core.Contracts.Application; - -namespace DigitalData.Core.Application -{ - /// - /// Represents the outcome of a service operation, encapsulating the success or failure status, - /// the data returned by the operation, and any associated messages. - /// - /// The type of data returned by the service operation, if any. - public class ServiceResult : ServiceMessage, IServiceResult - { - /// - /// Initializes a new instance of the ServiceResult class. - public ServiceResult() - { - } - - /// - /// Initializes a new instance of the ServiceResult class, specifying the success status is false and data. - /// Optionally, a function for translating message keys can be provided. - /// If a translation function is provided, it will be used for both string and enum message keys. - /// - /// The data associated with a successful operation. - /// A function that translates a string key into its localized representation. - /// If provided, it will also be adapted for translating enum keys by converting them to strings first. - [Obsolete("Deprecated: initialize objects using object initializers instead.")] - public ServiceResult(T? data) - { - Data = data; - IsSuccess = false; - } - - /// - /// Initializes a new instance of the ServiceResult class with specified success status and data. - /// - /// The data associated with a successful operation. - /// Indicates whether the service operation was successful. - [Obsolete("Deprecated: initialize objects using object initializers instead.")] - public ServiceResult(T? data, bool isSuccess) : base(isSuccess) => Data = data; - - /// - /// Initializes a new instance of the ServiceResult class with specified success status, data, and messages. - /// - /// The data associated with a successful operation. - /// Indicates whether the service operation was successful. - /// An array of messages related to the operation's outcome. - [Obsolete("Deprecated: Use ClientMessages instead.")] - public ServiceResult(T? data, bool isSuccess, params string[] messages) : base(isSuccess, messages) => Data = data; - - /// - /// Gets or sets the data resulting from the service operation. - /// - public T? Data { get; set; } = default; - } -} \ No newline at end of file diff --git a/DigitalData.Core.Application/To.cs b/DigitalData.Core.Application/To.cs deleted file mode 100644 index a10ea7a..0000000 --- a/DigitalData.Core.Application/To.cs +++ /dev/null @@ -1,43 +0,0 @@ -namespace DigitalData.Core.Application -{ - /// - /// Specifies the destination of a message. - /// - public enum To - { - /// - /// Indicates that the message is intended for the client. - /// - Client, - - /// - /// Indicates that the message is intended for tracing purposes. - /// - Trace, - - /// - /// Indicates that the message is intended for debugging purposes. - /// - Debug, - - /// - /// Indicates that the message is informational. - /// - Information, - - /// - /// Indicates that the message is a warning. - /// - Warning, - - /// - /// Indicates that the message represents an error. - /// - Error, - - /// - /// Indicates that the message represents a critical issue. - /// - Critical - } -} \ No newline at end of file diff --git a/DigitalData.Core.Contracts/Application/ICRUDService.cs b/DigitalData.Core.Contracts/Application/ICRUDService.cs index 24c54e8..43fac72 100644 --- a/DigitalData.Core.Contracts/Application/ICRUDService.cs +++ b/DigitalData.Core.Contracts/Application/ICRUDService.cs @@ -1,58 +1,43 @@ using DigitalData.Core.Contracts.Infrastructure; +using DigitalData.Core.DTO; namespace DigitalData.Core.Contracts.Application { - /// - /// Defines the contract for CRUD operations at the service level using Data Transfer Objects (DTOs) for entities of type TEntity, - /// wrapped in an IServiceResult to encapsulate the outcome of the operation, including success, data, and error messages. - /// - /// The repository type that provides CRUD operations for entities of type TEntity. - /// The type of the Data Transfer Object this service works with to create new entity. - /// The type of the Data Transfer Object this service works with to read new entity. - /// The type of the Data Transfer Object this service works with to update new entity. - /// The type of the entity this service maps to. - /// The type of the identifier for the entity. - public interface ICRUDService : IServiceBase + public interface ICRUDService where TCRUDRepository : ICRUDRepository where TCreateDto : class where TReadDto : class where TUpdateDto : class where TEntity : class { - /// - /// Creates a new entity based on the provided DTO and returns the result wrapped in an IServiceResult, - /// including the created entity on success or an error message on failure. - /// - /// The createDto to create a new entity from. - /// An IServiceResult containing the id of created entity or an error message. - Task> CreateAsync(TCreateDto createDto); + Task> CreateAsync(TCreateDto createDto); /// /// Retrieves an entity by its identifier and returns its readDTO representation wrapped in an IServiceResult, /// including the readDTO on success or null and an error message on failure. /// /// The identifier of the entity to retrieve. - /// An IServiceResult containing the readDTO representing the found entity or null, with an appropriate message. - Task> ReadByIdAsync(TId id); + /// An DataResult containing the readDTO representing the found entity or null, with an appropriate message. + Task> ReadByIdAsync(TId id); /// /// Retrieves all entities and returns their readDTO representations wrapped in an IServiceResult, /// including a collection of readDTOs on success or an error message on failure. /// - /// An IServiceResult containing a collection of readDTOs representing all entities or an error message. - Task>> ReadAllAsync(); + /// An DataResult containing a collection of readDTOs representing all entities or an error message. + Task>> ReadAllAsync(); /// /// Updates an existing entity based on the provided updateDTO and returns the result wrapped in an IServiceMessage, /// indicating the success or failure of the operation, including the error messages on failure. /// /// The updateDTO with updated values for the entity. - /// An IServiceMessage indicating the outcome of the update operation, with an appropriate message. - Task UpdateAsync(TUpdateDto updateDto); + /// An Result indicating the outcome of the update operation, with an appropriate message. + Task UpdateAsync(TUpdateDto updateDto); /// /// Deletes an entity by its identifier and returns the result wrapped in an IServiceMessage, /// indicating the success or failure of the operation, including the error messages on failure. /// /// The identifier of the entity to delete. - /// An IServiceMessage indicating the outcome of the delete operation, with an appropriate message. - Task DeleteAsyncById(TId id); + /// An Result indicating the outcome of the delete operation, with an appropriate message. + Task DeleteAsyncById(TId id); /// /// Asynchronously checks if an entity with the specified identifier exists within the data store. diff --git a/DigitalData.Core.Contracts/Application/IDirectorySearchService.cs b/DigitalData.Core.Contracts/Application/IDirectorySearchService.cs index 226827f..12f0c91 100644 --- a/DigitalData.Core.Contracts/Application/IDirectorySearchService.cs +++ b/DigitalData.Core.Contracts/Application/IDirectorySearchService.cs @@ -1,8 +1,9 @@ -using System.DirectoryServices; +using DigitalData.Core.DTO; +using System.DirectoryServices; namespace DigitalData.Core.Contracts.Application { - public interface IDirectorySearchService : IServiceBase + public interface IDirectorySearchService { public string ServerName { get; } @@ -14,9 +15,9 @@ namespace DigitalData.Core.Contracts.Application bool ValidateCredentials(string dirEntryUsername, string dirEntryPassword); - IServiceResult> FindAll(DirectoryEntry searchRoot, string filter, SearchScope searchScope = SearchScope.Subtree, int sizeLimit = 5000, params string[] properties); + DataResult> FindAll(DirectoryEntry searchRoot, string filter, SearchScope searchScope = SearchScope.Subtree, int sizeLimit = 5000, params string[] properties); - IServiceResult> FindAllByUserCache(string username, string filter, SearchScope searchScope = SearchScope.Subtree, int sizeLimit = 5000, params string[] properties); + DataResult> FindAllByUserCache(string username, string filter, SearchScope searchScope = SearchScope.Subtree, int sizeLimit = 5000, params string[] properties); void SetSearchRootCache(string username, string password); diff --git a/DigitalData.Core.Contracts/Application/IResponseService.cs b/DigitalData.Core.Contracts/Application/IResponseService.cs deleted file mode 100644 index d55f994..0000000 --- a/DigitalData.Core.Contracts/Application/IResponseService.cs +++ /dev/null @@ -1,185 +0,0 @@ -namespace DigitalData.Core.Contracts.Application -{ - /// - /// Defines the base functionality for a service, including creating service messages and results. - /// - public interface IResponseService - { - #region WITHOUT_MESSAGE - - /// - /// Creates a simple service message indicating success or failure without any additional messages. - /// - /// Indicates if the operation was successful. - /// A service message indicating the outcome of the operation without any messages. - IServiceMessage CreateMessage(bool isSuccess); - - /// - /// Creates a service result with the specified data and a success flag, without any additional messages. - /// - /// The type of data the service result will contain. - /// The data for the service result. - /// Indicates if the operation was successful. - /// A service result containing the data and indicating the outcome of the operation without any messages. - IServiceResult CreateResult(T? data, bool isSuccess); - - /// - /// Creates a service message indicating a successful operation without any messages. - /// - /// A service message indicating a successful operation. - IServiceMessage Successful(); - - /// - /// Creates a service message indicating a failed operation without any messages. - /// - /// A service message indicating a failed operation. - IServiceMessage Failed(); - - /// - /// Creates a successful service result with the specified data, without any messages. - /// - /// The type of data the service result will contain. - /// The data to include in the service result. - /// A successful service result containing the data, indicating a successful operation without any messages. - IServiceResult Successful(T data); - - /// - /// Creates a failed service result, optionally including data, without any messages. - /// - /// The type of data the service result can contain. - /// Optional data to include in the failed service result. - /// A failed service result, which may or may not contain data, indicating a failed operation without any messages. - IServiceResult Failed(T? data = default); - - #endregion - #region WITH_STRING_MESSAGE - - /// - /// Creates a service message. - /// - /// Indicates if the operation was successful. - /// The messages associated with the operation. - /// A service message indicating the outcome of the operation and any associated messages. - IServiceMessage CreateMessage(bool isSuccess, params string[] messages); - - /// - /// Creates a service result with the specified data. - /// - /// The type of data the service result will contain. - /// The data for the service result. - /// Indicates if the operation was successful. - /// The messages associated with the operation. - /// A service result containing the data and indicating the outcome of the operation. - IServiceResult CreateResult(T? data, bool isSuccess, params string[] messages); - - /// - /// Creates a successful service message. - /// - /// The success messages. - /// A successful service message. - IServiceMessage Successful(params string[] messages); - - /// - /// Creates a failed service message. - /// - /// The failure messages. - /// A failed service message. - IServiceMessage Failed(params string[] messages); - - /// - /// Creates a successful service result with the specified data. - /// - /// The type of data the service result will contain. - /// The data to include in the service result. - /// The success messages. - /// A successful service result containing the data. - IServiceResult Successful(T data, params string[] messages); - - /// - /// Creates a failed service result, optionally including data. - /// - /// The type of data the service result can contain. - /// Optional data to include in the failed service result. - /// The failure messages. - /// A failed service result, which may or may not contain data. - IServiceResult Failed(T? data = default, params string[] messages); - - /// - /// Creates a failed service result without explicitly including data, using only failure messages. - /// - /// - /// This overload is useful when you want to indicate a failure without the need to return any specific data, - /// but still want to provide details about the failure through messages. - /// - /// The type of data the service result can contain. The result will contain the default value for this type. - /// An array of failure messages associated with the operation. These provide detail about why the operation failed. - /// A failed service result. The data part of the result will be set to the default value for the specified type. - IServiceResult Failed(params string[] messages); - - #endregion - #region WITH_ENUM_MESSAGE - - /// - /// Creates a service message using enumeration values. - /// - /// Indicates if the operation was successful. - /// The enumeration values associated with the operation. - /// A service message indicating the outcome of the operation and any associated enumeration values. - IServiceMessage CreateMessage(bool isSuccess, params Enum[] messages); - - /// - /// Creates a service result with the specified data and enumeration values for messages. - /// - /// The type of data the service result will contain. - /// The data for the service result. - /// Indicates if the operation was successful. - /// The enumeration values associated with the operation. - /// A service result containing the data and indicating the outcome of the operation with enumeration values. - IServiceResult CreateResult(T? data, bool isSuccess, params Enum[] messages); - - /// - /// Creates a successful service message using enumeration values. - /// - /// The success enumeration values. - /// A successful service message. - IServiceMessage Successful(params Enum[] messages); - - /// - /// Creates a failed service message using enumeration values. - /// - /// The failure enumeration values. - /// A failed service message. - IServiceMessage Failed(params Enum[] messages); - - /// - /// Creates a successful service result with the specified data, using enumeration values for messages. - /// - /// The type of data the service result will contain. - /// The data to include in the service result. - /// The success enumeration values. - /// A successful service result containing the data. - IServiceResult Successful(T data, params Enum[] messages); - - /// - /// Creates a failed service result, optionally including data, using enumeration values for messages. - /// - /// The type of data the service result can contain. - /// Optional data to include in the failed service result. - /// The failure enumeration values. - /// A failed service result, which may or may not contain data. - IServiceResult Failed(T? data = default, params Enum[] messages); - - /// - /// Creates a failed service result without explicitly including data, using only enumeration values for failure messages. - /// - /// - /// This overload is useful when you want to indicate a failure without the need to return any specific data, - /// but still want to provide details about the failure through enumeration values. - /// - /// The type of data the service result can contain. The result will contain the default value for this type. - /// An array of enumeration values associated with the operation. These provide detail about why the operation failed. - /// A failed service result. The data part of the result will be set to the default value for the specified type. - IServiceResult Failed(params Enum[] messages); - #endregion - } -} \ No newline at end of file diff --git a/DigitalData.Core.Contracts/Application/IServiceBase.cs b/DigitalData.Core.Contracts/Application/IServiceBase.cs deleted file mode 100644 index f0eb10f..0000000 --- a/DigitalData.Core.Contracts/Application/IServiceBase.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace DigitalData.Core.Contracts.Application -{ - public interface IServiceBase : IResponseService - { - } -} \ No newline at end of file diff --git a/DigitalData.Core.Contracts/Application/IServiceMessage.cs b/DigitalData.Core.Contracts/Application/IServiceMessage.cs deleted file mode 100644 index 6641366..0000000 --- a/DigitalData.Core.Contracts/Application/IServiceMessage.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System.Text.Json.Serialization; - -namespace DigitalData.Core.Contracts.Application -{ - /// - /// Defines a structured format for service messages, categorizing them into success indicators and various types of messages. - /// This interface segregates messages into client-facing messages and different levels of logging messages, facilitating targeted communications and diagnostics. - /// Properties are initialized once per instance and cannot be modified afterwards, promoting immutability. - /// - public interface IServiceMessage - { - /// - /// Gets or sets a value indicating whether the service operation was successful. - /// - bool IsSuccess { get; set; } - - /// - /// Represents the list of flags that indicate specific types of statuses or conditions in a service operation. - /// These flags help in categorizing the state of the operation more granularly, allowing for multiple conditions to be represented simultaneously. - /// - [JsonIgnore] - public ICollection Flags { get; } - - /// - /// Checks if any of the current service message's flags matches the specified flag based on their string representations. - /// This method is useful for conditional logic where the exact string representation of the flag values is crucial. - /// - /// The flag to check against the current service message's flags. - /// true if a flag with a matching string representation exists; otherwise, false. - public bool HasFlag(Enum flag) => Flags.Any(f => f.ToString() == flag.ToString()); - - /// - /// [Obsolete("Deprecated: Use ClientMessages instead.")] - /// Gets a collection of messages associated with the service operation. These messages can be error descriptions, - /// success notifications, or other relevant information related to the operation's outcome. - /// - [Obsolete("Deprecated: Use ClientMessages instead.")] - ICollection Messages { get; init; } - - /// - /// Gets a collection of messages intended for client display. This replaces the deprecated 'Messages' property. - /// - ICollection ClientMessages { get; init; } - - /// - /// Gets a collection of messages used for tracing program execution at a fine-grained level. These are typically voluminous and detailed. - /// - [JsonIgnore] - ICollection TraceMessages { get; init; } - - /// - /// Gets a collection of messages helpful for debugging during development. These messages are often diagnostic. - /// - [JsonIgnore] - ICollection DebugMessages { get; init; } - - /// - /// Gets a collection of informational messages, less critical than warnings, generally used for non-critical notifications. - /// - [JsonIgnore] - ICollection InformationMessages { get; init; } - - /// - /// Gets a collection of messages indicating potential issues that are not necessarily errors, but which may require attention. - /// - [JsonIgnore] - ICollection WarningMessages { get; init; } - - /// - /// Gets a collection of error messages indicating failures or problems within the service. - /// - [JsonIgnore] - ICollection ErrorMessages { get; init; } - - /// - /// Gets a collection of messages indicating critical issues that require immediate attention. - /// - [JsonIgnore] - ICollection CriticalMessages { get; init; } - - /// - /// Adds a new message to the appropriate collection based on the message type. - /// - /// The message to add. - /// The current instance of IServiceMessage, allowing for method chaining. - IServiceMessage WithMessage(string message); - - /// - /// Adds a message corresponding to a specified message key to the appropriate collection, facilitating localization and standardization. - /// - /// The enum value representing the message key. - /// The current instance of IServiceMessage, allowing for method chaining. - IServiceMessage WithMessageKey(Enum messageKey); - } -} \ No newline at end of file diff --git a/DigitalData.Core.Contracts/Application/IServiceResult.cs b/DigitalData.Core.Contracts/Application/IServiceResult.cs deleted file mode 100644 index 43f2e1e..0000000 --- a/DigitalData.Core.Contracts/Application/IServiceResult.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace DigitalData.Core.Contracts.Application -{ - /// - /// Represents the outcome of a service operation, extending IServiceMessage with the addition of a data payload. - /// This interface is generic, allowing for the specification of the type of data returned by the service operation. - /// It is used to communicate not just the success or failure of an operation, but also to return any relevant data - /// along with the operation's outcome. - /// - /// The type of the data associated with the service operation's outcome. This could be a model, - /// a collection of models, or any other type relevant to the operation. - public interface IServiceResult : IServiceMessage - { - /// - /// Gets or sets the data resulting from the service operation. This property is nullable to accommodate operations - /// that might not return data upon failure. - /// - T? Data { get; set; } - } -} \ No newline at end of file diff --git a/DigitalData.Core.Contracts/DigitalData.Core.Contracts.csproj b/DigitalData.Core.Contracts/DigitalData.Core.Contracts.csproj index 864bace..e38fcb3 100644 --- a/DigitalData.Core.Contracts/DigitalData.Core.Contracts.csproj +++ b/DigitalData.Core.Contracts/DigitalData.Core.Contracts.csproj @@ -24,6 +24,10 @@ + + + + Never diff --git a/DigitalData.Core.Contracts/IFlag.cs b/DigitalData.Core.Contracts/IFlag.cs new file mode 100644 index 0000000..4e1aa0f --- /dev/null +++ b/DigitalData.Core.Contracts/IFlag.cs @@ -0,0 +1,7 @@ +namespace DigitalData.Core.Contracts +{ + internal interface IError + { + + } +} diff --git a/DigitalData.Core.Application/AutoMapperExtension.cs b/DigitalData.Core.DTO/AutoMapperExtension.cs similarity index 96% rename from DigitalData.Core.Application/AutoMapperExtension.cs rename to DigitalData.Core.DTO/AutoMapperExtension.cs index bb61521..44401ec 100644 --- a/DigitalData.Core.Application/AutoMapperExtension.cs +++ b/DigitalData.Core.DTO/AutoMapperExtension.cs @@ -1,6 +1,6 @@ using AutoMapper; -namespace DigitalData.Core.Application +namespace DigitalData.Core.DTO { public static class AutoMapperExtension { diff --git a/DigitalData.Core.Application/DTO/CookieConsentSettings.cs b/DigitalData.Core.DTO/CookieConsentSettings.cs similarity index 98% rename from DigitalData.Core.Application/DTO/CookieConsentSettings.cs rename to DigitalData.Core.DTO/CookieConsentSettings.cs index 9243cd1..004aad7 100644 --- a/DigitalData.Core.Application/DTO/CookieConsentSettings.cs +++ b/DigitalData.Core.DTO/CookieConsentSettings.cs @@ -1,4 +1,4 @@ -namespace DigitalData.Core.Application.DTO +namespace DigitalData.Core.DTO { /// /// Represents settings related to user cookie consent dialogs. Designed to be serialized into JSON format for use with JavaScript frontend libraries, diff --git a/DigitalData.Core.DTO/DIExtensions.cs b/DigitalData.Core.DTO/DIExtensions.cs new file mode 100644 index 0000000..7d51b46 --- /dev/null +++ b/DigitalData.Core.DTO/DIExtensions.cs @@ -0,0 +1,22 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using System.Configuration; + +namespace DigitalData.Core.DTO +{ + public static class DIExtensions + { + public static IServiceCollection AddCookieConsentSettings(this IServiceCollection services) + { + services.AddSingleton(sp => + { + var configuration = sp.GetRequiredService(); + var settings = configuration.GetSection("CookieConsentSettings").Get(); + return settings is null + ? throw new ConfigurationErrorsException("The 'CookieConsentSettings' section is missing or improperly configured in appsettings.json.") + : settings; + }); + return services; + } + } +} diff --git a/DigitalData.Core.DTO/DTOExtensions.cs b/DigitalData.Core.DTO/DTOExtensions.cs new file mode 100644 index 0000000..054aeac --- /dev/null +++ b/DigitalData.Core.DTO/DTOExtensions.cs @@ -0,0 +1,110 @@ +using Microsoft.Extensions.Logging; +using System.Text; + +namespace DigitalData.Core.DTO +{ + public static class DTOExtensions + { + public static T Message(this T result, string message) where T : Result + { + result.Messages.Add(message); + return result; + } + + public static T Message(this T result, params string[] messages) where T : Result + { + result.Messages.AddRange(messages); + return result; + } + + public static T Notice(this T result, Notice notice) where T : Result + { + result.Notices.Add(notice); + return result; + } + + public static T Notice(this T result, params Notice[] notices) where T : Result + { + result.Notices.AddRange(notices); + return result; + } + + public static T Notice(this T result, LogLevel level, params Enum[] flags) where T : Result + { + var notices = flags.Select(flag => new Notice() + { + Flag = flag, + Level = level + }); + result.Notices.AddRange(notices); + return result; + } + + public static T Notice(this T result, LogLevel level, Enum flag, params string[] messages) where T : Result + { + result.Notices.Add(new Notice() + { + Flag = flag, + Level = level, + Messages = messages.ToList() + }); + return result; + } + + public static T Notice(this T result, LogLevel level, params string[] messages) where T : Result + { + result.Notices.Add(new Notice() + { + Flag = null, + Level = level, + Messages = messages.ToList() + }); + return result; + } + + public static I Then(this Result result, Func Try, Func, List, I> Catch) + { + return result.IsSuccess ? Try() : Catch(result.Messages, result.Notices); + } + + public static I Then(this DataResult result, Func Try, Func, List, I> Catch) + { + return result.IsSuccess ? Try(result.Data) : Catch(result.Messages, result.Notices); + } + + public static async Task Then(this Task tResult, Func Try, Func, List, I> Catch) + { + Result result = await tResult; + return result.IsSuccess ? Try() : Catch(result.Messages, result.Notices); + } + + public static async Task Then(this Task> tResult, Func Try, Func, List, I> Catch) + { + DataResult result = await tResult; + return result.IsSuccess ? Try(result.Data) : Catch(result.Messages, result.Notices); + } + + public static void LogNotice(this ILogger logger, IEnumerable notices, string start = ": ", string seperator = ". ", string end = ".\n") + { + foreach(LogLevel level in Enum.GetValues(typeof(LogLevel))) + { + var logNotices = notices.Where(n => n.Level == level); + + if (!logNotices.Any()) + continue; + + var sb = new StringBuilder(); + foreach(Notice notice in logNotices) + { + if (notice.Flag is not null) + sb.Append(notice.Flag); + + if (notice.Messages.Any()) + sb.Append(start).Append(string.Join(seperator, notice.Messages)).AppendLine(end); + else sb.Append(end); + } + logger.Log(level, sb.ToString()); + } + } + } +} \ No newline at end of file diff --git a/DigitalData.Core.DTO/DataResult.cs b/DigitalData.Core.DTO/DataResult.cs new file mode 100644 index 0000000..a5ed5fb --- /dev/null +++ b/DigitalData.Core.DTO/DataResult.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace DigitalData.Core.DTO +{ + public class DataResult : Result + { + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public required T Data { get; init; } + } +} \ No newline at end of file diff --git a/DigitalData.Core.DTO/DigitalData.Core.DTO.csproj b/DigitalData.Core.DTO/DigitalData.Core.DTO.csproj new file mode 100644 index 0000000..8a17f62 --- /dev/null +++ b/DigitalData.Core.DTO/DigitalData.Core.DTO.csproj @@ -0,0 +1,17 @@ + + + + net7.0 + enable + enable + + + + + + + + + + + diff --git a/DigitalData.Core.Application/Flag.cs b/DigitalData.Core.DTO/Flag.cs similarity index 97% rename from DigitalData.Core.Application/Flag.cs rename to DigitalData.Core.DTO/Flag.cs index 9649926..bc2508b 100644 --- a/DigitalData.Core.Application/Flag.cs +++ b/DigitalData.Core.DTO/Flag.cs @@ -1,4 +1,4 @@ -namespace DigitalData.Core.Application +namespace DigitalData.Core.DTO { /// /// Defines flags that indicate specific types of status or conditions in a service operation. diff --git a/DigitalData.Core.DTO/Notice.cs b/DigitalData.Core.DTO/Notice.cs new file mode 100644 index 0000000..5094f9b --- /dev/null +++ b/DigitalData.Core.DTO/Notice.cs @@ -0,0 +1,11 @@ +using Microsoft.Extensions.Logging; + +namespace DigitalData.Core.DTO +{ + public class Notice + { + public Enum? Flag { get; init; } = null; + public LogLevel Level { get; init; } = LogLevel.None; + public List Messages { get; init; } = new(); + } +} \ No newline at end of file diff --git a/DigitalData.Core.DTO/Result.cs b/DigitalData.Core.DTO/Result.cs new file mode 100644 index 0000000..c1892f9 --- /dev/null +++ b/DigitalData.Core.DTO/Result.cs @@ -0,0 +1,40 @@ +using System.Text.Json.Serialization; + +namespace DigitalData.Core.DTO +{ + public class Result + { + public bool IsSuccess { get; set; } = false; + + public List Messages { get; init; } = new(); + + [JsonIgnore] + public List Notices = new(); + + public DataResult Data(T data) => new() + { + IsSuccess = IsSuccess, + Messages = Messages, + Notices = Notices, + Data = data + }; + + public static Result Success() => new() { IsSuccess = true }; + + public static Result Fail() => new() { IsSuccess = false }; + + public static DataResult Success(T data) => new() + { + IsSuccess = true, + Data = data + }; + +#pragma warning disable CS8601 // Possible null reference assignment. + public static DataResult Fail() => new() + { + IsSuccess = false, + Data = default + }; +#pragma warning restore CS8601 // Possible null reference assignment. + } +} \ No newline at end of file diff --git a/DigitalData.Core.Infrastructure/CRUDRepository.cs b/DigitalData.Core.Infrastructure/CRUDRepository.cs index 96a448b..80bbf83 100644 --- a/DigitalData.Core.Infrastructure/CRUDRepository.cs +++ b/DigitalData.Core.Infrastructure/CRUDRepository.cs @@ -78,5 +78,11 @@ namespace DigitalData.Core.Infrastructure var result = await _dbContext.SaveChangesAsync(); return result > 0; } + + /// + /// Asynchronously counts all entities in the repository. + /// + /// The total number of entities in the repository. + public virtual async Task CountAsync() => await _dbSet.CountAsync(); } } \ No newline at end of file diff --git a/DigitalData.Core.sln b/DigitalData.Core.sln index 71c0cce..8cd090c 100644 --- a/DigitalData.Core.sln +++ b/DigitalData.Core.sln @@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.Core.API", "Dig EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.Core.Tests", "DigitalData.Core.Tests\DigitalData.Core.Tests.csproj", "{B54DEF90-C30C-44EA-9875-76F1B330CBB7}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DigitalData.Core.DTO", "DigitalData.Core.DTO\DigitalData.Core.DTO.csproj", "{0B051A5F-BD38-47D1-BAFF-D44BA30D3FB7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +41,10 @@ Global {B54DEF90-C30C-44EA-9875-76F1B330CBB7}.Debug|Any CPU.Build.0 = Debug|Any CPU {B54DEF90-C30C-44EA-9875-76F1B330CBB7}.Release|Any CPU.ActiveCfg = Release|Any CPU {B54DEF90-C30C-44EA-9875-76F1B330CBB7}.Release|Any CPU.Build.0 = Release|Any CPU + {0B051A5F-BD38-47D1-BAFF-D44BA30D3FB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0B051A5F-BD38-47D1-BAFF-D44BA30D3FB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0B051A5F-BD38-47D1-BAFF-D44BA30D3FB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0B051A5F-BD38-47D1-BAFF-D44BA30D3FB7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE