当实体在不同的项目中时,Mediatr为什么不解析方法?

E-A*_*E-A 5 c# mediatr asp.net-core

我有一个简单的项目来尝试Mediatr问题。当我的API的SAME项目中的处理程序的具体类起作用时,它将起作用。但是,当我将该处理程序类带入另一个项目(并且API引用该项目的c)时,它不会解析注册表。

我收到此错误:

找不到针对MediatR.IRequestHandler`2 [MyBiz.GetTokenModelRequest,MyBiz.TokenModel]类型的请求的处理程序。在容器中注册您的处理程序。有关示例,请参见GitHub中的示例。

我在项目中有这个结构,并且还显示了它的工作原理和不工作的地方:

项目结构表示为树状视图

有关更多说明,请参见以下代码:

MyApi2-> Startup.cs:

namespace MyApi2
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.AddMediatR();
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseMvc();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

MyApi2-> ValuesController:

namespace MyApi2.Controllers
{
    [Route("api/[controller]")]
    public class ValuesController : Controller
    {
        private readonly IMediator _mediator;

        public ValuesController(IMediator mediator)
        {
            _mediator = mediator;
        }

        [HttpGet]
        public async Task<IEnumerable<string>> Get()
        {
            try
            {
                var rr = await _mediator.Send(new GetTokenModelRequest());
            }
            catch (Exception ex)
            {
                throw;
            }
            return new string[] { "value1", "value2" };
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

MyBiz-> GetTokenModelRequest

namespace MyBiz
{
    public class GetTokenModelRequest : LoginModel, IRequest<TokenModel>
    {
    }
    public class LoginModel
    {
        public string Username { get; set; }
        public string Password { get; set; }
    }
    public class TokenModel
    {
        #region Properties

        public Guid Id { get; set; }
        public string Username { get; set; }
        public string Token { get; set; }
        public DateTime Expiration { get; set; }

        #endregion
    }
}
Run Code Online (Sandbox Code Playgroud)

MyInftra-> TokenQueryHandler

namespace MyInfra
{
    public class TokenQueryHandler : ITokenQueryHandler
    {
        public Task<TokenModel> Handle(GetTokenModelRequest request, CancellationToken cancellationToken)
        {
            return Task.FromResult(new TokenModel());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

所以,如果我MOVE TokenQueryHandler来自MyInfraMyApi它的工作原理,但我应该能够把它引用项目,对不对?

Dar*_*ane 6

更新资料

从版本7.0.0的的MediatR.Extensions.Microsoft.DependencyInjection包,AppDomain不再被自动扫描含有MediatR基类型注册,呼叫时加载的程序集AddMediatR()的扩展方法。

实际上,该函数的无参数重载已从包中完全消除,需要用户传入程序集(或类型)进行扫描。

这使得MediatR基类型(登记IRequestHandlerINotificationHandlerIRequestPreProcessorIRequestPostProcessor内每个引用的组件),明确地在用户的控制和判断。

因此,如果我们在假想的程序集中有一些MediatR基本类型,Assembly1并且Assembly2我们想在MediatR容器中注册:

而不是做: services.AddMediatR();

您需要执行以下操作: services.AddMediatR(typeof(Assembly1), typeof(Assembly2));

这使我的原始答案(下)对于使用此软件包的版本7(或更高版本)的任何人都是多余的,但对于使用较旧版本的用户,我将保留在这里。


原始答案

注意:以下答案仅与<7.0.0的MediatR.Extensions.Microsoft.DependencyInjection程序包相关。

对文件中AddMediatR()扩展方法的调用完成了startup.cs许多初始化MediatR的操作:

  • 它将在App Domain中扫描当前加载的程序集
  • 它扫描通过每个那些当前加载组件中的查找每个类从MediatR基本类型继承(IRequestHandlerINotificationHandlerIRequestPreProcessorIRequestPostProcessor
  • 它将每种MediatR基本类型注册到容器中以供以后使用

考虑到以上几点,理解.NET CLR如何加载引用的程序集很重要。里克·斯特拉尔(Rick Strahl)有一篇非常有趣的博客文章,其中涉及细节,但在这里我将引述为总结:

简而言之,引用的程序集不会立即加载-会根据需要即时加载。因此,无论您是在顶级项目中拥有程序集引用,还是从属程序集,通常都根据需要加载,除非由用户代码明确加载。依赖程序集也是如此。

为什么要知道这一点很重要?

嗯,在您的MyApi2项目中,您引用了该MyInfra项目,但实际上并没有以任何方式使用它。这意味着程序集将不会被CLR加载,因此MediatR将无法在App Domain当前加载的程序集中找到它。结果,您IRequestHandler将不会被注册(该项目中也不会注册任何其他MediatR基本类型)。

解决此问题的方法是确保在调用之前加载包含要在MediatR容器中注册的类型的程序集AddMediatR()

您可以执行以下任一操作:

  • 手动加载参考装配
  • MyInfraMyApi2项目中引用项目中的类型/函数

后一个选项是最典型的选项,因为您通常会在要调用的引用程序集中拥有某些功能(而不是仅具有包含类型的程序)。

无论使用哪种选择,请确保在添加MediatR之前都已执行此操作。否则,您将遇到相同的问题。