Есть ли шаблон для выполнения всех зарегистрированных реализаций интерфейса? - PullRequest
1 голос
/ 10 ноября 2019

У меня есть связующее ПО для обработки исключений в основном приложении asp.net, которое использует приведенный ниже IExceptionHandler для обработки исключений

public interface IExceptionHandler<T> where T:Exception
{
    ProblemDetails HandleException(T exception);
}

Я хочу зарегистрировать различные виды обработчиков исключений, такие как

// Handles Sql based exceptions
public class SqlExceptionHandler: IExceptionHandler<SqlException>
{
    public ProblemDetails Handle(SqlException exception);
}

// Handles File based exceptions 
public class IOExceptionHandler : IExceptionHandler<IOException>
{
    public ProblemDetails Handle(IOException exception);
}

// Handles all the exceptions that are not handled by any handlers
public class ExceptionHandler : IExceptionHandler<Exception>
{
    public ProblemDetails Handle(Exception exception)
}

когда срабатывает промежуточное ПО для исключения, я хочу, чтобы обработчик исключения запускался автоматически в зависимости от типа, если исключение не обрабатывается каким-либо из них, тогда нужно, чтобы ExceptionHandler обрабатывал исключение.

Является ли этоМожно ли вызвать экземпляр обработчика исключений автоматически в зависимости от типа? Кроме того, я хочу, чтобы этот IExceptionHandler был расширяемым, чтобы в будущем, если я захочу обработать любой другой тип исключения, я просто зарегистрировал реализацию

Ответы [ 2 ]

1 голос
/ 10 ноября 2019

Это заняло у меня некоторое время, чтобы понять, но было весело и интересно, и, наконец, у меня есть рабочее решение.

Кроме того, я понимаю, что это, вероятно, не то, что вы искали. Было бы неплохо, если бы ASP предоставил что-то из коробки, что вы могли бы зарегистрировать в классе запуска, что, я полагаю, это то, что вы искали .

Если кто-то тамзнает о такой функции, я хотел бы лучшего решения !!

Вот [TestMethod] я использовал, который может быть помещен в MSTest [TestClass]

[TestMethod]
public void _58790281()
{
    try
    {
        throw new IOException("Text IO Exception");
    }
    catch (Exception e)
    {
        PassExceptionToHandler(e);
    }
}

private void PassExceptionToHandler(Exception ex)
{
    Assembly assembly = Assembly.GetAssembly(typeof(IExceptionHandler<>));
    List<Type> concreteTypes = assembly.GetTypes().Where(a => !a.IsInterface).ToList();
    Type currentExceptionHandlerType = concreteTypes
        .FirstOrDefault(a => a.GetInterfaces().Any(i =>
            i.IsGenericType
            && i.GetGenericTypeDefinition() == typeof(IExceptionHandler<>)
            && i.GenericTypeArguments.Any(ii => ii == ex.GetType())));

    MethodInfo exceptionHandlerMethod = currentExceptionHandlerType.GetMethod("HandleException");

    var exceptionHanderInstance = Activator.CreateInstance(currentExceptionHandlerType);
    exceptionHandlerMethod.Invoke(exceptionHanderInstance, new object[] { ex });
}

РЕДАКТИРОВАТЬ: Конечновы захотите изолировать ваши обработчики так, чтобы стоимость отражения была меньше, возможно, поместите их в их собственную сборку или измените это, чтобы получать типы из пространства имен или чего-то такого, чтобы он не просматривал все типы в вашей основной сборке.

EDIT_01: ASP.Net Core действительно предоставляет некоторую конфигурацию для глобальной обработки исключений, только что натолкнулся на это https://docs.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-3.0

0 голосов
/ 11 ноября 2019

Вы можете использовать библиотеку Scrutor для сканирования сборки для всех реализаций любого интерфейса и автоматически регистрировать их с помощью этой библиотеки. Например:

services.Scan(scan => scan
  .FromCallingAssembly()
    .AddClasses(x=> x.AssignableTo(typeof(IOpenGeneric<>))) // Can close generic types
      .AsImplementedInterfaces()
      .WithScopedLifetime()
);

И затем вы можете вызвать IServiceProvider внутри промежуточного программного обеспечения, чтобы получить все зарегистрированные реализации, а затем вам нужно немного отражения или динамической диспетчеризации, чтобы выполнить Handle метод требуемогообработчик исключений. Кроме того, вы должны каким-то образом сохранять порядки обработчиков исключений. Позвольте мне объяснить, что я имею в виду. Например, в случае SqlException вы должны выполнить SqlExceptionHandler (более конкретно), а не ExceptionHandler. Как вы собираетесь узнать, какой обработчик является более конкретным?

Конечно, все это можно сделать. Но, я думаю, это внесет ненужную сложность в вашу кодовую базу.

Думаю, будет лучше создать одно базовое исключение Промежуточное программное обеспечение . А потом несколько производных промежуточных программ. И хорошо то, что, как отмечено в msdn :

Порядок добавления компонентов промежуточного программного обеспечения в метод Startup.Configure определяет порядок, в котором компоненты промежуточного программного обеспечения вызываютсяпо запросам.

Как видите, при таком подходе проблема заказа будет решена автоматически.

Вот пример кода:

 public abstract class BaseExceptionHandlingMiddleware
    {
        private readonly RequestDelegate _nextMiddlewareRequestDelegate;

        public ExceptionHandlingMiddleware(RequestDelegate nextMiddlewareRequestDelegate)
        {
            _nextMiddlewareRequestDelegate = nextMiddlewareRequestDelegate;
        }

        public async Task Invoke(HttpContext context)
        {
            try
            {
                await _nextMiddlewareRequestDelegate(context);
            }
            catch (Exception exception)
            {
                await HandleExceptionAsync(context, exception);
            }
        }

        private static Task HandleExceptionAsync(HttpContext context, Exception exception)
        {
            var errorDescription = exception.Message;
            var result = JsonConvert.SerializeObject(new { error = errorDescription });

            context.Response.ContentType = MediaTypeNames.Application.Json;
            context.Response.StatusCode = (int)DetermineStatusCode(exception);
            return context.Response.WriteAsync(result);
        }

        protected virtual HttpStatusCode DetermineStatusCode(Exception exception);
    }

И создайтепроизнесите SqlExceptionHandlinMiddleware, IOExceptionHandlinMiddleware, GeneralExceptionHandlinMiddleware, а затем просто переопределите DetermineStatusCode.

и затем зарегистрируйте их так:

builder.UseMiddleware<SqlExceptionHandlinMiddleware>();
builder.UseMiddleware<IOExceptionHandlinMiddleware>();
builder.UseMiddleware<GeneralExceptionHandlinMiddleware>();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...