IHandler
IHandler<Request,Response> is an interface for classes that process Request queries.
/// <summary>Handler that processes <typeparamref name="Request"/> types.</summary>
public interface IHandler<in Request, out Response> : IHandlerT where Request : notnull
{
/// <summary>Handle <paramref name="query"/>. </summary>
/// <param name="query">Query object that contains request and response</param>
/// <exception cref="Exception">Throw on unexpected runtime error. The caller should mark the error as <see cref="EntryStatus.Error"/>.</exception>
void Handle(IQuery<Request, Response> query);
}
IHandlerAsync<Request,Response> is for classes that process Request queries asynchronously.
/// <summary>Handler that processes <typeparamref name="Request"/> types.</summary>
public interface IHandlerAsync<in Request, out Response> : IHandlerAsyncT where Request : notnull
{
/// <summary>Handle <paramref name="query"/>. </summary>
/// <param name="query">Query object that contains request and response</param>
/// <exception cref="Exception">Throw on unexpected runtime error. The caller should mark the error as <see cref="EntryStatus.Error"/>.</exception>
Task HandleAsync(IQuery<Request, Response> query);
}
IHandler is for classes that process all types of queries.
/// <summary>Handler with generic method that processes all request response types.</summary>
public interface IHandler : IHandlerBaseSync
{
/// <summary>Handle <paramref name="query"/>. </summary>
/// <param name="query">Query object that contains request and response</param>
/// <exception cref="Exception">Throw on unexpected runtime error. The caller should mark the error as <see cref="EntryStatus.Error"/>.</exception>
void Handle<Request, Response>(IQuery<Request, Response> query) where Request : notnull;
}
IHandlerAsync is for classes that process all types of queries asynchronously.
/// <summary>Handler with generic method that processes all request response types.</summary>
public interface IHandlerAsync : IHandlerBaseAsync
{
/// <summary>Handle <paramref name="query"/>. </summary>
/// <param name="query">Query object that contains request and response</param>
/// <exception cref="Exception">Throw on unexpected runtime error. The caller should mark the error as <see cref="EntryStatus.Error"/>.</exception>
Task HandleAsync<Request, Response>(IQuery<Request, Response> query) where Request : notnull;
}
Implementation
IHandler<Request, Response> handles a specific Request type, and returns a specific Response type.
public class Handler : IHandler<TypeRequest, Type>
{
public void Handle(IQuery<TypeRequest, Type> query)
{
// Assign result
query.Response.SetValue(typeof(string));
}
}
IHandler<object, object> is forwarded with all requests classes and structs. This, however, causes value-typed requests to be boxed, which adds a bit of heap garbage.
public class Handler5 : IHandler<object, object>
{
public void Handle(IQuery<object, object> query)
{
// Already handled
if (query.Handled()) return;
// Assign result
query.Response.SetValue(typeof(string));
}
}
IHandlerAsync<Request, Response> and IHandlerAsync process queries asynchronously.
public class DownloadUrl : IHandlerAsync<string, string>
{
public async Task HandleAsync(IQuery<string, string> query)
{
// Already handled
if (query.Handled()) return;
// Create client
WebClient client = new WebClient();
// Get url
string url = query.Request;
// Download content
string content = await client.DownloadStringTaskAsync(url);
// Write result
query.Response.SetValue(content);
}
}
IHandlerBase interface allows generic method type parameters Handle<T>(IQuery<,>).
public class Handler13 : IHandlerBase
{
[Order(1000)]
public void Handle<T>(IQuery<ScopedServiceRequest<T>, object> query)
{
}
}
And same for async method Task HandleAsync<T>(IQuery<,>).
public class Handler14 : IHandlerBase
{
[Order(1000)]
public Task HandleAsync<T>(IQuery<ScopedServiceRequest<T>, object> query)
{
return Task.Delay(1);
}
}
where constraint can be used with generics Handle method.
public class Handler15 : IHandlerBase
{
public void Handle<T>(IQuery<ScopedServiceRequest<T>, object> query) where T : IList<T> { }
}
MethodArgumentConstraints-property can be used to list generics method argument constraints if where is not expressive enough.
public class Handler16 : IHandlerBase
{
/// <summary>Additional type constraints (will be picked in <see cref="IHandlerInfo"/>)</summary>
public (string, Type)[]? MethodArgumentConstraints =>
new (string, Type)[]
{
("T", typeof(IList<>))
};
public void Handle<T>(IQuery<ScopedServiceRequest<T>, object> query) { }
}
IHandler handles all request and response types.
public class ConsoleLogger : IHandler
{
public void Handle<Request, Response>(IQuery<Request, Response> query) where Request : notnull
=> Console.WriteLine(query.Response);
}
public bool HandlesRequest(Type requestType) method can be used to evaluate which request types are to be handled.
public class Handler17 : IHandlerBase
{
/// <summary>Evaluate which request types to evaluate</summary>
public bool HandlesRequest(Type requestType)
=> requestType.IsGenericType &&
requestType.GetGenericTypeDefinition().Equals(typeof(ScopedServiceRequest<>));
public void Handle<T>(IQuery<ScopedServiceRequest<T>, object> query) { }
}
If handler implements multiple IHandlerT interfaces, HandlesRequest can be assigned to specific Handle() method by introducing it in an interface.
public class Handler18 : IMyHandler
{
/// <summary>Evaluate which request types to evaluate</summary>
bool IMyHandler.HandlesRequest(Type requestType)
=> requestType.IsGenericType &&
requestType.GetGenericTypeDefinition().Equals(typeof(ScopedServiceRequest<>));
void IMyHandler.Handle<T>(IQuery<ScopedServiceRequest<T>, object> query) { }
}
public interface IMyHandler : IHandlerBase
{
/// <summary>Evaluate which request types to evaluate</summary>
bool HandlesRequest(Type requestType);
/// <summary>Handle</summary>
void Handle<T>(IQuery<ScopedServiceRequest<T>, object> query);
}
Customized handler interface
Custom handler interfaces can be defined.
public interface IServiceHandler<T> : IHandlerBaseSync
{
void Handle(IQuery<ScopedServiceRequest<T>, object> query);
}
public interface IServiceHandler : IHandlerBaseSync
{
public void Handle<T>(IQuery<ScopedServiceRequest<T>, object> query);
}
If custom handler interface has "Order" property, then the Handle() method will be associated with it in the implementation.
public interface IServiceHandlerWithOrder<T> : IHandlerBaseSync
{
long? Order { get; }
void Handle(IQuery<ScopedServiceRequest<T>, object> query);
}
Break Query
.BreakQuery is a property to indicate that query is not to be processed by any further handlers. This does not fail the already assigned result, stops further evaluation and accepts the result.
public class Handler20 : IHandler<TypeRequest, Type>
{
public void Handle(IQuery<TypeRequest, Type> query)
{
// Assign result
query.Response.SetValue(typeof(string));
// Break query process
query.BreakQuery = true;
}
}
Full Example
Full example
#pragma warning disable SYSLIB0014
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using Avalanche.Service;
public class handler_handler
{
// <01>
public class Handler : IHandler<TypeRequest, Type>
{
public void Handle(IQuery<TypeRequest, Type> query)
{
// Assign result
query.Response.SetValue(typeof(string));
}
}
// </01>
// <02>
public class Handler2 : IHandler<TypeRequest, Type>
{
public void Handle(IQuery<TypeRequest, Type> query)
{
// Assign result (overwrite result from earlier handler)
query.Response.SetValue(typeof(string));
}
}
// </02>
// <02B>
public class Handler2B : IHandler<TypeRequest, Type>
{
public void Handle(IQuery<TypeRequest, Type> query)
{
// Already handled by another handler
if (query.Handled()) return;
// Assign result
query.Response.SetValue(typeof(string));
}
}
// </02B>
// <05>
public class Handler5 : IHandler<object, object>
{
public void Handle(IQuery<object, object> query)
{
// Already handled
if (query.Handled()) return;
// Assign result
query.Response.SetValue(typeof(string));
}
}
// </05>
// <06>
public class ConsoleLogger : IHandler
{
public void Handle<Request, Response>(IQuery<Request, Response> query) where Request : notnull
=> Console.WriteLine(query.Response);
}
// </06>
// <07>
public class DownloadUrl : IHandlerAsync<string, string>
{
public async Task HandleAsync(IQuery<string, string> query)
{
// Already handled
if (query.Handled()) return;
// Create client
WebClient client = new WebClient();
// Get url
string url = query.Request;
// Download content
string content = await client.DownloadStringTaskAsync(url);
// Write result
query.Response.SetValue(content);
}
}
// </07>
// <10>
public interface IServiceHandler<T> : IHandlerBaseSync
{
void Handle(IQuery<ScopedServiceRequest<T>, object> query);
}
// </10>
// <11>
public interface IServiceHandlerWithOrder<T> : IHandlerBaseSync
{
long? Order { get; }
void Handle(IQuery<ScopedServiceRequest<T>, object> query);
}
// </11>
// <12>
public interface IServiceHandler : IHandlerBaseSync
{
public void Handle<T>(IQuery<ScopedServiceRequest<T>, object> query);
}
// </12>
// <13>
public class Handler13 : IHandlerBase
{
[Order(1000)]
public void Handle<T>(IQuery<ScopedServiceRequest<T>, object> query)
{
}
}
// </13>
// <14>
public class Handler14 : IHandlerBase
{
[Order(1000)]
public Task HandleAsync<T>(IQuery<ScopedServiceRequest<T>, object> query)
{
return Task.Delay(1);
}
}
// </14>
// <15>
public class Handler15 : IHandlerBase
{
public void Handle<T>(IQuery<ScopedServiceRequest<T>, object> query) where T : IList<T> { }
}
// </15>
// <16>
public class Handler16 : IHandlerBase
{
/// <summary>Additional type constraints (will be picked in <see cref="IHandlerInfo"/>)</summary>
public (string, Type)[]? MethodArgumentConstraints =>
new (string, Type)[]
{
("T", typeof(IList<>))
};
public void Handle<T>(IQuery<ScopedServiceRequest<T>, object> query) { }
}
// </16>
// <17>
public class Handler17 : IHandlerBase
{
/// <summary>Evaluate which request types to evaluate</summary>
public bool HandlesRequest(Type requestType)
=> requestType.IsGenericType &&
requestType.GetGenericTypeDefinition().Equals(typeof(ScopedServiceRequest<>));
public void Handle<T>(IQuery<ScopedServiceRequest<T>, object> query) { }
}
// </17>
// <18>
public class Handler18 : IMyHandler
{
/// <summary>Evaluate which request types to evaluate</summary>
bool IMyHandler.HandlesRequest(Type requestType)
=> requestType.IsGenericType &&
requestType.GetGenericTypeDefinition().Equals(typeof(ScopedServiceRequest<>));
void IMyHandler.Handle<T>(IQuery<ScopedServiceRequest<T>, object> query) { }
}
public interface IMyHandler : IHandlerBase
{
/// <summary>Evaluate which request types to evaluate</summary>
bool HandlesRequest(Type requestType);
/// <summary>Handle</summary>
void Handle<T>(IQuery<ScopedServiceRequest<T>, object> query);
}
// </18>
// <20>
public class Handler20 : IHandler<TypeRequest, Type>
{
public void Handle(IQuery<TypeRequest, Type> query)
{
// Assign result
query.Response.SetValue(typeof(string));
// Break query process
query.BreakQuery = true;
}
}
// </20>
}
#pragma warning restore SYSLIB0014