具有自定义 return 类型的通用 CQRS 查询处理程序
Generic CQRS Query handler with custom return type
我正在尝试使用 MediatR (v8) 库构建通用查询处理程序。让我们跳转到代码:
首先,我有一个像这样的抽象查询 class:
public abstract class Query<TQueryResult> : IRequest<TQueryResult>
{
public Guid Id { get; } = Guid.NewGuid();
public DateTime Timestamp { get; }
protected Query()
{
Timestamp = DateTime.Now;
}
}
我想从相应的查询处理程序中 return 一个 Result
包装器对象,如下所示:
public class Result<T>
{
public T Payload { get; }
public string FailureReason { get; }
public bool IsSuccess => FailureReason == null;
public Result(T payload)
{
Payload = payload;
}
public Result(string failureReason)
{
FailureReason = failureReason;
}
public static Result<T> Success(T payload)
=> new Result<T>(payload);
public static Result<T> Failure(string reason)
=> new Result<T>(reason);
public static implicit operator bool(Result<T> result) => result.IsSuccess;
}
最后但同样重要的是,让我们看看查询处理程序:
public abstract class AbstractQueryHandler<TQuery, TQueryResult, TResultValue> : IRequestHandler<TQuery, TQueryResult>
where TQuery : Query<TQueryResult>
where TQueryResult : class
{
public Task<TQueryResult> Handle(TQuery request, CancellationToken cancellationToken)
{
try
{
return HandleQuery(request);
}
catch (Exception e)
{
return Task.FromResult(Result<TResultValue>.Failure(GetFailureMessage(e)) as TQueryResult);
}
}
public abstract Task<TQueryResult> HandleQuery(TQuery request);
private static string GetFailureMessage(Exception e)
{
return "There was an error while executing query: \r\n" + e.Message;
}
}
老实说,由于查询处理程序中的三个类型参数,我对这个解决方案并不满意。让我们看看一些相应的测试来揭示我的担忧。首先是测试助手对象:
public class ExampleDto
{
public string Name { get; set; }
}
public class BasicQuery : Query<Result<ExampleDto>>
{
}
public class BasicQueryHandler : AbstractQueryHandler<BasicQuery, Result<ExampleDto>, ExampleDto>
{
public override Task<Result<ExampleDto>> HandleQuery(BasicQuery request)
{
return Task.FromResult(Result<ExampleDto>.Success(new ExampleDto() { Name = "Result Name" }));
}
}
然后是测试:
[Fact]
public async Task GivenBasicQuery_whenHandle_thenSuccessResultWithPayload()
{
var handler = new BasicQueryHandler();
var result = await handler.Handle(new BasicQuery(), CancellationToken.None);
Check.That(result.IsSuccess).IsTrue();
Check.That(result.Payload.Name).IsEqualToValue("Result Name");
}
正如您在 BasicQueryHandler
中看到的,在声明三种类型时存在某种重复,即 <BasicQuery, Result<ExampleDto>, ExampleDto>
。这对我来说真的很可疑。我还尝试了很多其他的可能性,检查了互联网上的文章和 SO questions/answers 但无法提出更清洁的解决方案。我究竟做错了什么?是否可以将(查询处理程序的)类型参数的数量减少到 2 个?在此先感谢您的帮助!
基本上,我将 Result<> 从 class 声明中的类型参数移到了方法声明中。为了清楚起见,我还删除了接口(无论如何你都没有共享定义)。
public abstract class AbstractQueryHandler<TQuery, TQueryResult>
where TQuery : Query<TQueryResult>
where TQueryResult : class, new()
{
public Task<Result<TQueryResult>> Handle(TQuery request, CancellationToken cancellationToken)
{
try
{
return HandleQuery(request);
}
catch (Exception e)
{
return Task.FromResult(Result<TQueryResult>.Failure(new TQueryResult(), GetFailureMessage(e)));
}
}
public abstract Task<Result<TQueryResult>> HandleQuery(TQuery request);
private static string GetFailureMessage(Exception e)
{
return "There was an error while executing query: \r\n" + e.Message;
}
}
public class BasicQueryHandler : AbstractQueryHandler<BasicQuery, ExampleDto>
{
public override Task<Result<ExampleDto>> HandleQuery(BasicQuery request)
{
return Task.FromResult(Result<ExampleDto>.Success(new ExampleDto() { Name = "Result Name" }));
}
}
请注意,由于您需要在异常路径中新建一个 TQueryResult,因此必须将 new()
关键字添加到通用约束中。
我正在尝试使用 MediatR (v8) 库构建通用查询处理程序。让我们跳转到代码: 首先,我有一个像这样的抽象查询 class:
public abstract class Query<TQueryResult> : IRequest<TQueryResult>
{
public Guid Id { get; } = Guid.NewGuid();
public DateTime Timestamp { get; }
protected Query()
{
Timestamp = DateTime.Now;
}
}
我想从相应的查询处理程序中 return 一个 Result
包装器对象,如下所示:
public class Result<T>
{
public T Payload { get; }
public string FailureReason { get; }
public bool IsSuccess => FailureReason == null;
public Result(T payload)
{
Payload = payload;
}
public Result(string failureReason)
{
FailureReason = failureReason;
}
public static Result<T> Success(T payload)
=> new Result<T>(payload);
public static Result<T> Failure(string reason)
=> new Result<T>(reason);
public static implicit operator bool(Result<T> result) => result.IsSuccess;
}
最后但同样重要的是,让我们看看查询处理程序:
public abstract class AbstractQueryHandler<TQuery, TQueryResult, TResultValue> : IRequestHandler<TQuery, TQueryResult>
where TQuery : Query<TQueryResult>
where TQueryResult : class
{
public Task<TQueryResult> Handle(TQuery request, CancellationToken cancellationToken)
{
try
{
return HandleQuery(request);
}
catch (Exception e)
{
return Task.FromResult(Result<TResultValue>.Failure(GetFailureMessage(e)) as TQueryResult);
}
}
public abstract Task<TQueryResult> HandleQuery(TQuery request);
private static string GetFailureMessage(Exception e)
{
return "There was an error while executing query: \r\n" + e.Message;
}
}
老实说,由于查询处理程序中的三个类型参数,我对这个解决方案并不满意。让我们看看一些相应的测试来揭示我的担忧。首先是测试助手对象:
public class ExampleDto
{
public string Name { get; set; }
}
public class BasicQuery : Query<Result<ExampleDto>>
{
}
public class BasicQueryHandler : AbstractQueryHandler<BasicQuery, Result<ExampleDto>, ExampleDto>
{
public override Task<Result<ExampleDto>> HandleQuery(BasicQuery request)
{
return Task.FromResult(Result<ExampleDto>.Success(new ExampleDto() { Name = "Result Name" }));
}
}
然后是测试:
[Fact]
public async Task GivenBasicQuery_whenHandle_thenSuccessResultWithPayload()
{
var handler = new BasicQueryHandler();
var result = await handler.Handle(new BasicQuery(), CancellationToken.None);
Check.That(result.IsSuccess).IsTrue();
Check.That(result.Payload.Name).IsEqualToValue("Result Name");
}
正如您在 BasicQueryHandler
中看到的,在声明三种类型时存在某种重复,即 <BasicQuery, Result<ExampleDto>, ExampleDto>
。这对我来说真的很可疑。我还尝试了很多其他的可能性,检查了互联网上的文章和 SO questions/answers 但无法提出更清洁的解决方案。我究竟做错了什么?是否可以将(查询处理程序的)类型参数的数量减少到 2 个?在此先感谢您的帮助!
基本上,我将 Result<> 从 class 声明中的类型参数移到了方法声明中。为了清楚起见,我还删除了接口(无论如何你都没有共享定义)。
public abstract class AbstractQueryHandler<TQuery, TQueryResult>
where TQuery : Query<TQueryResult>
where TQueryResult : class, new()
{
public Task<Result<TQueryResult>> Handle(TQuery request, CancellationToken cancellationToken)
{
try
{
return HandleQuery(request);
}
catch (Exception e)
{
return Task.FromResult(Result<TQueryResult>.Failure(new TQueryResult(), GetFailureMessage(e)));
}
}
public abstract Task<Result<TQueryResult>> HandleQuery(TQuery request);
private static string GetFailureMessage(Exception e)
{
return "There was an error while executing query: \r\n" + e.Message;
}
}
public class BasicQueryHandler : AbstractQueryHandler<BasicQuery, ExampleDto>
{
public override Task<Result<ExampleDto>> HandleQuery(BasicQuery request)
{
return Task.FromResult(Result<ExampleDto>.Success(new ExampleDto() { Name = "Result Name" }));
}
}
请注意,由于您需要在异常路径中新建一个 TQueryResult,因此必须将 new()
关键字添加到通用约束中。