Fullstack Hub

[Count: 1]

In the last part, we implemented the database logic by Repository and unit of Work design patterns with Dapper & Dapper.Contrib packages. In this part, let’s implement the business logic in UserManagement.Application. As discussed in Part 1, we will be using CQRS (Command Query Segregation Principle) and Mediator pattern to loosely couple the API and Application layer connection. We will see the implementation in action but first let’s create the base classes e.g. logging, validation behavior, exception handling, etc in this part.

Let’s Start

In UserManagement.Application project, we will add a few useful functionalities e.g centralized logging, exception handling, and model validation. These all functionalities are already implemented in Jason Taylor Clean Architecture with .NET Core article and we will just try to understand the flow of how these functionalities are working. Before moving further, let’s add the dependencies for the Application project so that we don’t get any error in upcoming steps.

Add Dependencies

Right Click on Dependencies folder and select option Manage NuGet Packages… Search and install following packages:

  1. AutoMapper Version 10.X.X
  2. AutoMapper.Extensions.Microsoft.DependencyInjection Version 8.X.X
  3. FluentValidation Version 9.X.X
  4. FluentValidation.DependencyInjectionExtensions Version 9.X.X
  5. MediatR.Extensions.Microsoft.DependencyInjection Version 8.X.X
  6. Microsoft.Extensions.DependencyInjection.Abstractions Version 3.X.X
  7. Microsoft.Extensions.Logging Version 3.X.X
  8. Newtonsoft.Json Version 12.X.X
  9. Add UserManagement.Domain project dependency by right-clicking on Dependencies and select Add Project References…
Loagging Each Request

Create a new folder Behaviors in Common folder and add a nee class LoggingBehaviour in it, replace the code with the following:

namespace UserManagement.Application.Common.Behaviors
{
    using MediatR.Pipeline;
    using Microsoft.Extensions.Logging;
    using System.Threading;
    using System.Threading.Tasks;

    public class LoggingBehaviour<TRequest> : IRequestPreProcessor<TRequest>
    {
        private readonly ILogger _logger;

        public LoggingBehaviour(ILogger<TRequest> logger)
        {
            _logger = logger;
        }

        public async Task Process(TRequest request, CancellationToken cancellationToken)
        {
            var requestName = typeof(TRequest).Name;
            string userName = string.Empty;

            await Task.Run(() => _logger.LogInformation("UserManagement Request: {Name} {@UserName} {@Request}",
                    requestName, userName, request));
        }
    }
}

The Process function in the above class will execute for each API call, get the API name and log it in console, currently, I am not implementing the user authentication but if you follow Jason’s article, you can get both logged in user and request name. Since we are implementing the IRequestPreProcessor interface and adding the MediatR service in DependencyInjection in the upcoming class, we do not need to explicitly call this class, it will automatically be called before each API command/query execution that provides a great deal of loosely coupled architecture. In the future, I would update this article to log in to the text file.

Logging the Time Taking APIs

I am taking this class straight from Jason’s article that is logging a long time taking APIs. The only change I made is a configurable time in milliseconds from the appsettings.json file. Create a new class PerformanceBehaviour in the Behaviors folder and add the following to it:

namespace UserManagement.Application.Common.Behaviors
{
    using MediatR;
    using Microsoft.Extensions.Logging;
    using System.Diagnostics;
    using System.Threading;
    using System.Threading.Tasks;
    using UserManagement.Application.Common.Interfaces;
    public class PerformanceBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    {
        private readonly Stopwatch _timer;
        private readonly ILogger<TRequest> _logger;
        private readonly IConfigConstants _constact;

        public PerformanceBehaviour(ILogger<TRequest> logger, IConfigConstants constants)
        {
            _timer = new Stopwatch();
            _logger = logger;
            _constact = constants;
        }

        public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
        {
            _timer.Start();

            var response = await next();

            _timer.Stop();

            var elapsedMilliseconds = _timer.ElapsedMilliseconds;

            if (elapsedMilliseconds > _constact.LongRunningProcessMilliseconds)
            {
                var requestName = typeof(TRequest).Name;
                var userName = string.Empty;
                _logger.LogWarning("UserManagement Long Running Request: {Name} ({ElapsedMilliseconds} milliseconds) {@UserName} {@Request}",
                    requestName, elapsedMilliseconds, userName, request);
            }

            return response;
        }
    }
}

The main thing to understand in this class is implementing generic IPipelineBehavior interface that adds an additional behavior before going to the next function. IPipelineBehavior has a handler method where we add our custom implementation that is supposed to be executed before moving to the next delegate functionality e.g. executing API command or query handlers. We can also log it in a text file rather than only logging in a running console.

Loagging Unhandled Exception

Again, got straight from Jason’s article and work exactly like long time taken APIs additional behavior class, create a new class UnhandledExceptionBehaviour in the Behaviors folder, and replace the code with the following:

namespace UserManagement.Application.Common.Behaviours
{
    using MediatR;
    using Microsoft.Extensions.Logging;
    using System;
    using System.Threading;
    using System.Threading.Tasks;
    public class UnhandledExceptionBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    {
        private readonly ILogger<TRequest> _logger;

        public UnhandledExceptionBehaviour(ILogger<TRequest> logger)
        {
            _logger = logger;
        }

        public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
        {
            try
            {
                return await next();
            }
            catch (Exception ex)
            {
                var requestName = typeof(TRequest).Name;

                _logger.LogError(ex, "UserManagement Request: Unhandled Exception for Request {Name} {@Request}", requestName, request);

                throw;
            }
        }
    }
}

Add one more class NotFoundException in the same folder that we will use if there is no record found in the database e.g. to update the user, we first will check if the user exists in the database and if not found, will throw NotFoundException. Replace the code in NotFoundException with following code:

namespace UserManagement.Application.Common.Exceptions
{
    using System;
    public class NotFoundException : Exception
    {
        public NotFoundException()
            : base()
        {
        }

        public NotFoundException(string message)
            : base(message)
        {
        }

        public NotFoundException(string message, Exception innerException)
            : base(message, innerException)
        {
        }

        public NotFoundException(string name, object key)
            : base($"Entity \"{name}\" ({key}) was not found.")
        {
        }
    }
}
Model Class Validation Behavior

This is quite an interesting behavior class that in summary, validates all model validation rules we specify for command/query using the Fluent Validation package, get the failure one (the one that is not matching our validation rules), and send to our custom validation class (extended from Exception base class) where we group errors by model class property name and corresponding error message that we define in command/query validation class. This list of property names and errors travels to UserManagement.API project’s CustomExceptionHandlerMiddleware class we serialize to JSON and send it to the front end.

I understand this is quite difficult to comprehend now since we didn’t create the command/query and corresponding validation classes for adding, updating, deleting, and loading users. Once we will create those and add CustomExceptionHandlerMiddleware in userManagement.API project, hopefully, everything will start making sense. For now, just understand this class consolidates all the model validation messages and sends them to the front-end.

Create a new class ValidationBehaviour in Behaviors folder and repalce it’s content with the following:

namespace UserManagement.Application.Common.Behaviours
{
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using FluentValidation;
    using MediatR;
    using ValidationException = UserManagement.Application.Common.Exceptions.ValidationException;
    public class ValidationBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
        where TRequest : IRequest<TResponse>
    {
        private readonly IEnumerable<IValidator<TRequest>> _validators;

        public ValidationBehaviour(IEnumerable<IValidator<TRequest>> validators)
        {
            _validators = validators;
        }

        public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
        {
            if (_validators.Any())
            {
                var context = new ValidationContext<TRequest>(request);

                var validationResults = await Task.WhenAll(_validators.Select(v => v.ValidateAsync(context, cancellationToken)));
                var failures = validationResults.SelectMany(r => r.Errors).Where(f => f != null).ToList();

                if (failures.Count != 0)
                    throw new ValidationException(failures);
            }
            return await next();
        }
    }
}

In the Handle function, a generic code is checking if there is any validation errors exist, it is storing it to the failures list variable and passing it to ValidationException class that we are going to create next.

Exception Handling

Go ahead and create a new folder Exceptions in the Common folder. Create a new class ValidationException in the Exceptions folder and replace its content with the following code:

namespace UserManagement.Application.Common.Exceptions
{
    using FluentValidation.Results;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    public class ValidationException : Exception
    {
        public ValidationException()
            : base("One or more validation failures have occurred.")
        {
            Errors = new Dictionary<string, string[]>();
        }

        public ValidationException(IEnumerable<ValidationFailure> failures)
                : this()
        {
            Errors = failures
                .GroupBy(e => e.PropertyName, e => e.ErrorMessage)
                .ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.ToArray());
        }

        public IDictionary<string, string[]> Errors { get; }
    }
}

In the ValidationException constructor, we are receiving the failures and creating a list by propertyName, and its corresponding error message. This list travels to the API project where we serialize it to JSON and send it to the front-end application. Just remember, it’s totally up to you how to manage these errors and properties, I just standardized it as a propertyName and error message key-value pair so that it should be easy to manage on the front-end (in our case, Angular) application.

AutoMapper Configuration Classes

As discussed in previous parts, we will be using the AutoMapper package to map two entities e.g. DTO to DB Entity and vice versa. We can implement the generic interface IMapFrom that has only one method Mapping helping us to create the dynamic mapping and reverse mapping profile. Create a new folder Mappings in Common folder and add an interface IMapFrom in it, replace the code with following:

namespace UserManagement.Application.Common.Mappings
{
    using AutoMapper;
    public interface IMapFrom<T>
    {
        void Mapping(Profile profile) => profile.CreateMap(typeof(T), GetType()).ReverseMap();
    }
}

Make sure Application, Domain, and Persistance projects are using .NET Standard 2.1.

So each of our DTO (if required) will implement the IMapFrom interface, pass the source mapping class type, and implement the Mapping function if there is custom mapping required. If there is no mapping provided in the Mapping function, the matching properties (by name) would be automatically mapped to the destination class object. We will see this in action while creating the UserDTO that will implement the IMapFrom interface and pass the User database entity class type. The ReverseMapping extension method helps to map the reverse the source and destination classes e.g. UserDTO can be mapped to User and User entity can also be mapped to UserDTO.

We need one more class that is MappingProfile, create it in Mappings folder and add following script in it:

namespace UserManagement.Application.Common.Mappings
{
    using AutoMapper;
    using System;
    using System.Linq;
    using System.Reflection;

    public class MappingProfile : Profile
    {
        public MappingProfile()
        {
            ApplyMappingsFromAssembly(Assembly.GetExecutingAssembly());
        }

        private void ApplyMappingsFromAssembly(Assembly assembly)
        {
            var types = assembly.GetExportedTypes()
                .Where(t => t.GetInterfaces().Any(i =>
                    i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapFrom<>)))
                .ToList();

            foreach (var type in types)
            {
                var instance = Activator.CreateInstance(type);

                var methodInfo = type.GetMethod("Mapping")
                    ?? type.GetInterface("IMapFrom`1").GetMethod("Mapping");

                methodInfo?.Invoke(instance, new object[] { this });

            }
        }
    }
}

This class is basically used for the Application project’s unit tests and it is used to create the Mapper class object. So basically, from current running classes, we are looking for classes implementing IMapFrom interface (e.g. UserDTO), getting their Mapping function, and invoking it. In upcoming parts, we will use this class to create a MapperConfiguration class object and will call its CreateMapper function to initialize the Mapper object. This will help us to enable the mapping functionality from the unit tests. This would make more sense once we will implement user commands/queries and unit tests.

Let’s create the user command/queries and APIs in next part.

Yaseer Mumtaz

Leave a Reply

Your email address will not be published. Required fields are marked *