I have a scenario, where I would like to Enable/Disable a PurchaseOrderItem
So I have written two separate Command and commandHandlers. Ideally I have to create base abstract class and need to move my logic to one place.
How I can achieve this?
You can do it like this:
Commands:
enum CommandType {
Disable,
Enable
}
class EnableCommand : IRequest {
}
class DisableCommand : IRequest {
}
class CommonCommand : IRequest {
public CommandType Type {get; set;}
}
Handlers:
class EnableCommandHandler : IRequestHandler<EnableCommand> {
private readonly IMediator _mediator;
public EnableCommandHandler(IMediator mediator) {
_mediator = mediator;
}
public async Task Handle(EnableCommand command, CancellationToken cancellationToken) {
await _mediator.Send(new CommonCommand {Type = CommandType.Enable});
}
}
class DisableCommandHandler : IRequestHandler<DisableCommand> {
private readonly IMediator _mediator;
public EnableCommandHandler(IMediator mediator) {
_mediator = mediator;
}
public async Task Handle(DisableCommand command, CancellationToken cancellationToken) {
await _mediator.Send(new CommonCommand {Type = CommandType.Disable});
}
}
class CommonCommandHandler : IRequestHandler<CommonCommand> {
private readonly IMediator _mediator;
public EnableCommandHandler(IMediator mediator) {
_mediator = mediator;
}
public async Task Handle(CommonCommand command, CancellationToken cancellationToken) {
... // some common logic
switch(command.Type) {
... // some specific logic
}
}
}
And if you don't want to create 3 handlers you can create one for all this commands:
class CommonCommandHandler : IRequestHandler<EnableCommand> , IRequestHandler<DisableCommand>, IRequestHandler<CommonCommand> {
public async Task Handle(EnableCommand command, CancellationToken cancellationToken) {
await Handle(new CommonCommand {Type = CommandType.Enable});
}
public async Task Handle(DisableCommand command, CancellationToken cancellationToken) {
await Handle(new CommonCommand {Type = CommandType.Disable});
}
public async Task Handle(CommonCommand command, CancellationToken cancellationToken) {
... // some common logic
switch(command.Type) {
... // some specific logic
}
}
}
For last options you actually don't need CommonCommand as IRequest. So idea is to re-use handlers.
You can solve this problem with one command/handler.
public enum PurchaseOrderStatus
{
Enabled,
Disabled
}
public class TogglePurchaseOrderItemStatusCommand : IRequest
{
public PurchaseOrderStatus Status { get; }
public TogglePurchaseOrderItemStatusCommand(PurchaseOrderStatus status)
{
Status = status;
}
}
public class TogglePurchaseOrderItemCommandHandler : IRequestHandler<TogglePurchaseOrderItemCommand>
{
public Task<Unit> Handle(TogglePurchaseOrderItemCommand request, CancellationToken cancellationToken)
{
//Execute Logic Here Based on request.Status
}
}
Related
I have several classes that have inherited from one interface. I want the desired service to be loaded and used in the controller depending on the conditions.
Controller
public class GatewayController
{
private readonly IAction action;
public GatewayController(IAction action)
{
this.action = action;
}
[HttpPost("gateway")]
public async Task<ApiResult> Gateway(GatewayRequest gatewayRequest, CancellationToken cancellationToken)
{
try
{
var comId = gatewayRequest.CommandId;
switch (comId)
{
case (int) GuaranteeItemStatus.End:
return await action.Perform(gatewayRequest, cancellationToken); //must be use EndActionService
case (int) GuaranteeItemStatus.SendProduct:
return await action.Perform(gatewayRequest, cancellationToken); //must be use SendProductActionService
default:
return null;
}
}
catch (Exception ex)
{
return ApiResult.ToErrorModel("error");
}
}
}
Parent interface:
public interface IAction
{
Task<ApiResult> Perform(GatewayRequest gatewayRequest, CancellationToken cancellationToken);
}
Services:
1-SendProductActionService:
public class SendProductActionService:IAction
{
public async Task<ApiResult> Perform(GatewayRequest gatewayRequest, CancellationToken cancellationToken)
{
return ApiResult.ToSuccessModel("SendProduct");
}
}
2-EndActionService:
public class EndActionService:IAction
{
public async Task<ApiResult> Perform(GatewayRequest gatewayRequest, CancellationToken cancellationToken)
{
return ApiResult.ToSuccessModel("EndAction");
}
}
It's possible to register, and inject, an IEnumerable that contains all your IActions.
First, to identify which IAction reacts to which command, you can add a CommandId property:
public interface IAction
{
int CommandId { get; }
}
public class SendProductActionService : IAction
{
public int CommandId => (int)GuaranteeItemStatus.SendProduct;
}
public class EndActionService : IAction
{
public int CommandId => (int)GuaranteeItemStatus.End;
}
In your Startup.cs, you register all your actions:
services.AddScoped<IAction, SendProductActionService>();
services.AddScoped<IAction, EndActionService>();
Then in your controller, you inject all the IAction, and select the appropriate one when needed:
public class GatewayController
{
// map the command ID to the proper IAction
private readonly Dictionary<int, IAction> actions;
// inject all the services registered that implement IAction
public GatewayController(IEnumerable<IAction> actions)
{
this.actions = actions.ToDictionary(_ => _.CommandId);
}
[HttpPost("gateway")]
public async Task<ApiResult> Gateway(GatewayRequest gatewayRequest, CancellationToken cancellationToken)
{
// find the appropriate IAction
if (!actions.TryGetValue((int)gatewayRequest.CommandId, out var action)
return BadRequest();
return await action.Perform(gatewayRequest, cancellationToken);
}
}
In your startup.cs:
services.AddScoped<SendProductActionService>();
services.AddScoped<EndActionService>();
services.AddScoped<Func<GuaranteeItemStatus, IAction>>(serviceProvider => status =>
{
switch (status)
{
case GuaranteeItemStatus.SendProduct:
return serviceProvider.GetService<SendProductActionService>();
case GuaranteeItemStatus.End:
return serviceProvider.GetService<EndActionService>();
default:
throw new InvalidOperationException();
}
});
Your controller should be similar to this:
public class GatewayController
{
private readonly Func<GuaranteeItemStatus, IAction> actionProvider;
public GatewayController(Func<GuaranteeItemStatus, IAction> actionProvider)
{
this.actionProvider = actionProvider;
}
[HttpPost("gateway")]
public async Task<ApiResult> Gateway(GatewayRequest gatewayRequest, CancellationToken cancellationToken)
{
try
{
return await actionProvider((GuaranteeItemStatus)gatewayRequest.CommandId)
.Perform(gatewayRequest, cancellationToken);
}
catch (Exception ex)
{
return ApiResult.ToErrorModel("error");
}
}
}
I am trying to implement a background service, QueuedBackground service with Mediator.
So far I am able to implement the queues but I am unable to execute Mediator.
Interface
public interface IBackgroundTaskQueueService
{
void QueueBackgroundWorkItem(object workItem, CancellationToken token);
Task<object> DequeueAsync(
CancellationToken cancellationToken);
}
Implementation
public class BackgroundTaskQueueService : IBackgroundTaskQueueService
{
private readonly ConcurrentQueue<(object,CancellationToken)> _workItems =
new ConcurrentQueue<(object,CancellationToken)>();
private SemaphoreSlim _signal = new SemaphoreSlim(0);
public void QueueBackgroundWorkItem(object workItem, CancellationToken token)
{
if (workItem == null)
{
throw new ArgumentNullException(nameof(workItem));
}
_workItems.Enqueue((workItem,token));
_signal.Release();
}
public async Task<object> DequeueAsync( CancellationToken cancellationToken)
{
await _signal.WaitAsync(cancellationToken);
_workItems.TryDequeue(out var workItem);
return workItem.Item1;
}
}
Background Service
public class QueuedHostedService : BackgroundService
{
private readonly ILogger _logger;
private readonly IMediator _mediator;
public QueuedHostedService(IBackgroundTaskQueueService taskQueue, ILoggerFactory loggerFactory, IMediator mediator)
{
TaskQueue = taskQueue;
_mediator = mediator;
_logger = loggerFactory.CreateLogger<QueuedHostedService>();
}
public IBackgroundTaskQueueService TaskQueue { get; }
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (false == stoppingToken.IsCancellationRequested)
{
try
{
var workItem = await TaskQueue.DequeueAsync(stoppingToken);
await _mediator.Send(workItem, stoppingToken);
// await _mediator.Send(new UpdateProductCostByMaterialRequestModel()
// {
// Id = 1
// }, stoppingToken);
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error occurred executing Work item.");
}
}
}
}
Usage
_queueService.QueueBackgroundWorkItem(new UpdateProductCostByMaterialRequestModel()
{
Id = request.ProductId
}, CancellationToken.None);
Now with the above code I am able to receive the class object but when I pass it in the Mediator I get InvalidOperation Handler not registered.
I am confused.
Okay I found the issue
Instead of passing it from the Constructor I had to use the ServiceFactory Interface
My Solution
BackgroundService is a Singleton. You cannot inject a Scoped into a Singleton.
public class QueuedHostedService : BackgroundService
{
private readonly ILogger _logger;
private readonly IServiceScopeFactory _serviceScopeFactory;
public QueuedHostedService(IBackgroundTaskQueueService taskQueue, ILoggerFactory loggerFactory, IServiceScopeFactory serviceScopeFactory)
{
TaskQueue = taskQueue;
_serviceScopeFactory = serviceScopeFactory;
_logger = loggerFactory.CreateLogger<QueuedHostedService>();
}
public IBackgroundTaskQueueService TaskQueue { get; }
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
using var scope = _serviceScopeFactory.CreateScope();
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
while (false == stoppingToken.IsCancellationRequested)
{
try
{
var workItem = await TaskQueue.DequeueAsync(stoppingToken);
if (workItem is IRequest<object> item)
{
await mediator.Send(workItem, stoppingToken);
}
// await _mediator.Send(new UpdateProductCostByMaterialRequestModel()
// {
// Id = 1
// }, stoppingToken);
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error occurred executing Work item.");
}
}
}
}
My Mediatr is using the SyncContinueOnException publish strategy, is there any way to run some validation before start the propagation?
Example:
_mediatr.Publish(new MyNotification());
public class MyValidationHandler :
INotificationHandler<MyValidationHandler>
{
Task INotificationHandler<MyValidationHandler>.Handle(MyValidationHandler notification, CancellationToken cancellationToken)
{
// STOP propagation if some condition is false
}
}
public class FirstHandlers :
INotificationHandler<MyNotification>
{
Task INotificationHandler<MyNotification>.Handle(MyNotification notification, CancellationToken cancellationToken)
{
Console.WriteLine("x");
return Task.CompletedTask;
}
}
public class SecondHandlers :
INotificationHandler<MyNotification>
{
Task INotificationHandler<MyNotification>.Handle(MyNotification notification, CancellationToken cancellationToken)
{
Console.WriteLine("x");
return Task.CompletedTask;
}
}
Update Sorry, misread this originally!
One way to wrap the MediatR Publish behavior is to decorate the IMediator instance itself, either manually or with a library like Scrutor. You can add a marker interface to identify notifications that should pass through your validation logic, with any other events just flowing through to MediatR.
public class MediatorDecorator : IMediator
{
private readonly IMediator _mediator;
public MediatorDecorator(IMediator mediator)
{
_mediator = mediator;
}
public Task Publish<TNotification>(TNotification notification, CancellationToken cancellationToken = default) where TNotification : INotification
{
if (notification is IValidatableNotification)
{
// Your validation behavior here
return Task.CompletedTask; // if the validation fails, respond as you'd like ...
}
return _mediator.Publish(notification, cancellationToken);
}
// ...
}
And in Startup, after the MediatR registration, using Scrutor's Decorate:
services.AddMediatR(typeof(Startup));
services.Decorate<IMediator, MediatorDecorator>();
I am receiving a Microsoft.Azure.ServiceBus.ServiceBusException (message below with sensitive information removed) periodically in my queue receiver. The SAS key has send/listen access and the error seems inconsequential as processing continues as normal. However, the message is creating a signal to noise problem in my dashboards (receiving 10-70 errors per day). Any ideas on why this is happening? The listener is running in an Azure App Service, but I don't think that matters. I have adjusted my retry logic to use a RetryExponential with a 1 second to 1 minute backoff with 5 retries.
Request for guidance from SDK developers
Packages
Net Core 3.1
Microsoft.Azure.ServiceBus, Version=4.1.3.0, Culture=neutral, PublicKeyToken=7e34167dcc6d6d8c
Error Message
The link 'xxx;xxx:xxx:xxx:source(address:xxx):xxx' is force detached. Code: RenewToken. Details: Unauthorized access. 'Listen' claim(s) are required to perform this operation. Resource: 'sb://xxx.servicebus.windows.net/xxx'.. TrackingId:xxx, SystemTracker:xxx, Timestamp:2020-04-27T09:36:04 The link 'xxx;xxx:xxx:xxx:source(address:xxx):xxx' is force detached. Code: RenewToken. Details: Unauthorized access. 'Listen' claim(s) are required to perform this operation. Resource: 'sb://xxx.servicebus.windows.net/xxx'.. TrackingId:xxx, SystemTracker:xxx, Timestamp:2020-04-27T09:36:04
Source
internal delegate TClient ClientFactory<out TClient>(string connectionString, string entityPath,
RetryPolicy retryPolicy);
internal delegate Task OnMessageCallback<in TMessage>(TMessage message,
CancellationToken cancellationToken = default) where TMessage : ICorrelative;
internal sealed class ReceiverClientWrapper<TMessage> : IReceiverClientWrapper<TMessage>
where TMessage : ICorrelative
{
// ReSharper disable once StaticMemberInGenericType
private static readonly Regex TransientConnectionErrorRegex =
new Regex(
#"(The link '([a-f0-9-]+);([0-9]*:)*source\(address:([a-z0-9_]+)\):([a-z0-9_]+)' is force detached. Code: RenewToken. Details: Unauthorized access. 'Listen' claim\(s\) are required to perform this operation. Resource: 'sb:\/\/([a-z0-9-_.\/]+)'.. TrackingId:([a-z0-9_]+), SystemTracker:([a-z0-9]+), Timestamp:([0-9]{4}(-[0-9]{2}){2}T([0-9]{2}:){2}[0-9]{2}) )+",
RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase);
private readonly IReceiverClient _receiverClient;
private readonly IMessageConverter<TMessage> _messageConverter;
private readonly ILogger _logger;
private readonly int _maximumConcurrency;
public ReceiverClientWrapper(IReceiverClient receiverClient, IMessageConverter<TMessage> messageConverter,
ILogger logger, int maximumConcurrency)
{
_receiverClient = receiverClient;
_messageConverter = messageConverter;
_logger = logger;
_maximumConcurrency = maximumConcurrency;
}
public Task SubscribeAsync(OnMessageCallback<TMessage> onMessageCallback,
OnFailureCallback onFailureCallback, CancellationToken cancellationToken = default)
{
var messageHandlerOptions = CreateMessageHandlerOptions(onFailureCallback, cancellationToken);
async Task Handler(Message message, CancellationToken token)
{
var convertedMessage = _messageConverter.Convert(message);
await onMessageCallback(convertedMessage, cancellationToken);
await _receiverClient.CompleteAsync(message.SystemProperties.LockToken);
}
_receiverClient.RegisterMessageHandler(Handler, messageHandlerOptions);
return Task.CompletedTask;
}
private MessageHandlerOptions CreateMessageHandlerOptions(OnFailureCallback onFailureCallback,
CancellationToken cancellationToken)
{
async Task HandleExceptionAsync(ExceptionReceivedEventArgs arguments)
{
var exception = arguments.Exception;
if (TransientConnectionErrorRegex.IsMatch(exception.Message))
{
_logger.LogWarning(exception, #"Transient connectivity error occurred");
return;
}
await onFailureCallback(exception, cancellationToken);
}
return new MessageHandlerOptions(HandleExceptionAsync)
{
AutoComplete = false,
MaxConcurrentCalls = _maximumConcurrency
};
}
public async ValueTask DisposeAsync()
{
await _receiverClient.CloseAsync();
}
}
internal sealed class SenderClientWrapper<TMessage> : ISenderClientWrapper<TMessage> where TMessage : ICorrelative
{
private readonly ISenderClient _senderClient;
private readonly IMessageConverter<TMessage> _messageConverter;
public SenderClientWrapper(ISenderClient senderClient, IMessageConverter<TMessage> messageConverter)
{
_senderClient = senderClient;
_messageConverter = messageConverter;
}
public Task SendAsync(TMessage message, CancellationToken cancellationToken = default)
{
var internalMessage = _messageConverter.Convert(message);
return _senderClient.SendAsync(internalMessage);
}
public Task SendAsync(IEnumerable<TMessage> messages, CancellationToken cancellationToken = default)
{
var internalMessages = messages
.Select(_messageConverter.Convert)
.ToImmutableArray();
return _senderClient.SendAsync(internalMessages);
}
public async ValueTask DisposeAsync()
{
await _senderClient.CloseAsync();
}
}
internal abstract class AbstractClientWrapperFactory
{
private const int MaximumRetryCount = 5;
private static readonly TimeSpan MinimumRetryBackOff = TimeSpan.FromSeconds(1);
private static readonly TimeSpan MaximumRetryBackOff = TimeSpan.FromMinutes(1);
protected AbstractClientWrapperFactory(IOptions<MessageBusConfiguration> options)
{
Options = options;
}
protected IOptions<MessageBusConfiguration> Options { get; }
protected static string GetEntityPath<TMessage>() where TMessage : class
{
var messageAttribute = typeof(TMessage).GetCustomAttribute<AbstractMessageAttribute>();
if (messageAttribute == null)
{
throw new ArgumentException($#"Message requires {nameof(AbstractMessageAttribute)}");
}
return messageAttribute.EntityName;
}
protected TClient CreateClientEntity<TMessage, TClient>(ClientFactory<TClient> clientFactory)
where TMessage : class
{
var entityPath = GetEntityPath<TMessage>();
var retryPolicy = CreateRetryPolicy();
return clientFactory(Options.Value.ConnectionString, entityPath, retryPolicy);
}
protected static IQueueClient QueueClientFactory(string connectionString, string entityPath,
RetryPolicy retryPolicy)
{
return new QueueClient(connectionString, entityPath, retryPolicy: retryPolicy);
}
private static RetryPolicy CreateRetryPolicy()
{
return new RetryExponential(MinimumRetryBackOff, MaximumRetryBackOff, MaximumRetryCount);
}
}
internal sealed class SenderClientWrapperFactory : AbstractClientWrapperFactory, ISenderClientWrapperFactory
{
private readonly IMessageConverterFactory _messageConverterFactory;
public SenderClientWrapperFactory(IMessageConverterFactory messageConverterFactory,
IOptions<MessageBusConfiguration> options) : base(options)
{
_messageConverterFactory = messageConverterFactory;
}
public ISenderClientWrapper<TEvent> CreateTopicClient<TEvent>() where TEvent : class, IEvent
{
return CreateWrapper<TEvent, ITopicClient>(TopicClientFactory);
}
public ISenderClientWrapper<TRequest> CreateQueueClient<TRequest>() where TRequest : class, IRequest
{
return CreateWrapper<TRequest, IQueueClient>(QueueClientFactory);
}
private ISenderClientWrapper<TMessage> CreateWrapper<TMessage, TClient>(ClientFactory<TClient> clientFactory)
where TMessage : class, ICorrelative
where TClient : ISenderClient
{
var clientEntity = CreateClientEntity<TMessage, TClient>(clientFactory);
var messageConverter = _messageConverterFactory.Create<TMessage>();
return new SenderClientWrapper<TMessage>(clientEntity, messageConverter);
}
private static ITopicClient TopicClientFactory(string connectionString, string entityPath,
RetryPolicy retryPolicy)
{
return new TopicClient(connectionString, entityPath, retryPolicy);
}
}
internal sealed class ReceiverClientWrapperFactory : AbstractClientWrapperFactory, IReceiverClientWrapperFactory
{
private readonly IMessageConverterFactory _messageConverterFactory;
private readonly ILogger<ReceiverClientWrapperFactory> _logger;
public ReceiverClientWrapperFactory(IOptions<MessageBusConfiguration> options,
IMessageConverterFactory messageConverterFactory,
ILogger<ReceiverClientWrapperFactory> logger) : base(options)
{
_messageConverterFactory = messageConverterFactory;
_logger = logger;
}
public IReceiverClientWrapper<TEvent> CreateTopicClient<TEvent>() where TEvent : class, IEvent
{
return CreateReceiverClientWrapper<TEvent, ISubscriptionClient>(SubscriptionClientFactory);
}
public IReceiverClientWrapper<TRequest> CreateQueueClient<TRequest>() where TRequest : class, IRequest
{
return CreateReceiverClientWrapper<TRequest, IQueueClient>(QueueClientFactory);
}
private IReceiverClientWrapper<TMessage> CreateReceiverClientWrapper<TMessage, TClient>(
ClientFactory<TClient> clientFactory)
where TMessage : class, ICorrelative
where TClient : IReceiverClient
{
var clientEntity = CreateClientEntity<TMessage, TClient>(clientFactory);
var messageConverter = _messageConverterFactory.Create<TMessage>();
return new ReceiverClientWrapper<TMessage>(clientEntity, messageConverter, _logger,
Options.Value.MaximumConcurrency);
}
private ISubscriptionClient SubscriptionClientFactory(string connectionString, string entityPath,
RetryPolicy retryPolicy)
{
return new SubscriptionClient(connectionString, entityPath, Options.Value.SubscriberName,
retryPolicy: retryPolicy);
}
}
internal sealed class RequestService<TRequest> : IRequestService<TRequest> where TRequest : class, IRequest
{
private readonly Lazy<ISenderClientWrapper<TRequest>> _senderClient;
private readonly Lazy<IReceiverClientWrapper<TRequest>> _receiverClient;
public RequestService(ISenderClientWrapperFactory senderClientWrapperFactory,
IReceiverClientWrapperFactory receiverClientWrapperFactory)
{
_senderClient =
new Lazy<ISenderClientWrapper<TRequest>>(senderClientWrapperFactory.CreateQueueClient<TRequest>,
LazyThreadSafetyMode.PublicationOnly);
_receiverClient
= new Lazy<IReceiverClientWrapper<TRequest>>(receiverClientWrapperFactory.CreateQueueClient<TRequest>,
LazyThreadSafetyMode.PublicationOnly);
}
public Task PublishRequestAsync(TRequest requestMessage, CancellationToken cancellationToken = default)
{
return _senderClient.Value.SendAsync(requestMessage, cancellationToken);
}
public Task PublishRequestAsync(IEnumerable<TRequest> requestMessages,
CancellationToken cancellationToken = default)
{
return _senderClient.Value.SendAsync(requestMessages, cancellationToken);
}
public Task SubscribeAsync(OnRequestCallback<TRequest> onRequestCallback, OnFailureCallback onFailureCallback,
CancellationToken cancellationToken = default)
{
return _receiverClient
.Value
.SubscribeAsync((message, token) => onRequestCallback(message, cancellationToken), onFailureCallback,
cancellationToken);
}
public async ValueTask DisposeAsync()
{
if (_senderClient.IsValueCreated)
{
await _senderClient.Value.DisposeAsync();
}
if (_receiverClient.IsValueCreated)
{
await _receiverClient.Value.DisposeAsync();
}
}
public Task ThrowIfNotReadyAsync(CancellationToken cancellationToken = default)
{
return _senderClient.Value.SendAsync(ImmutableArray<TRequest>.Empty, cancellationToken);
}
}
This was never resolved using the original Nuget package, but the new Azure.Messaging.ServiceBus package does not appear to have the issue. I have opted to move to that.
I'm trying to understand how to set up pipeline behaviours with mediator.
My command:
public class MyUpdateCommand : IRequest<CommandResult>
{
// fields
}
My handler:
public class MyUpdateCommandHandler : RequestHandler<MyUpdateCommand, CommandResult>
{
private readonly IMyRepository _repository;
public GasDetailsUpdateCommandHandler(IMyRepository repository)
{
_repository = repository;
}
protected override CommandResult HandleCore(MyUpdateCommand command)
{
{
_repository.Update(command);
return CommandResult.Success;
}
}
}
Now, let's say I want to handle all exceptions raised in RequestHandlers that return a CommandResult by logging them and returning the message in my CommandResult object. I've created this class:
public class ExceptionLoggingBehavior<TRequest, TResponse>
: IPipelineBehavior<TRequest, TResponse> where TResponse : CommandResult
{
private readonly ILog _logger;
public ExceptionLoggingBehavior(ILog logger)
{
_logger = logger;
}
public async Task<TResponse> Handle(TRequest request,
CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
try
{
return await next();
}
catch (Exception ex)
{
_logger.Error(ex.Message);
return (TResponse) CommandResult.Fail(ex.Message);
}
}
}
What do I need to put into my inversion of control container (AutoFac)?