C# 带有泛型处理程序和查询的Mediatr

C# 带有泛型处理程序和查询的Mediatr,c#,covariance,contravariance,mediator,mediatr,C#,Covariance,Contravariance,Mediator,Mediatr,我正在使用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 ca

我正在使用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>();
   }       
}

我花了好几个小时尝试了很多东西,但没有一件奏效。我甚至厌倦了在服务集合中使用Autofac,遵循中介github repo中提供的示例

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

编辑


在处理程序中,我使用automapper投影来限制查询的内容 从讨论中的db表中。允许调用方告诉查询和 反过来,处理程序会改变所需数据的形状

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

因此,例如,
订单
实体的示例如下所示

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);
    }
}
公共类OrderDto
{
公共字符串名称{get;set;}
公共整数金额{get;set;}
}
公共类筛选器订单查询:IRequest
{
公共字符串筛选器{get;set;}
}
公共类筛选器OrdersQueryHandler:IRequestHandler
{
公共任务句柄(FilterOrdersQuery通知、CancellationToken CancellationToken)
{
var dataSource=新列表(){
新订单dto()
{
Name=“blah”,
金额=65
},
新订单dto()
{
Name=“foo”,
金额=12
},
};
var结果=数据源
.Where(x=>x.Name.Contains(notification.Filter))
.ToList();
返回Task.FromResult(结果);
}
}
这只是一个简单的示例,演示了如何过滤给定的实体并返回过滤对象列表。您还可以为分页、OrderBy等添加逻辑。

//您可以在下面进行尝试。复制到您的启动
//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);
   }
}
var builder=new ContainerBuilder(); 建造商。填充(服务); var entityTypes=typeof(SomeModel).Assembly.GetTypes(); var handerType=typeof(MyQueryHandler); foreach(entityTypes中的变量entityType) { var handlerGenericType=(TypeInfo)handerType.MakeGenericType(entityType); foreach(handlerGenericType.ImplementedInterfaces中的变量genericType) { builder.RegisterType(handlerGenericType).As(genericType); } }
如果处理程序位于单独的程序集中,则需要告诉MediatR在何处查找它。
AddMediatR
方法获取程序集列表,或MediatR用于在同一程序集中查找处理程序的类型

ConfigureServices
中的
Startup
类中,您添加的MediatR可能如下所示:

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

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

如果处理程序位于另一个程序集中,则可以将其添加到MediatR,如下所示:

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

您需要在Program.cs中添加Autofac

// In your Program.cs
public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseServiceProviderFactory(new AutofacServiceProviderFactory())
        .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
//在您的程序中。cs
公共静态IHostBuilder CreateHostBuilder(字符串[]args)=>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(新的AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webBuilder=>{webBuilder.UseStartup();});
在创业结束时,您需要添加以下内容:

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);
        }
    }
public void配置容器(ContainerBuilder生成器)
{
类型[]entityTypes=
{
类型(某型号)
//添加您想要的所有型号
};
var handlerTypes=新列表
{
类型(MyQuery)
//添加所有需要的处理程序
};
foreach(entityType中的类型entityType)
foreach(handlerType中的类型handlerType)
{
var handlerGenericType=(TypeInfo)handlerType.MakeGenericType(entityType);
foreach(handlerGenericType.ImplementedInterfaces中的类型genericType)
builder.RegisterType(handlerGenericType).As(genericType);
}
}

我认为每个查询对象都应该有一个平面结构,比如
Dtos
,这样就可以在运行时轻松注册它的处理程序。如果你想创建一些通用查询处理程序,为什么不只是使用行为?你能告诉我处理程序中的
MyQuery
的目标是什么吗?我使用automapper投影来限制从所讨论的db表中查询的内容。让调用方告诉查询,然后让处理程序告诉所需数据的形状。我更新了答案。这就是你问题的答案,还是你想做些不同的事情?如果没有,请给我更多的细节,这样我可以尝试帮助。我花了几个小时与这个图书馆斗争,所以我知道你的痛苦:我有一个解决我的具体问题的办法,我会尝试写一个答案很快。
// 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);
        }
    }