ASP.NET Core MediatR错误:使用容器注册您的处理程序

47

我有一个.NET Core应用程序,其中我使用.AddMediatR扩展来注册我的命令和处理程序的程序集,遵循CQRS方法。

在Startup.cs中的ConfigureServices方法中,我使用了官方包MediatR.Extensions.Microsoft.DependencyInjection的扩展方法,并使用了以下参数:

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

以下是命令和命令处理程序类:

AddEducationCommand.cs

public class AddEducationCommand : IRequest<bool>
{
    [DataMember]
    public int UniversityId { get; set; }

    [DataMember]
    public int FacultyId { get; set; }

    [DataMember]
    public string Name { get; set; }
}

AddEducationCommandHandler.cs

public class AddEducationCommandHandler : IRequestHandler<AddEducationCommand, bool>
    {
        private readonly IUniversityRepository _repository;
        public AddEducationCommandHandler(IUniversityRepository repository)
        {
            _repository = repository;
        }

        public async Task<bool> Handle(AddEducationCommand command, CancellationToken cancellationToken)
        {
            var university = await _repository.GetAsync(command.UniversityId);

            university.Faculties
                .FirstOrDefault(f => f.Id == command.FacultyId)
                .CreateEducation(command.Name);

            return await _repository.UnitOfWork.SaveEntitiesAsync();
        }
    }

当我运行执行简单的await _mediator.Send(command);代码的REST端点时,我的日志记录显示以下错误:

Error constructing handler for request of type MediatR.IRequestHandler`2[UniversityService.Application.Commands.AddEducationCommand,System.Boolean]. Register your handlers withthe container. See the samples in GitHub for examples.

我尝试查看官方文档中的示例,但没有找到。有人知道如何配置MediatR以正常工作吗?


1
问题之外还有很多可能出错的地方。请提供“最小完整可验证示例”(MCVE),这样我们才能了解您在做什么。 (注:链接为 https://stackoverflow.com/help/mcve ) - Alexander Leonov
你应该前往 https://github.com/jbogard/MediatR/blob/master/samples/MediatR.Examples.AspNetCore/Program.cs,因为还有很多代码需要编写来注册 MediatR 及其处理程序。 - hugo
7
附加调试器并检查InnerException。 可能是IUniversityRepository没有注册,因此无法构建RequestHandler。 services.AddMediatR()不足够,您还需要在服务集合中注册所有其他依赖项。 - PeterFromCologne
谢谢Peter,我的Autofac模块配置中存储库的配置有问题。 - Mike Hawkins
17个回答

48
我遇到了同样的问题。
问题在于这行代码
services.AddMediatR(typeof(AddEducationCommand).GetTypeInfo().Assembly);
处理所有的 MediatR IRequest 和 IRequestHandlers。
但是,你创建了一个 IRepository 接口及其实现类,这些不能被 MediatR.Extensions.Microsoft.DependencyInjection 处理。
所以,保留所有更改,然后手动注册此项,像这样
services.AddScoped(typeof(IUniversityRepository), typeof(UniversitySqlServerRepository));
问题就解决了。

2
在我的情况下,InnerException 告诉我,我的依赖项无法找到其特定版本的依赖项(例如,UniversitySqlServerRepository 找不到 Newtonsoft.Json)。 - Reyhn
只想强调一下,查看InnerException是关键。我的异常与@Reyhn的不同,但它导致了解决方案。 - Rokit

20

我遇到了同样的问题并进行了数小时的搜索,但是没有找到任何答案,因为这个错误非常普遍。表面上看起来像是MediatR的问题,但很多时候并不是这样。

我是如何得出这个结论的?

为了获取原始异常,我打开了Windows默认存在的事件查看器应用程序。

自定义视图 > 概要页事件中,我发现了一些与我的应用程序对应的错误。在我的情况下,错误信息大致如下:

An error occured during migration

Exception: 
System.ArgumentNullException: Value cannot be null. (Parameter 'connectionString')
   at Microsoft.EntityFrameworkCore.Utilities.Check.NotEmpty(String value, String parameterName)
   at Microsoft.EntityFrameworkCore.MySqlDbContextOptionsExtensions.UseMySql(DbContextOptionsBuilder optionsBuilder, String connectionString, Action`1 mySqlOptionsAction)

正如错误提示所说,connectionString为空。最终我发现,当我发布应用程序以获取dlls时,appsettings.json没有出现在发布的文件夹中,因此找不到connectionString,这就是迁移失败的原因。最终导致应用程序崩溃,并显示一个非常通用的错误:

Error constructing handler for request of type MediatR.IRequestHandler. 

Register your handlers with the container. See the samples in GitHub for examples

你节省了我很多时间。谢谢你!你是如何添加appsettings.json的? - samantha07
@samantha07 感谢您的赞赏 :) 由于一些错误,appsettings.json 被包含在 .gitignore 文件中。因此,它没有被推送到 bitbucket 服务器上,最终当我从 bitbucket pipeline 生成构建时,appsettings.json 文件不在那里。必须取消忽略该文件。 - SU7

8

对我来说,其他解决方案都不起作用,因为我已经注册了一切。对我有效的解决方案是在我的program.cs文件中添加以下行:

.UseDefaultServiceProvider(options => options.ValidateScopes = false);

因此,CreateHostBuilder 方法将被更改为:

Host.CreateDefaultBuilder(args)
    .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder.UseStartup<Startup>();
    }).UseDefaultServiceProvider(options =>
    options.ValidateScopes = false); // needed for mediatr DI

实际上,这与“作用域服务”有关,您可能还会发现答案也相关。


7

我曾遇到同样的问题,我的情况是当我在调用services.AddMediatR()之后注册某个处理程序所需的依赖项时,实际上出现了问题。后来我改为在注册中介者之前注册我的依赖项,一切工作正常。

我正在使用默认的DI容器和MediatR 6.0.0的dot net core 2.2。


4
我使用了这个替代方案。
services.AddMediatR(AppDomain.CurrentDomain.GetAssemblies());

2
你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心找到有关如何编写良好答案的更多信息。 - Community
1
非常感谢您的回答,它对我很有帮助。我只需要补充一下,我的 .Net Core 版本是 3.1。 - Mahmoud Nasr

3

我也在按照https://github.com/jasontaylordev/NorthwindTraders的Clean Architecture和CQRS进行开发。

以下是我的代码:

public class MyClassUpdateCommandHandler : IRequest<MyClass>

但它本应是这样的:
public class MyClassUpdateCommandHandler : IRequestHandler<MyClassUpdateCommand, MyClass>

一个更完整的示例如下:
  public class MyClassUpdateCommand : IRequest<MyClass>
  {
    public string Id { get; set; }
    ...
    //
    public MyClassUpdateCommand()
    {
      Id = "";
      ...
    }
  }
  //
  public class MyClassUpdateCommandHandler : IRequestHandler<MyClassUpdateCommand, MyClass>
  {
    private ApplicationDbContext _context;
    public MyClassUpdateCommandHandler(ApplicationDbContext context, IMediator mediator)
    {
      _context = context;
    }
    //
    public async Task<MyClass> Handle(MyClassUpdateCommand request, CancellationToken cancellationToken)
    {
      ...
      return _entity;
    }
    //
  }

3
今天我遇到了这个问题,我的解决方案和需要注意的是,如果你打算这么做:
``` services.AddMediatR(Assembly.GetExecutingAssembly()); ```
请确保获取的程序集与您的处理程序所在的程序集相同。在我的情况下,处理程序位于另一个程序集中(或者您可以称之为项目)。

2
也许问题在于您的处理程序位于单独的程序集中,如果是这样,您需要在Startup.cs中注册该程序集名称。
public void ConfigureServices(IServiceCollection services){
    var assembly = AppDomain.CurrentDomain.Load("HandlersDomain");
    services.AddMediatR(assembly);
}

"HandlersDomain" 是存储所有处理程序的程序集名称。

1
为了解决这个问题,我们可以手动注册MediatR以便于使用。我在我的项目中添加了Scrutor来使其更加易用。
        var serviceConfig = new MediatRServiceConfiguration();
        ServiceRegistrar.AddRequiredServices(services, serviceConfig);

        services.Scan(scan => scan
        .AddTypes(typeof(IRequestHandler<>))
        .AsSelf()
        .WithScopedLifetime()); 

或者不使用任何库

    foreach (var item in GetInstanceByInterface(typeof(IRequestHandler<>)))
    {
            services.AddTransient(item.GetType());
    }

    public IList<object> GetInstanceByInterface(Type baseInterFace)
    {
        var LoadedAssemblies = AppDomain.CurrentDomain.GetAssemblies().Where(p => !p.IsDynamic).ToList();
        var result = LoadedAssemblies
            .Where(a => a.FullName.Contains("Ahmad.Aghazadeh")).ToList();

        var baseClassName = baseInterFace.Name;

        return result.SelectMany(a => a.GetTypes())
            .Where(a => a.GetInterfaces().Any(b => b.Name == baseClassName)).Distinct()
            .Select(Activator.CreateInstance).ToList();
    }

1

我的问题最终是由控制器中的一个错误的名称属性引起的。它是单词“clients”,而该单词已经在URL中存在。将名称属性更改为“clientlist”后,它开始正常工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接