MediatR 行为的约束违反异常

Posted

技术标签:

【中文标题】MediatR 行为的约束违反异常【英文标题】:Constraint Violated Exception on MediatR Behavior 【发布时间】:2019-02-04 10:02:04 【问题描述】:

我将MediatR 与以下类一起使用:

public class GetPostsRequest : IRequest<Envelope<GetPostsResponse>> 
  public Int32 Age  get; set; 


public class GetPostResponse 
  public String Title  get; set; 
  public String Content  get; set;       

Envelope 是一个包装类:

public class Envelope<T> 
  public List<T> Result  get; private set;  = new List<T>();  
  public List<Error> Errors  get; private set;  = new List<Error>();

Mediator 发送的 GetPostsRequest 由 Handler 执行:

public class GetPostsRequestHandler : IRequestHandler<GetPostsRequest, Envelope<GetPostsResponse>> 

  public async Task<Envelope<GetPostsResponse>> Handle(GetPostsRequest request, CancellationToken cancellationToken) 
  


MediatR 允许使用在特定请求的处理程序之前执行的行为。我创建了一个ValidationBehavior 如下:

public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, Envelope<TResponse>> 
    where TRequest : IRequest<Envelope<TResponse>> 

  private readonly IEnumerable<IValidator<TRequest>> _validators;

  public ValidationBehavior(IEnumerable<IValidator<TRequest>> validators) 
    _validators = validators;
  

  public Task<Envelope<TResponse>> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<Envelope<TResponse>> next) 

    ValidationContext context = new ValidationContext(request);

    List<Error> errors = _validators
      .Select(x => x.Validate(context))
      .SelectMany(x => x.Errors)
      .Select(x => new Error(ErrorCode.DataNotValid, x.ErrorMessage, x.PropertyName))
      .ToList();

    if (errors.Any())
      return Task.FromResult<Envelope<TResponse>>(new Envelope<TResponse>(errors));  

    return next();

  


我在 ASP.NET Core 应用程序上注册了 ValidationBehavior:

services.AddScoped(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));

当我调用 API 时出现以下错误:

An unhandled exception has occurred while executing the request.
System.ArgumentException: GenericArguments[0], 'TRequest', on 'ValidationBehavior`2[TRequest,TResponse]' violates the constraint of type 'TRequest'. 
---> System.TypeLoadException: GenericArguments[0], 'TRequest', on 'ValidationBehavior`2[TRequest,TResponse]' violates the constraint of type parameter 'TRequest'.

我错过了什么?

【问题讨论】:

请您展示一下您的调用方式 - 用什么?参数?我认为这是一个通用的不匹配 我认为这是一对多的通用包装器 - Envelope&lt;TResponse&gt; != Envelope&lt;Response&gt; 我不是在调用它...它正在被注入。请注意,在我使用泛型的行为上:TRequest 和 TResponse。 @Alex Env​​elope 应该不同于 Envelope。我按问题更新以澄清。 @MiguelMoura 你能找出解决问题的正确方法吗?我面临着完全相同的问题并且正在努力解决它。提前致谢! 【参考方案1】:
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));

我不认为这条线能达到你预期的效果。

在您的示例中,您的请求类型是 IRequest&lt;Envelope&lt;Response&gt;&gt;,因此当 MediatR 查找管道行为时,它将查找 IPipelineBehavior&lt;Request, Envelope&lt;Response&gt;&gt;

由于IPipelineBehavior&lt;,&gt; 的 DI 容器中有一个开放通用注册,因此将使用该实现。因此来自IPipelineBehavior&lt;,&gt; 的泛型类型参数将用于实例化ValidationBehavior&lt;,&gt; 的类型。

所以对于IPipelineBehavior&lt;Request, Envelope&lt;Response&gt;&gt;,这将创建一个ValidationBehavior&lt;Request, Envelope&lt;Response&gt;&gt;。这意味着在您的通用实现中,TRequest 将是 RequestTResponse 将是 Envelope&lt;Response&gt;。然而,这意味着您的类型上的类型约束意味着TRequest,或在这种情况下为Request,需要实现IRequest&lt;Envelope&lt;Envelope&lt;Response&gt;&gt;&gt;。当然,事实并非如此,因此可以解释您所看到的类型约束违规。

不幸的是,这意味着您无法按照自己希望的方式进行操作。您不能对泛型类型施加类型约束,至少对 Envelope&lt;T&gt; 没有。

【讨论】:

这就是为什么最初我尝试使用 ValidationBehavior> : IPipelineBehavior> 其中 TRequest : IRequest> 但它没有编译. 我所有的回复都包裹在一个信封中,例如 Envelope 或 Envelope ...在我的行为中,我需要使用信封,所以如果出现错误,我会返回并使用带有这些错误的信封.你会如何解决这个问题? 我认为没有什么好的方法可以做到这一点。你可以引入一个具有Errors 属性的IEnvelope 接口,这样你就可以在你的行为中检查IEnvelope 类型,然后设置错误,而无需访问实际的响应类型。【参考方案2】:

您的ValidationBehavior 指定TRequest 必须是IRequest&lt;Envelope&lt;TResponse&gt;&gt; 您的请求实现IRequest&lt;Envelope&lt;Response&gt;&gt; (不匹配 - Envelope&lt;TResponse&gt; 不是 Envelope&lt;Response&gt;

但是,您的 ValidationBehavior 实现 IPipelineBehavior&lt;TRequest, Envelope&lt;TResponse&gt;&gt; 并且有约束where TRequest : IRequest&lt;Envelope&lt;TResponse&gt;&gt;

所以,将Envelope&lt;TResponse&gt; 更改为Envelope&lt;Response&gt;

【讨论】:

我不能用那个。 Request 和 Response 类的具体例子。该行为必须使用泛型,仅指定 Response 的类型为 Envelope。我更新了我的问题,试图让它更清楚。

以上是关于MediatR 行为的约束违反异常的主要内容,如果未能解决你的问题,请参考以下文章

Vapor fluent 不会在违反外键约束时抛出

Mediatr 行为管道中的验证

Mediatr 3.0 使用管道行为进行身份验证

向 MediatR 行为管道添加验证?

来自管道行为的 MediatR 流畅的验证响应

Mediatr:单元测试行为/验证