【问题标题】:Mediatr with generic handler and query具有通用处理程序和查询的 Mediatr
【发布时间】:2019-12-21 13:58:35
【问题描述】:

我正在使用 Mediatr 开发 ASP.NET Core 2.2 Web API 应用程序。

我有一个看起来像 -

public class MyQueryHandler<T> : IRequestHanlder<MyQuery<T>, IQueryable<T>>
{
   public Task<IQueryable<T>> Handle(MyQuery<T> myquery, CancellationToken cancellationToken)
   {
       //perform query

       IQueryable<T> models = someDatabaseQuery.ProjectTo<T>();
   }       
}

这是查询 -

public class MyQuery<T> : IRequest<IQueryable<T>>
{
   //some properties
}

当我尝试提出这样的请求时 -

var result = await _mediator.Send(new MyQuery<SomeModel> {/* set the properties on the query */})

我得到一个例外 -

An unhandled exception occurred while processing the request.

InvalidOperationException: Handler was not found for request of type MediatR.IRequestHandler`2[MyQuery`1[SomeModel],System.Linq.IQueryable`1[SomeModel]]. Register your handlers with the container. See the samples in GitHub for examples.

我花了好几个小时尝试了很多东西,但都没有奏效。按照 Mediator github repo 中提供的示例,我什至厌倦了在服务集合旁边使用 Autofac。

【问题讨论】:

  • 我认为每个 Query 对象都应该有一个像Dtos 这样的扁平结构,所以它的处理程序可以很容易地在运行时注册。如果您想创建一些通用查询处理程序,为什么不只使用 Behaviors?
  • 你能告诉我MyQuery&lt;T&gt;的目标是什么
  • 在处理程序中,我使用自动映射器投影来限制从相关数据库表中查询的内容。 让调用者告诉查询,然后处理程序需要的数据形状。
  • 我更新了我的答案。这是您问题的答案还是您想要实现不同的目标?如果没有,请给我更多详细信息,以便我可以尝试提供帮助。我花了几个小时在这个库上苦苦挣扎,所以我知道你的痛苦:D
  • 我对我的具体问题有一个解决方案,我会尽快写一个答案。

标签: c# covariance contravariance mediator mediatr


【解决方案1】:

每个查询都应该有一个具体的类型/平面结构,以便它的处理程序可以在运行时由依赖注入容器轻松注册。我相信注册通用查询处理程序作为示例是不可能的,因为 DI 容器可能会遇到注册通用类型的问题。 我相信创建Behavior 是您应该做的正确事情。它可以让您在一个地方处理所有查询或命令,因此您可以在点击给定Query/Command 的处理程序之前运行一些额外/通用逻辑,如日志记录等。

编辑

在处理程序中,我使用自动映射器投影来限制查询的内容 从有问题的 db 表中。让调用者告诉查询和 反过来处理程序所需的数据形状。

为了限制从数据库中查询的内容,我将使用一种为每个实体创建查询和查询处理程序的方法。我认为进行这样的分离是有意义的,因为从安全角度来看,您可能希望仅授予特定用户组以运行给定查询。

因此,例如Order 实体看起来像。

public class OrderDto
{
    public string Name { get; set; }

    public int Amount { get; set; }
}

public class FilterOrdersQuery : IRequest<List<OrderDto>>
{
    public string Filter { get; set; }
}

public class FilterOrdersQueryHandler : IRequestHandler<FilterOrdersQuery, List<OrderDto>>
{
    public Task<List<OrderDto>> Handle(FilterOrdersQuery notification, CancellationToken cancellationToken)
    {
        var dataSource = new List<OrderDto>(){
            new OrderDto()
            {
                Name = "blah",
                Amount = 65
            },
            new OrderDto()
            {
                Name = "foo",
                Amount = 12
            },
        };

        var result = dataSource
            .Where(x => x.Name.Contains(notification.Filter))              
            .ToList();

        return Task.FromResult(result);
    }
}

这只是一个简单的例子,展示了如何过滤给定的实体并返回过滤对象的列表。您还可以为分页、OrderBy 等添加逻辑。

【讨论】:

    【解决方案2】:
    //You can try below. Copy to your startup
    var builder = new ContainerBuilder();
    builder.Populate(services);
    var entityTypes = typeof(SomeModel).Assembly.GetTypes();
    var handerType = typeof(MyQueryHandler<>);
    foreach (var entityType in entityTypes)
    {
       var handlerGenericType = (TypeInfo)handerType.MakeGenericType(entityType);
       foreach (var genericType in handlerGenericType.ImplementedInterfaces)
       {
          builder.RegisterType(handlerGenericType).As(genericType);
       }
    }
    

    【讨论】:

      【解决方案3】:

      如果您将处理程序放在单独的程序集中,则需要告诉 MediatR 在哪里查找它。 AddMediatR 方法采用程序集列表或 MediatR 用于在同一程序集中查找处理程序的类型。

      ConfigureServicesStartup 类中,您添加 MediatR 可能是这样的:

      services.AddMediatR(typeof(Startup));
      

      services.AddMediatR(typeof(Startup).GetTypeInfo().Assembly);
      

      两者都给出相同的结果 - MediatR 在 Startup 类所在的程序集中查找处理程序。

      如果您的处理程序在另一个程序集中,您可以像这样将它添加到 MediatR:

      services.AddMediatR(typeof(Startup),
                          typeof(FooBar),
                          typeof(Some.Other.Class.In.Another.Assembly));
      

      【讨论】:

        【解决方案4】:

        您需要在 Program.cs 中添加 Autofac

        // In your Program.cs
        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .UseServiceProviderFactory(new AutofacServiceProviderFactory())
                .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
        

        在启动结束时,您需要添加如下内容:

        public void ConfigureContainer(ContainerBuilder builder)
            {
                Type[] entityTypes =
                {
                    typeof(SomeModel)
                    // add all the models you want
                };
        
                var handlerTypes = new List<Type>
                {
                    typeof(MyQuery<>)
                    // add all the handlers you want
                };
        
                foreach (Type entityType in entityTypes)
                foreach (Type handlerType in handlerTypes)
                {
                    var handlerGenericType = (TypeInfo) handlerType.MakeGenericType(entityType);
                    foreach (Type genericType in handlerGenericType.ImplementedInterfaces)
                        builder.RegisterType(handlerGenericType).As(genericType);
                }
            }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-12-08
          • 2019-10-18
          • 1970-01-01
          • 2019-03-27
          • 1970-01-01
          • 2020-01-24
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多