Erweiterung der DTOs und Implementierung der Lokalisierungsdienste

- 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.
This commit is contained in:
Developer 02
2024-04-30 17:01:26 +02:00
parent f6d8721c27
commit 4b71836fea
36 changed files with 303 additions and 1109 deletions

View File

@@ -1,22 +0,0 @@
using AutoMapper;
namespace DigitalData.Core.Application
{
public static class AutoMapperExtension
{
/// <summary>
/// Maps a source object to a destination object, or throws an exception if the mapping result is null.
/// </summary>
/// <typeparam name="TSource">The source object type.</typeparam>
/// <typeparam name="TDestination">The destination object type.</typeparam>
/// <param name="source">The source object to map from.</param>
/// <returns>The mapped destination object.</returns>
/// <exception cref="MappingResultNullException">Thrown when the mapping result is null.</exception>
public static TDestination MapOrThrow<TDestination>(this IMapper mapper, object source)
{
return mapper.Map<TDestination>(source) ?? throw new AutoMapperMappingException(
$"Mapping to {typeof(TDestination).FullName} resulted in a null object. " +
"Hint: Ensure that the AutoMapper profile configuration for this mapping is correct.");
}
}
}

View File

@@ -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
/// <typeparam name="TUpdateDto">The DTO type for update operations.</typeparam>
/// <typeparam name="TEntity">The entity type.</typeparam>
/// <typeparam name="TId">The type of the identifier for the entity.</typeparam>
public class CRUDService<TCRUDRepository, TCreateDto, TReadDto, TUpdateDto, TEntity, TId> : ServiceBase, ICRUDService<TCRUDRepository, TCreateDto, TReadDto, TUpdateDto, TEntity, TId>, IServiceBase
public class CRUDService<TCRUDRepository, TCreateDto, TReadDto, TUpdateDto, TEntity, TId> : ICRUDService<TCRUDRepository, TCreateDto, TReadDto, TUpdateDto, TEntity, TId>
where TCRUDRepository : ICRUDRepository<TEntity, TId> 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;
/// <summary>
/// Initializes a new instance of the CRUDService class with the specified repository, translation service, and mapper.
/// </summary>
/// <param name="repository">The CRUD repository for accessing the database.</param>
/// <param name="translationService">The service for translating messages based on culture.</param>
/// <param name="localizer">The localizer for translating messages based on culture.</param>
/// <param name="mapper">The AutoMapper instance for mapping between DTOs and entity objects.</param>
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;
}
/// <summary>
@@ -42,14 +46,11 @@ namespace DigitalData.Core.Application
/// </summary>
/// <param name="createDto">The DTO to create an entity from.</param>
/// <returns>A service result indicating success or failure, including the entity DTO.</returns>
public virtual async Task<IServiceResult<TId>> CreateAsync(TCreateDto createDto)
public virtual async Task<DataResult<TId>> CreateAsync(TCreateDto createDto)
{
var entity = _mapper.MapOrThrow<TEntity>(createDto);
var createdEntity = await _repository.CreateAsync(entity);
if (createdEntity is null)
return Failed<TId>();
else
return Successful(KeyValueOf(createdEntity));
return createdEntity is null ? Result.Fail<TId>() : Result.Success(KeyValueOf(createdEntity));
}
/// <summary>
@@ -57,27 +58,23 @@ namespace DigitalData.Core.Application
/// </summary>
/// <param name="id">The identifier of the entity to read.</param>
/// <returns>A service result indicating success or failure, including the read DTO if successful.</returns>
public virtual async Task<IServiceResult<TReadDto>> ReadByIdAsync(TId id)
public virtual async Task<DataResult<TReadDto>> ReadByIdAsync(TId id)
{
var entity = await _repository.ReadByIdAsync(id);
if (entity is null)
{
var translatedMessage = MessageKey.EntityDoesNotExist.LocalizedBy(_localizer);
return Failed<TReadDto>();
}
else
return Successful(_mapper.MapOrThrow<TReadDto>(entity));
return entity is null
? Result.Fail<TReadDto>().Message(_localizer[Key.EntityDoesNotExist])
: Result.Success(_mapper.MapOrThrow<TReadDto>(entity));
}
/// <summary>
/// Asynchronously reads all entities and maps them to read DTOs.
/// </summary>
/// <returns>A service result including a collection of read DTOs.</returns>
public virtual async Task<IServiceResult<IEnumerable<TReadDto>>> ReadAllAsync()
public virtual async Task<DataResult<IEnumerable<TReadDto>>> ReadAllAsync()
{
var entities = await _repository.ReadAllAsync();
var readDto = _mapper.MapOrThrow<IEnumerable<TReadDto>>(entities);
return Successful(readDto);
return Result.Success(readDto);
}
/// <summary>
@@ -85,17 +82,11 @@ namespace DigitalData.Core.Application
/// </summary>
/// <param name="updateDto">The DTO to update an entity from.</param>
/// <returns>A service message indicating success or failure.</returns>
public virtual async Task<IServiceMessage> UpdateAsync(TUpdateDto updateDto)
public virtual async Task<Result> UpdateAsync(TUpdateDto updateDto)
{
var entity = _mapper.MapOrThrow<TEntity>(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]);
}
/// <summary>
@@ -103,26 +94,15 @@ namespace DigitalData.Core.Application
/// </summary>
/// <param name="id">The identifier of the entity to delete.</param>
/// <returns>A service message indicating success or failure.</returns>
public virtual async Task<IServiceMessage> DeleteAsyncById(TId id)
public virtual async Task<Result> 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]);
}
/// <summary>

View File

@@ -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<IResponseService, ResponseService>();
return service;
}
public static IServiceCollection AddJWTService<TClaimValue>(this IServiceCollection services, Func<TClaimValue, SecurityTokenDescriptor> tokenDescriptorFactory)
{
return services.AddScoped<IJWTService<TClaimValue>, JWTService<TClaimValue>>(provider => new (tokenDescriptorFactory));
}
public static IServiceCollection AddCookieConsentSettings(this IServiceCollection services)
{
services.AddSingleton<CookieConsentSettings>(sp =>
{
var configuration = sp.GetRequiredService<IConfiguration>();
var settings = configuration.GetSection("CookieConsentSettings").Get<CookieConsentSettings>();
if (settings is null)
{
throw new ConfigurationErrorsException("The 'CookieConsentSettings' section is missing or improperly configured in appsettings.json.");
}
return settings;
});
return services;
}
}
}

View File

@@ -1,74 +0,0 @@
namespace DigitalData.Core.Application.DTO
{
/// <summary>
/// Represents settings related to user cookie consent dialogs. Designed to be serialized into JSON format for use with JavaScript frontend libraries,
/// such as the bootstrap-cookie-consent-settings at the GitHub repository: https://github.com/shaack/bootstrap-cookie-consent-settings
/// </summary>
public class CookieConsentSettings
{
/// <summary>
/// URL to the privacy policy page.
/// </summary>
public string? PrivacyPolicyUrl { get; set; }
/// <summary>
/// URL to the legal notice page.
/// </summary>
public string? LegalNoticeUrl { get; set; }
/// <summary>
/// URL to the content of the dialog box.
/// </summary>
public string? ContentURL { get; set; }
/// <summary>
/// CSS class for the 'Agree' button.
/// </summary>
public string? ButtonAgreeClass { get; set; }
/// <summary>
/// CSS class for the 'Don't Agree' button.
/// </summary>
public string? ButtonDontAgreeClass { get; set; }
/// <summary>
/// CSS class for the 'Save' button.
/// </summary>
public string? ButtonSaveClass { get; set; }
/// <summary>
/// Language in which the modal is displayed.
/// </summary>
public string? Lang { get; set; }
/// <summary>
/// Default language for the modal if the user's browser language is not supported.
/// </summary>
public string? DefaultLang { get; set; }
/// <summary>
/// Name of the cookie used to store the consent status.
/// </summary>
public string? CookieName { get; set; }
/// <summary>
/// Number of days the cookie will be stored.
/// </summary>
public int CookieStorageDays { get; set; }
/// <summary>
/// Identifier for the modal dialog element.
/// </summary>
public string? ModalId { get; set; }
/// <summary>
/// Indicates whether to also store the settings in the browser's localStorage.
/// </summary>
public bool AlsoUseLocalStorage { get; set; }
/// <summary>
/// List of categories for cookie consent.
/// </summary>
public List<string>? Categories { get; set; }
}
}

View File

@@ -21,4 +21,8 @@
<ProjectReference Include="..\DigitalData.Core.Contracts\DigitalData.Core.Contracts.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="NewFolder\" />
</ItemGroup>
</Project>

View File

@@ -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 = "<Pending>")]
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<string, string> CustomSearchFilters { get; }
protected readonly IStringLocalizer _localizer;
public DirectorySearchService(IConfiguration configuration, ILogger<DirectorySearchService> logger, IMemoryCache memoryCache, IStringLocalizer defaultLocalizer) : base(defaultLocalizer)
public DirectorySearchService(IConfiguration configuration, ILogger<DirectorySearchService> 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<IEnumerable<ResultPropertyCollection>> FindAll(DirectoryEntry searchRoot, string filter, SearchScope searchScope = SearchScope.Subtree, int sizeLimit = 5000, params string[] properties)
public DataResult<IEnumerable<ResultPropertyCollection>> FindAll(DirectoryEntry searchRoot, string filter, SearchScope searchScope = SearchScope.Subtree, int sizeLimit = 5000, params string[] properties)
{
List<ResultPropertyCollection> list = new();
@@ -71,17 +75,17 @@ namespace DigitalData.Core.Application
list.Add(rpc);
}
return Successful<IEnumerable<ResultPropertyCollection>>(list);
return Result.Success<IEnumerable<ResultPropertyCollection>>(list);
}
public IServiceResult<IEnumerable<ResultPropertyCollection>> FindAllByUserCache(string username, string filter, SearchScope searchScope = SearchScope.Subtree, int sizeLimit = 5000, params string[] properties)
public DataResult<IEnumerable<ResultPropertyCollection>> FindAllByUserCache(string username, string filter, SearchScope searchScope = SearchScope.Subtree, int sizeLimit = 5000, params string[] properties)
{
List<ResultPropertyCollection> list = new();
_memoryCache.TryGetValue(username, out DirectoryEntry? searchRoot);
if (searchRoot is null)
return Failed<IEnumerable<ResultPropertyCollection>>(MessageKey.DirSearcherDisconnected.ToString());
return Result.Fail<IEnumerable<ResultPropertyCollection>>().Message(_localizer[Key.DirSearcherDisconnected]);
return FindAll(searchRoot, filter, searchScope, sizeLimit, properties);
}

View File

@@ -1,23 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DigitalData.Core.Application
{
/// <summary>
/// Provides extension methods for IEnumerable<T>.
/// </summary>
public static class EnumerableExtensions
{
/// <summary>
/// Concatenates the members of a collection, using the specified separator between each member.
/// </summary>
/// <typeparam name="T">The type of the elements of the enumerable.</typeparam>
/// <param name="enumerable">The IEnumerable<T> whose elements to concatenate.</param>
/// <param name="separator">The string to use as a separator. Separator is included in the returned string only between elements of the collection.</param>
/// <returns>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.</returns>
public static string Join<T>(this IEnumerable<T> enumerable, string separator = ". ") => string.Join(separator, enumerable);
}
}

View File

@@ -1,44 +0,0 @@
namespace DigitalData.Core.Application
{
/// <summary>
/// Defines flags that indicate specific types of status or conditions in a service operation.
/// These flags help in categorizing and identifying specific circumstances or issues that may arise during execution.
/// </summary>
public enum Flag
{
/// <summary>
/// Indicates a security breach or vulnerability has been detected during the service operation.
/// </summary>
SecurityBreach,
/// <summary>
/// Indicates a potential issue with data integrity during the service operation.
/// This flag is used when data may have been altered, corrupted, or is otherwise unreliable,
/// which could impact the accuracy or trustworthiness of the operation's results.
/// </summary>
DataIntegrityIssue,
/// <summary>
/// Indicates that either a security breach, a data integrity issue, or both have been detected during the service operation.
/// 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.
/// </summary>
SecurityBreachOrDataIntegrity,
/// <summary>
/// Indicates a possible security breach during the service operation.
/// </summary>
PossibleSecurityBreach,
/// <summary>
/// Indicates a possible issue with data integrity during the service operation.
/// This flag is used when there is a suspicion of data alteration, corruption, or unreliability.
/// </summary>
PossibleDataIntegrityIssue,
/// <summary>
/// Indicates that either a possible security breach, a possible data integrity issue, or both have been detected during the service operation.
/// This flag is used when it is uncertain whether the issue is related to security, data integrity, or both.
/// </summary>
PossibleSecurityBreachOrDataIntegrity
}
}

View File

@@ -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";
}
}

View File

@@ -1,41 +0,0 @@
using DigitalData.Core.Contracts.Application;
using Microsoft.Extensions.Logging;
namespace DigitalData.Core.Application
{
/// <summary>
/// Provides extension methods for ILogger to handle logging of message collections and service messages more effectively.
/// </summary>
public static class LoggerExtensions
{
/// <summary>
/// Logs a list of messages at the specified log level.
/// </summary>
/// <param name="logger">The extended ILogger instance.</param>
/// <param name="logLevel">The severity level of the log entry.</param>
/// <param name="messages">The collection of messages to log.</param>
/// <param name="separator">The separator used to join messages. Default is newline.</param>
/// <param name="args">Additional arguments used for formatting the log message.</param>
public static void LogMessageList(this ILogger logger, LogLevel logLevel, IEnumerable<string> messages, string separator = "\n", params object?[] args)
{
if (messages.Any())
logger.Log(logLevel: logLevel, message: string.Join(separator, messages), args);
}
/// <summary>
/// Logs all messages from a service message instance, categorized by message type to appropriate log levels.
/// </summary>
/// <param name="logger">The extended ILogger instance.</param>
/// <param name="serviceMessage">The service message instance containing categorized messages.</param>
/// <param name="separator">The separator used to join messages within each category. Default is newline.</param>
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);
}
}
}

View File

@@ -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
}
}

View File

@@ -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
/// <summary>
/// Creates a service message indicating success or failure without additional messages.
/// </summary>
/// <param name="isSuccess">Indicates if the operation was successful.</param>
/// <returns>A service message reflecting the operation outcome.</returns>
public IServiceMessage CreateMessage(bool isSuccess = false) => new ServiceMessage()
{
IsSuccess = isSuccess
};
/// <summary>
/// Creates a service result containing the provided data and a success flag, without additional messages.
/// </summary>
/// <typeparam name="T">The type of the data included in the result.</typeparam>
/// <param name="data">The data to include in the result.</param>
/// <param name="isSuccess">Indicates if the operation was successful.</param>
/// <returns>A service result with the specified data and outcome.</returns>
public IServiceResult<T> CreateResult<T>(T? data = default, bool isSuccess = false) => new ServiceResult<T>()
{
IsSuccess = isSuccess,
Data = data
};
/// <summary>
/// Creates a service message indicating a successful operation without additional messages.
/// </summary>
/// <returns>A service message indicating a successful operation.</returns>
public IServiceMessage Successful() => CreateMessage(true);
/// <summary>
/// Creates a service message indicating a failed operation without additional messages.
/// </summary>
/// <returns>A service message indicating a failed operation.</returns>
public IServiceMessage Failed() => CreateMessage(false);
/// <summary>
/// Creates a successful service result with the specified data, without additional messages.
/// </summary>
/// <typeparam name="T">The type of data included in the result.</typeparam>
/// <param name="data">The data to include in the result.</param>
/// <returns>A successful service result containing the specified data.</returns>
public IServiceResult<T> Successful<T>(T data) => CreateResult(data, true);
/// <summary>
/// Creates a failed service result with optional data, without additional messages.
/// </summary>
/// <typeparam name="T">The type of data the service result can contain.</typeparam>
/// <param name="data">Optional data to include in the result.</param>
/// <returns>A failed service result, which may or may not contain the specified data.</returns>
public IServiceResult<T> Failed<T>(T? data = default) => CreateResult(data, false);
#endregion
#region WITH_STRING_MESSAGE
[Obsolete("Deprecated: Use ClientMessages instead.")]
/// <summary>
/// Creates a service message with the specified success flag and messages.
/// </summary>
/// <param name="isSuccess">Indicates if the operation was successful.</param>
/// <param name="messages">An array of messages associated with the operation.</param>
/// <returns>A new instance of <see cref="ServiceMessage"/> reflecting the operation outcome.</returns>
public virtual IServiceMessage CreateMessage(bool isSuccess, params string[] messages) => new ServiceMessage(isSuccess, messages);
[Obsolete("Deprecated: Use ClientMessages instead.")]
/// <summary>
/// Creates a service result containing the provided data, success flag, and messages.
/// </summary>
/// <typeparam name="T">The type of the data included in the result.</typeparam>
/// <param name="data">The data to include in the result.</param>
/// <param name="isSuccess">Indicates if the operation was successful.</param>
/// <param name="messages">An array of messages associated with the operation.</param>
/// <returns>A new instance of <see cref="ServiceResult{T}"/> with the specified data and outcome.</returns>
public virtual IServiceResult<T> CreateResult<T>(T? data = default, bool isSuccess = true, params string[] messages) => new ServiceResult<T>(data, isSuccess, messages);
[Obsolete("Deprecated: Use ClientMessages instead.")]
/// <summary>
/// Creates a successful service message.
/// </summary>
/// <param name="messages">An array of success messages.</param>
/// <returns>A successful service message.</returns>
public virtual IServiceMessage Successful(params string[] messages) => CreateMessage(true, messages);
[Obsolete("Deprecated: Use ClientMessages instead.")]
/// <summary>
/// Creates a failed service message.
/// </summary>
/// <param name="messages">An array of failure messages.</param>
/// <returns>A failed service message.</returns>
public virtual IServiceMessage Failed(params string[] messages) => CreateMessage(false, messages);
[Obsolete("Deprecated: Use ClientMessages instead.")]
/// <summary>
/// Creates a successful service result with the provided data.
/// </summary>
/// <typeparam name="T">The type of data included in the result.</typeparam>
/// <param name="data">The data to include in the result.</param>
/// <param name="messages">An array of success messages.</param>
/// <returns>A successful service result containing the specified data.</returns>
public virtual IServiceResult<T> Successful<T>(T data, params string[] messages) => CreateResult(data, true, messages);
[Obsolete("Deprecated: Use ClientMessages instead.")]
/// <summary>
/// Creates a failed service result, optionally including data.
/// </summary>
/// <typeparam name="T">The type of data the service result can contain.</typeparam>
/// <param name="data">Optional data to include in the failed result.</param>
/// <param name="messages">An array of failure messages.</param>
/// <returns>A failed service result, which may or may not contain the specified data.</returns>
public virtual IServiceResult<T> Failed<T>(T? data = default, params string[] messages) => CreateResult(data, false, messages);
[Obsolete("Deprecated: Use ClientMessages instead.")]
/// <summary>
/// Creates a failed service result using only failure messages, without explicitly including data.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <typeparam name="T">The type of data the service result can contain. The result will contain the default value for this type.</typeparam>
/// <param name="messages">An array of failure messages that provide details about the reasons for the operation's failure.</param>
/// <returns>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.</returns>
public virtual IServiceResult<T> Failed<T>(params string[] messages) => Failed<T>(default, messages);
#endregion
#region WITH_ENUM_MESSAGE
[Obsolete("Deprecated: Use ClientMessages instead.")]
/// <summary>
/// Creates a service message with the specified success flag and enumeration messages.
/// </summary>
/// <param name="isSuccess">Indicates if the operation was successful.</param>
/// <param name="messages">An array of enumeration values associated with the operation.</param>
/// <returns>A new instance of <see cref="ServiceMessage"/> reflecting the operation outcome with enumeration messages.</returns>
public IServiceMessage CreateMessage(bool isSuccess, params Enum[] messages) => CreateMessage(isSuccess, messages.Select(m => m.ToString()).ToArray());
[Obsolete("Deprecated: Use ClientMessages instead.")]
/// <summary>
/// Creates a service result containing the provided data, success flag, and enumeration messages.
/// </summary>
/// <typeparam name="T">The type of the data included in the result.</typeparam>
/// <param name="data">The data to include in the result.</param>
/// <param name="isSuccess">Indicates if the operation was successful.</param>
/// <param name="messages">An array of enumeration values associated with the operation.</param>
/// <returns>A new instance of <see cref="ServiceResult{T}"/> with the specified data and outcome using enumeration messages.</returns>
public IServiceResult<T> CreateResult<T>(T? data, bool isSuccess, params Enum[] messages) => CreateResult(data, isSuccess, messages.Select(m => m.ToString()).ToArray());
[Obsolete("Deprecated: Use ClientMessages instead.")]
/// <summary>
/// Creates a successful service message using enumeration messages.
/// </summary>
/// <param name="messages">An array of success enumeration values.</param>
/// <returns>A successful service message.</returns>
public IServiceMessage Successful(params Enum[] messages) => CreateMessage(true, messages.Select(m => m.ToString()).ToArray());
[Obsolete("Deprecated: Use ClientMessages instead.")]
/// <summary>
/// Creates a failed service message using enumeration messages.
/// </summary>
/// <param name="messages">An array of failure enumeration values.</param>
/// <returns>A failed service message.</returns>
public IServiceMessage Failed(params Enum[] messages) => CreateMessage(false, messages.Select(m => m.ToString()).ToArray());
[Obsolete("Deprecated: Use ClientMessages instead.")]
/// <summary>
/// Creates a successful service result with the provided data using enumeration messages.
/// </summary>
/// <typeparam name="T">The type of data included in the result.</typeparam>
/// <param name="data">The data to include in the result.</param>
/// <param name="messages">An array of success enumeration values.</param>
/// <returns>A successful service result containing the specified data.</returns>
public IServiceResult<T> Successful<T>(T data, params Enum[] messages) => CreateResult(data, true, messages.Select(m => m.ToString()).ToArray());
[Obsolete("Deprecated: Use ClientMessages instead.")]
/// <summary>
/// Creates a failed service result, optionally including data, using enumeration messages.
/// </summary>
/// <typeparam name="T">The type of data the service result can contain.</typeparam>
/// <param name="data">Optional data to include in the failed result.</param>
/// <param name="messages">An array of failure enumeration values.</param>
/// <returns>A failed service result, which may or may not contain the specified data.</returns>
public IServiceResult<T> Failed<T>(T? data = default, params Enum[] messages) => CreateResult(data, false, messages.Select(m => m.ToString()).ToArray());
[Obsolete("Deprecated: Use ClientMessages instead.")]
/// <summary>
/// Creates a failed service result using only failure messages, without explicitly including data.
/// </summary>
/// <typeparam name="T">The type of data the service result can contain. The result will contain the default value for this type.</typeparam>
/// <param name="messages">An array of failure enumeration values that provide details about the reasons for the operation's failure.</param>
/// <returns>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.</returns>
public IServiceResult<T> Failed<T>(params Enum[] messages) => Failed<T>(default(T), messages);
#endregion
}
}

View File

@@ -1,16 +0,0 @@
using AutoMapper;
using DigitalData.Core.Contracts.Application;
using Microsoft.Extensions.Localization;
namespace DigitalData.Core.Application
{
/// <summary>
/// Provides a base implementation of <see cref="IServiceBase"/>.
/// </summary>
public class ServiceBase : ResponseService, IServiceBase, IResponseService
{
public ServiceBase(IStringLocalizer defaultLocalizer) : base(defaultLocalizer)
{
}
}
}

View File

@@ -1,138 +0,0 @@
using DigitalData.Core.Contracts.Application;
using System.Text.Json.Serialization;
namespace DigitalData.Core.Application
{
/// <summary>
/// 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.
/// </summary>
public class ServiceMessage : IServiceMessage
{
/// <summary>
/// Initializes a new instance of the ServiceMessage class.
/// </summary>
public ServiceMessage()
{
}
/// <summary>
/// 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.
/// </summary>
/// <param name="isSuccess">Indicates whether the service operation was successful.</param>
/// If provided, it will also be adapted for translating enum keys by converting them to strings first.</param>
[Obsolete("Deprecated: initialize objects using object initializer.")]
public ServiceMessage(bool isSuccess)
{
IsSuccess = isSuccess;
}
/// <summary>
/// Initializes a new instance of the ServiceMessage class with specified success status, and messages.
/// </summary>
/// <param name="isSuccess">Indicates whether the service operation was successful.</param>
/// <param name="messages">An array of messages related to the operation's outcome.</param>
[Obsolete("Deprecated: initialize objects using object initializer.")]
public ServiceMessage(bool isSuccess, params string[] messages)
{
IsSuccess = isSuccess;
Messages = messages.ToList<string>();
}
/// <summary>
/// Gets or sets a value indicating whether the service operation was successful.
/// </summary>
public bool IsSuccess { get; set; } = false;
/// <summary>
/// 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.
/// </summary>
[JsonIgnore]
public ICollection<Enum> Flags { get; } = new List<Enum>();
/// <summary>
/// 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.
/// </summary>
/// <param name="flag">The flag to check against the current service message's flags.</param>
/// <returns>true if a flag with a matching string representation exists; otherwise, false.</returns>
public bool HasFlag(Enum flag) => Flags.Any(f => f.ToString() == flag.ToString());
/// <summary>
/// [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.
/// </summary>
[Obsolete("Deprecated: Use ClientMessages instead.")]
public ICollection<string> Messages { get; init; } = new List<string>();
/// <summary>
/// Gets a collection of messages intended for client display. This replaces the deprecated 'Messages' property.
/// </summary>
public ICollection<string> ClientMessages { get; init; } = new List<string>();
/// <summary>
/// Gets a collection of messages used for tracing program execution at a fine-grained level. These are typically voluminous and detailed.
/// </summary>
[JsonIgnore]
public ICollection<string> TraceMessages { get; init; } = new List<string>();
/// <summary>
/// Gets a collection of messages helpful for debugging during development. These messages are often diagnostic.
/// </summary>
[JsonIgnore]
public ICollection<string> DebugMessages { get; init; } = new List<string>();
/// <summary>
/// Gets a collection of informational messages, less critical than warnings, generally used for non-critical notifications.
/// </summary>
[JsonIgnore]
public ICollection<string> InformationMessages { get; init; } = new List<string>();
/// <summary>
/// Gets a collection of messages indicating potential issues that are not necessarily errors, but which may require attention.
/// </summary>
[JsonIgnore]
public ICollection<string> WarningMessages { get; init; } = new List<string>();
/// <summary>
/// Gets a collection of error messages indicating failures or problems within the service.
/// </summary>
[JsonIgnore]
public ICollection<string> ErrorMessages { get; init; } = new List<string>();
/// <summary>
/// Gets a collection of messages indicating critical issues that require immediate attention.
/// </summary>
[JsonIgnore]
public ICollection<string> CriticalMessages { get; init; } = new List<string>();
/// <summary>
/// Adds a new message to the collection of messages associated with the service operation.
/// </summary>
/// <param name="message">The message to add.</param>
/// <returns>The current instance of ServiceMessage, allowing for method chaining.</returns>
[Obsolete("Deprecated: Use ClientMessages instead.")]
public IServiceMessage WithMessage(string message)
{
Messages.Add(message);
return this;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="messageKey">The enum value representing the message key.</param>
/// <returns>The current instance of ServiceMessage, allowing for method chaining.</returns>
[Obsolete("Deprecated: Use ClientMessages instead.")]
public IServiceMessage WithMessageKey(Enum messageKey)
{
Messages.Add(messageKey.ToString());
return this;
}
}
}

View File

@@ -1,153 +0,0 @@
using DigitalData.Core.Contracts.Application;
using Microsoft.Extensions.Localization;
namespace DigitalData.Core.Application
{
/// <summary>
/// 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.
/// </summary>
public static class ServiceMessageExtensions
{
#region Flag
/// <summary>
/// Sets the specified flag on the service message or result, allowing for the categorization of the message based on specific conditions or statuses.
/// </summary>
/// <typeparam name="T">The type of IServiceMessage.</typeparam>
/// <param name="serviceMessage">The service message instance to modify.</param>
/// <param name="flag">The flag to set, indicating a specific condition or status.</param>
/// <returns>The service message instance with the updated flag, facilitating fluent chaining of methods.</returns>
public static T WithFlag<T>(this T serviceMessage, Enum flag) where T : IServiceMessage
{
serviceMessage.Flags.Add(flag);
return serviceMessage;
}
/// <summary>
/// Determines whether the service message has a flag indicating a security breach.
/// </summary>
/// <param name="serviceMessage">The service message instance to check.</param>
/// <returns>True if the service message has the security breach flag; otherwise, false.</returns>
public static bool HasSecurityBreachFlag(this IServiceMessage serviceMessage) => serviceMessage.HasFlag(Flag.SecurityBreach);
/// <summary>
/// Determines whether the service message has a flag indicating a data integrity issue.
/// </summary>
/// <param name="serviceMessage">The service message instance to check.</param>
/// <returns>True if the service message has the data integrity issue flag; otherwise, false.</returns>
public static bool HasDataIntegrityIssueFlag(this IServiceMessage serviceMessage) => serviceMessage.HasFlag(Flag.DataIntegrityIssue);
/// <summary>
/// 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.
/// </summary>
/// <param name="serviceMessage">The service message instance to check.</param>
/// <returns>True if the service message has the flag indicating either or both issues; otherwise, false.</returns>
public static bool HasSecurityBreachOrDataIntegrityFlag(this IServiceMessage serviceMessage) => serviceMessage.HasFlag(Flag.SecurityBreachOrDataIntegrity);
#endregion
#region StringLocalizer
/// <summary>
/// Retrieves the localized string for the specified key.
/// </summary>
/// <param name="key">The key for the localized string.</param>
/// <param name="localizer">The localizer to use for retrieving the localized string.</param>
/// <returns>The localized string associated with the specified key.</returns>
public static string LocalizedBy(this string key, IStringLocalizer localizer) => localizer[key];
/// <summary>
/// Retrieves the localized string for the specified enumeration key.
/// </summary>
/// <param name="key">The enumeration key for the localized string.</param>
/// <param name="localizer">The localizer to use for retrieving the localized string.</param>
/// <returns>The localized string associated with the specified enumeration key.</returns>
public static string LocalizedBy(this Enum key, IStringLocalizer localizer) => localizer[key.ToString()];
#endregion
#region ClientMessage
/// <summary>
/// Adds a single client message to the service message.
/// </summary>
/// <param name="serviceMessage">The service message to modify.</param>
/// <param name="message">The message to add.</param>
/// <returns>The modified service message instance.</returns>
public static T WithClientMessage<T>(this T serviceMessage, string message) where T : IServiceMessage
{
serviceMessage.ClientMessages.Add(message);
return serviceMessage;
}
#endregion
#region TraceMessages
/// <summary>
/// Adds a trace message to the service message.
/// </summary>
/// <param name="serviceMessage">The service message to modify.</param>
/// <param name="message">The trace message to add.</param>
/// <returns>The modified service message instance.</returns>
public static T WithTraceMessage<T>(this T serviceMessage, string message) where T : IServiceMessage
{
serviceMessage.TraceMessages.Add(message);
return serviceMessage;
}
#endregion
#region DebugMessages
/// <summary>
/// Adds a debug message to the service message.
/// </summary>
/// <param name="serviceMessage">The service message to modify.</param>
/// <param name="message">The debug message to add.</param>
/// <returns>The modified service message instance.</returns>
public static T WithDebugMessage<T>(this T serviceMessage, string message) where T : IServiceMessage
{
serviceMessage.DebugMessages.Add(message);
return serviceMessage;
}
#endregion
#region WarningMessages
/// <summary>
/// Adds a warning message to the service message.
/// </summary>
/// <param name="serviceMessage">The service message to modify.</param>
/// <param name="message">The warning message to add.</param>
/// <returns>The modified service message instance.</returns>
public static T WithWarningMessage<T>(this T serviceMessage, string message) where T : IServiceMessage
{
serviceMessage.WarningMessages.Add(message);
return serviceMessage;
}
#endregion
#region ErrorMessages
/// <summary>
/// Adds an error message to the service message.
/// </summary>
/// <param name="serviceMessage">The service message to modify.</param>
/// <param name="message">The error message to add.</param>
/// <returns>The modified service message instance.</returns>
public static T WithErrorMessage<T>(this T serviceMessage, string message) where T : IServiceMessage
{
serviceMessage.ErrorMessages.Add(message);
return serviceMessage;
}
#endregion
#region CriticalMessages
/// <summary>
/// Adds a critical message to the service message.
/// </summary>
/// <param name="serviceMessage">The service message to modify.</param>
/// <param name="message">The critical message to add.</param>
/// <returns>The modified service message instance.</returns>
public static T WithCriticalMessage<T>(this T serviceMessage, string message) where T : IServiceMessage
{
serviceMessage.CriticalMessages.Add(message);
return serviceMessage;
}
#endregion
}
}

View File

@@ -1,55 +0,0 @@
using DigitalData.Core.Contracts.Application;
namespace DigitalData.Core.Application
{
/// <summary>
/// Represents the outcome of a service operation, encapsulating the success or failure status,
/// the data returned by the operation, and any associated messages.
/// </summary>
/// <typeparam name="T">The type of data returned by the service operation, if any.</typeparam>
public class ServiceResult<T> : ServiceMessage, IServiceResult<T>
{
/// <summary>
/// Initializes a new instance of the ServiceResult class.
public ServiceResult()
{
}
/// <summary>
/// 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.
/// </summary>
/// <param name="data">The data associated with a successful operation.</param>
/// <param name="stringKeyTranslator">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.</param>
[Obsolete("Deprecated: initialize objects using object initializers instead.")]
public ServiceResult(T? data)
{
Data = data;
IsSuccess = false;
}
/// <summary>
/// Initializes a new instance of the ServiceResult class with specified success status and data.
/// </summary>
/// <param name="data">The data associated with a successful operation.</param>
/// <param name="isSuccess">Indicates whether the service operation was successful.</param>
[Obsolete("Deprecated: initialize objects using object initializers instead.")]
public ServiceResult(T? data, bool isSuccess) : base(isSuccess) => Data = data;
/// <summary>
/// Initializes a new instance of the ServiceResult class with specified success status, data, and messages.
/// </summary>
/// <param name="data">The data associated with a successful operation.</param>
/// <param name="isSuccess">Indicates whether the service operation was successful.</param>
/// <param name="messages">An array of messages related to the operation's outcome.</param>
[Obsolete("Deprecated: Use ClientMessages instead.")]
public ServiceResult(T? data, bool isSuccess, params string[] messages) : base(isSuccess, messages) => Data = data;
/// <summary>
/// Gets or sets the data resulting from the service operation.
/// </summary>
public T? Data { get; set; } = default;
}
}

View File

@@ -1,43 +0,0 @@
namespace DigitalData.Core.Application
{
/// <summary>
/// Specifies the destination of a message.
/// </summary>
public enum To
{
/// <summary>
/// Indicates that the message is intended for the client.
/// </summary>
Client,
/// <summary>
/// Indicates that the message is intended for tracing purposes.
/// </summary>
Trace,
/// <summary>
/// Indicates that the message is intended for debugging purposes.
/// </summary>
Debug,
/// <summary>
/// Indicates that the message is informational.
/// </summary>
Information,
/// <summary>
/// Indicates that the message is a warning.
/// </summary>
Warning,
/// <summary>
/// Indicates that the message represents an error.
/// </summary>
Error,
/// <summary>
/// Indicates that the message represents a critical issue.
/// </summary>
Critical
}
}