I have implemented Mediatr in my .NET framework project and would like to use a IPipelineBehavior.
I have implemented and registered the container using the example from the project: https://github.com/jbogard/MediatR/blob/master/samples/MediatR.Examples.Unity/Program.cs
This is my Behavior
public class AuditPipelineBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
private readonly ILeaveAuditTrail _auditor;
public AuditPipelineBehavior(ILeaveAuditTrail auditor)
{
_auditor = auditor;
}
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
if (AuditPrevented(request))
return await next();
var response = await next();
var auditLog = _auditor.CreateAuditLog(request, response);
//Dispatch audit
return response;
}
private static bool AuditPrevented<TInput>(TInput query)
{
return query.GetType().GetCustomAttributes(typeof(PreventAuditAttribute), true).Any();
}
}
And i register is like this together with my Mediatr
container.RegisterMediator(new HierarchicalLifetimeManager());
container.RegisterMediatorHandlers(applicationAssembly);
container.RegisterType(typeof(IPipelineBehavior<,>), typeof(AuditPipelineBehavior<,>));
container.RegisterType<ILeaveAuditTrail, DefaultAuditor>();
When i send the IRequest with Mediatr it is handled fine and i get the results but the AuditPipeline is not called.
If i remove the async keyword and just return next(); It works. But this way i cannot correctly intercept my response.
Also on the example Github the handle is implemented async: https://github.com/jbogard/MediatR/wiki/Behaviors
The issue was not related to the registration of Mediatror the PipelineBehavior.
When calling the mediator.Send()method it was not in an async method. .Resultwas used instead of await. Because of this the handling of the pipeline only worked when the asynckeyword was not present.
Make sure to never use the .Result to chain these async calls. Mark the controller method async and await the mediator.Send()
Related
Injecting state into your HttpRequest when using IHttpClientFactory is achievable by populating HttpRequestMessage.Properties see Using DelegatingHandler with custom data on HttpClient
Now if I have third party extensions on HttpClient (such as IdentityModel), how would I intercept these http requests using custom state?
public async Task DoEnquiry(IHttpClientFactory factory)
{
var id = Database.InsertEnquiry();
var httpClient = factory.CreateClient();
// GetDiscoveryDocumentAsync is a third party extension method on HttpClient
// I therefore cannot inject or alter the request message to be handled by the InterceptorHandler
var discovery = await httpClient.GetDiscoveryDocumentAsync();
// I want id to be associated with any request / response GetDiscoveryDocumentAsync is making
}
The only plausible solution I currently have is to override HttpClient.
public class InspectorHttpClient: HttpClient
{
private readonly HttpClient _internal;
private readonly int _id;
public const string Key = "insepctor";
public InspectorHttpClient(HttpClient #internal, int id)
{
_internal = #internal;
_id = id;
}
public override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// attach data into HttpRequestMessage for the delegate handler
request.Properties.Add(Key, _id);
return _internal.SendAsync(request, cancellationToken);
}
// override all methods forwarding to _internal
}
A then I'm able to intercept these requests.
public async Task DoEnquiry(IHttpClientFactory factory)
{
var id = Database.InsertEnquiry();
var httpClient = new InspectorHttpClient(factory.CreateClient(), id);
var discovery = await httpClient.GetDiscoveryDocumentAsync();
}
Is that a plausible solution? Something tell me now not to override HttpClient. Quoting from https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient?view=net-5.0
The HttpClient also acts as a base class for more specific HTTP clients. An example would be a FacebookHttpClient providing additional methods specific to a Facebook web service (a GetFriends method, for instance). Derived classes should not override the virtual methods on the class. Instead, use a constructor overload that accepts HttpMessageHandler to configure any pre- or post-request processing instead.
I almost included this in my other answer as an alternative solution, but I figured it was too long already. :)
The technique is practically the same, but instead of HttpRequestMessage.Properties, use AsyncLocal<T>. "Async local" is kind of like thread-local storage but for a specific asynchronous code block.
There are a few caveats to using AsyncLocal<T> that aren't particularly well-documented:
Use an immutable nullable type for T.
When setting the async local value, return an IDisposable that resets it.
If you don't do this, then only set the async local value from an async method.
You don't have to follow these guidelines, but they will make your life much easier.
With that out of the way, the solution is similar to the last one, except it just uses AsyncLocal<T> instead. Starting with the helper methods:
public static class AmbientContext
{
public static IDisposable SetId(int id)
{
var oldValue = AmbientId.Value;
AmbientId.Value = id;
// The following line uses Nito.Disposables; feel free to write your own.
return Disposable.Create(() => AmbientId.Value = oldValue);
}
public static int? TryGetId() => AmbientId.Value;
private static readonly AsyncLocal<int?> AmbientId = new AsyncLocal<int?>();
}
Then the calling code is updated to set the ambient value:
public async Task DoEnquiry(IHttpClientFactory factory)
{
var id = Database.InsertEnquiry();
using (AmbientContext.SetId(id))
{
var httpClient = factory.CreateClient();
var discovery = await httpClient.GetDiscoveryDocumentAsync();
}
}
Note that there is an explicit scope for that ambient id value. Any code within that scope can get the id by calling AmbientContext.TryGetId. Using this pattern ensures that this is true for any code: synchronous, async, ConfigureAwait(false), whatever - all code within that scope can get the id value. Including your custom handler:
public class HttpClientInterceptor : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var id = AmbientContext.TryGetId();
if (id == null)
throw new InvalidOperationException("The caller must set an ambient id.");
// associate the id with this request
Database.InsertEnquiry(id.Value, request);
return await base.SendAsync(request, cancellationToken);
}
}
Followup readings:
Blog post on "async local" - written before AsyncLocal<T> existed, but has details on how it works. This answers the questions "why should T be immutable?" and "if I don't use IDisposable, why do I have to set the value from an async method?".
I have a AuthorizationHandler depending on a Service offering async methods for .NET Core 3.1's Authorization Middleware. I have o call some of these async methods inside the HandleRequirementAsync method. The overall code looks like this:
{
public class MyAuthorizationHandler : AuthorizationHandler<MyRequirement, Tuple<string, string>>
{
private readonly IAuthIntelRepository authIntelRepository;
public UserAssistanceAuthorizationHandler(IAuthIntelRepository authIntelRepository)
{
this.authIntelRepository = authIntelRepository;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MyRequirement requirement, Tuple<string, string> someRessource)
{
//some async calls to authIntelRepository
if (/*someCondition*/false)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
public class MyRequirement : IAuthorizationRequirement { }
}
As soon is I use an await statement though, I get an error that the signature isn't explicitly set as async. Adding async to the inherited method's signature causes the following error.
a return keyword must not be followed by an object expression. Did you intend to return 'Task<T>'?
This thread elaborates a similar issue but the solution doesn't seem to work in .NET Core 3.1.
Using Result in the following manner works, but AFAIK this will result in a blocking call:
Task<Object> obj= this.authIntelRepository.getSomeAsync(...);
obj.Result.property //do Something to check the requirement
I'm not sure what the correct solution would look like here.
If the return type of your async method is Task, then, apart from the await keyword, your treat your method as if it was void returning:
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, MyRequirement requirement, Tuple<string, string> someRessource)
{
await authIntelRepository....
if (/*someCondition*/false)
{
context.Succeed(requirement);
}
return;
}
Adding async to HandleRequirementAsync and use await to call the async method inside HandleRequirementAsync breaks the authorization, try calling external db or httpClient (delays), Type in the browser the route address with authorization. The route will be redirected to the non authorized page even though the context.Succeed(requirement) is executed .
The working solution for me (blazor server .NET 5) is keeping the HandleRequirementAsync as it is, execute the async method we need to call using pattern for executing async method inside non async method.
My sample working code derived from https://stackoverflow.com/a/43148321/423356
my sample async method:
public async Task<IList<Permission>> GetGroupPermissions(int userId)
{
HttpResponseMessage response = await _httpClient.GetAsync(string.Format("Auth/GroupPermissions/{0}", userId));
try
{
var payload = await response.Content.ReadFromJsonAsync<List<Permission>>();
response.EnsureSuccessStatusCode();
return payload;
}
catch
{
return new List<Permission>();
}
}
HandleRequirementAsync:
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
{
var t2 = (Task.Run(() => GetGroupPermissions(userId)));
t2.Wait();
var userGroupPermissions = t2.Result;
if (!userGroupPermissions.Contains(requirement.Permission))
{
//context.Fail(); //no need to fail, other requirement might success
return Task.CompletedTask;
}
context.Succeed(requirement);
return Task.CompletedTask;
}
I am using AzureFunctions.Autofac to inject into my Azure Functions web api. An example of the config:
public class DIConfig
{
public DIConfig()
{
DependencyInjection.Initialize(builder =>
{
// DAL
builder.Register<IDbContext>(c => new SecretCompanyContext()).InstancePerLifetimeScope();
builder.RegisterType<SecretCompanyContext>().InstancePerLifetimeScope();
builder.RegisterType<SecretCompanyContext>().As<ICartContext>().InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)).InstancePerLifetimeScope();
// Services
builder.RegisterType<InventoryServices>().As<IInventoryServices>().InstancePerLifetimeScope();
// Controllers ported from ASP.NET MVC Web API
builder.RegisterType<InventoryController>().InstancePerLifetimeScope();
});
}
Then my Azure functions, I have one class that defines all methods in the API
[DependencyInjectionConfig(typeof(DIConfig))]
public class InventoryFunctions : FunctionsApi
{
[FunctionName("GetProductsByCategory")]
// /inventory/categories/{id}/products
public static async Task<HttpResponseMessage> GetProductsByCategory(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "inventory/categories/{id}/products")]
HttpRequestMessage req,
TraceWriter log,
int id,
[Inject] InventoryController controller)
{
// do stuff
var result = await controller.GetProductsByCategory(id);
return JsonResponse(result, HttpStatusCode.OK);
}
[FunctionName("GetInventoryBySku")]
// /inventory/skus?sku=ASDF&sku=ASDG&sku=ASDH
public static async Task<HttpResponseMessage> GetInventoryBySku(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "inventory")]
HttpRequestMessage req,
TraceWriter log,
[Inject] InventoryController controller)
{
// do stuff
var result = await controller.QueryInventoryBySkuList(skuList);
return JsonResponse(result, HttpStatusCode.OK);
}
[FunctionName("UpdateProductsQuantity")]
// /inventory
// Post
public static async Task<HttpResponseMessage> UpdateProductsQuantity(
[HttpTrigger(AuthorizationLevel.Function, "put", Route = "inventory")]
HttpRequestMessage req,
TraceWriter log,
[Inject] InventoryController controller)
{
// do stuff
var inventoryProducts = await req.Content.ReadAsAsync<List<InvProductOperation>>();
var result = await controller.UpdateAvailableProductsQuantity(inventoryProducts);
return JsonResponse(result, HttpStatusCode.OK);
}
But I keep getting this error:
A second operation started on this context before a previous
asynchronous operation completed. Use 'await' to ensure that
any asynchronous operations have completed before calling
another method on this context. Any instance members are not
guaranteed to be thread safe.
I have verified that async and await are used properly, so following the error message's recommendation isn't fixing it. What appears to be the issue is that IDbContext is not honoring the InstancePerLifetimeScope as expected. Is this happening because I have more than one method in my InventoryFunctions class? Or is AzureFunctions.Autofac not threadsafe?
Change the registration of the DbContext to this:
builder.Register<IDbContext>(c => new SecretCompanyContext()).InstancePerDependency();
You can find a deeper explanation of mine for why this is happening here.
I was going by this SO answer: Autofac - InstancePerHttpRequest vs InstancePerLifetimeScope which said that InstancePerLifetimeScope was the non-ASP.NET equivalent of InstancePerRequest.
I spoke to the developers and they said the truth is that getting one DbContext per HttpRequest was the default behavior when you simply register using builder.RegisterType<SecretCompanyContext>.As<IDbContext>() so there's some misinformation out there.
So the solution is, instead of using
builder.Register<IDbContext>(c => new SecretCompanyContext()).InstancePerDependency();
or
builder.RegisterType<SecretCompanyContext>().As<IDbContext>().InstancePerLifetimeScope();
one should just use
builder.RegisterType<SecretCompanyContext>().As<IDbContext>();
if the goal is one instance per HTTP request.
I use the CQS pattern in my asp.net core project. Let's start with an example to better explain what I want to achieve. I created a command:
public class EmptyCommand : INotification{}
The command handler:
public class EmptyCommandHandler : INotificationHandler<EmptyCommand>
{
public Task Handle(EmptyCommand notification, CancellationToken cancellationToken)
{
return Task.FromResult(string.Empty);
}
}
The query:
public class EmptyQuery : IRequest<string>{}
The query handler:
public class EmptyQueryHandler : IRequestHandler<EmptyQuery, string>
{
public Task<string> Handle(EmptyQuery notification, CancellationToken cancellationToken)
{
return Task.FromResult(string.Empty);
}
}
and this is a simple example of how to run the command and query and invoke the Handle method from the EmptyCommandHandler and EmptyQueryHandler:
readonly IMediator _mediator;
public HomeController(IMediator mediator)
{
_mediator = mediator;
}
public async Task<IActionResult> Index()
{
await _mediator.Publish(new EmptyCommand());
var queryResult = await _mediator.Send(new EmptyQuery());
return View();
}
Please bear in mind that query can return other types not necessarily the string.
I would like to create some kind of a bridge class e.g. MediatorBoostrapper, which allows me to run some business logic(e.g. log command/query via Logger) every time the Publish method is invoked and then
invoke the public Task Handle(EmptyCommand notification,... method from the command handler. The solution must be generic, so this method would be invoked every time I run the Publish method. I also want to be able to do the same thing for the Send method.
I was thinking about the creation of the public class MediatorBoostrapper : IMediator
but not sure what should be a proper implementation of the class and if my idea is good.
Any ideas? Cheers
Edit
I want to have an example of how to use the Behaviors
to create a generic way to run some external method from the generic handler every time I Run the Send method for queries. I want to have a similar example for Publish method, which I use for sending commands.
I want to have an example of how to use Polymorphic dispatch
for the creation of the GenericCommandHandler and a GenericQueryHandler
I created a sample project on GitHub which can be found here
You can feel free to try to extend this project with your solution.
This time I want to answer the question starting from the end.
2.
TL;DR Polymorphic Dispatch cannot be used for the CQS
After some time of playing with the MediatR library, reading the comments under my Question and consultation with my friend, I found the Polymorphic Dispatch(PD) can be used to create a generic handler only in case of the Commands. The PD solution cannot be implemented for Queries. Based on the Documentation, the handlers are contravariant and not covariant. This means the PD works only in the case where the TResponse is a constant type. In case of the Queries, this is false and each Query handler can return a different result.
I also found this issue. I think it's interesting to know you can use the Polymorphic Dispatch only if your container supports it.
1. Behaviors is the one and only solution for CQS when using the MediatR.
Based on the comment under my question from #Steve and comment from jbogard I've found the way how to use Behaviors and IRequestHandler for the strict Command pattern. The full comment:
Just to summarize the changes, there are 2 main flavors of requests:
those that return a value, and those that do not. The ones that do not
now implement IRequest<T> where T : Unit. This was to unify requests
and handlers into one single type. The diverging types broke the
pipeline for many containers, the unification means you can use
pipelines for any kind of request.
It forced me to add the Unit type in all cases, so I've added some helper classes for you.
IRequestHandler<T> - implement this and you will return Task<Unit>.
AsyncRequestHandler<T> - inherit this and you will return Task.
RequestHandler<T> - inherit this and you will return nothing (void).
For requests that do return values:
IRequestHandler<T, U> - you will return Task<U>
RequestHandler<T, U> - you will return U
I got rid of the AsyncRequestHandler because it really wasn't doing anything after the consolidation, a redundant base class.
The example
a) The Commands management:
public class EmptyCommand : IRequest{...}
public class EmptyCommandHandler : RequestHandler<EmptyCommand>
{
protected override void Handle(EmptyCommand request){...}
}
b) The Queries management:
// can be any other type not necessarily `string`
public class EmptyQuery : IRequest<string>{...}
public class EmptyQueryHandler : IRequestHandler<EmptyQuery, string>
{
public Task<string> Handle(EmptyQuery notification, CancellationToken cancellationToken)
{
return Task.FromResult("Sample response");
}
}
c) The sample LogginBehavior class:
public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
readonly ILogger<LoggingBehavior<TRequest, TResponse>> _logger;
public LoggingBehavior(ILogger<LoggingBehavior<TRequest, TResponse>> logger)
{
_logger = logger;
}
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
var requestType = typeof(TRequest).Name;
var response = await next();
if (requestType.EndsWith("Command"))
{
_logger.LogInformation($"Command Request: {request}");
}
else if (requestType.EndsWith("Query"))
{
_logger.LogInformation($"Query Request: {request}");
_logger.LogInformation($"Query Response: {response}");
}
else
{
throw new Exception("The request is not the Command or Query type");
}
return response;
}
}
d) To register the LoggingBehavior add the command
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));
to the body of the ConfigureServices method in the Startup.cs.
e) The example of how to run sample command and query:
await _mediator.Send(new EmptyCommand());
var result = await _mediator.Send(new EmptyQuery());
MediatR supports dispatching notifications to generic handlers (polymorphic dispatch). For example:
public class GenericHandler<TNotification> : INotificationHandler<TNotification>
where TNotification : INotification
{
public Task Handle(TNotification notification, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
This handler will be invoked for every notification that is published through Publish(). The same is true for requests (queries/commands). You should also take a look at behaviors.
If you're using MediatR with ASP.NET Core I suggest you use the MediatR.Extensions.Microsoft.DependencyInjection library which takes care of wiring all the handlers together.
I have a synchronous, generic method that looks like this
public TResponse Execute<TResponse>(Request request) where TResponse : Response
{
return (TResponse) proxy.ExecuteRequest(request);
the proxy is a WCF service reference
It just has one method that takes a request and returns a response. But it is used by passing derived requests and returning derived responses. As you can see above the wrapper method is casting the response to the derived type specified by the generic parameter (TResponse).
You call the method with derived requests and responses
e.g.
Execute<GetSomeDataResponse>(new GetSomeDataRequest());
I am now generating an async service reference so can make use of Tasks
So I would like a method that looks like this
public Task<TResponse> ExecuteAsync<TResponse>(Request request) where TResponse : Response
{
// need to cast to a Task<TResponse>
return proxy.ExecuteRequestAsync(request
that can be called like this
Task<GetSomeDataResponse> res = ExecuteAsync<GetSomeDataResponse>(new GetSomeDataRequest());
So I need a way to cast the Task<Response> to a Task<TResponse>
I've been reading this which seems kind of the opposite of what I need, but cant quite figure out how to bend it to my use case
How to convert a Task<TDerived> to a Task<TBase>?
any ideas?
Easy way is use async\await pattern:
public static async Task<TResponse> ExecuteAsync<TResponse>(Request request) where TResponse : Response {
var response = await proxy.ExecuteRequestAsync(request);
return (TResponse) response;
}
A bit more complicated (taken from your linked question) is to use TaskCompletionSource:
public static Task<TResponse> ExecuteAsync2<TResponse>(Request request) where TResponse : Response {
var tcs = new TaskCompletionSource<TResponse>();
proxy.ExecuteRequestAsync(request).ContinueWith(t => {
if (t.IsFaulted)
tcs.TrySetException(t.Exception.InnerExceptions);
else if (t.IsCanceled)
tcs.TrySetCanceled();
else
tcs.TrySetResult((TResponse) t.Result);
}, TaskContinuationOptions.ExecuteSynchronously);
return tcs.Task;
}