当实体在不同的项目中时,为啥 Mediator 不解析方法?

Posted

技术标签:

【中文标题】当实体在不同的项目中时,为啥 Mediator 不解析方法?【英文标题】:Why doesn't Mediatr resolve method when entites are in different projects?当实体在不同的项目中时,为什么 Mediator 不解析方法? 【发布时间】:2019-08-15 15:53:49 【问题描述】:

我有一个简单的项目来尝试 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();
        
    

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" ;
        
    

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
    

MyInftra -> TokenQueryHandler

namespace MyInfra

    public class TokenQueryHandler : ITokenQueryHandler
    
        public Task<TokenModel> Handle(GetTokenModelRequest request, CancellationToken cancellationToken)
        
            return Task.FromResult(new TokenModel());
        
    

所以,如果我 MOVE TokenQueryHandlerMyInfraMyApi 它可以工作,但我应该可以把它作为参考项目,对吧?

【问题讨论】:

如何在启动文件中将 Mediatr 添加到 DI? 在 Startup.cs 我在 ConfigureServices(IServiceCollection services) 中使用 services.AddMediatR(); 【参考方案1】:

更新

从MediatR.Extensions.Microsoft.DependencyInjection 包的7.0.0 版本开始,在调用AddMediatR() 扩展方法时,不再自动扫描AppDomain 以查找包含要注册的MediatR 基类型的加载程序集。

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

这使得在每个引用的程序集中注册 MediatR 基本类型(IRequestHandlerINotificationHandlerIRequestPreProcessorIRequestPostProcessor),由用户明确控制和酌情决定。

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

而不是这样做:services.AddMediatR();

您需要这样做:services.AddMediatR(typeof(Assembly1), typeof(Assembly2));

这使得我的原始答案(如下)对于使用此软件包版本 7(可能更高版本)的任何人来说都是多余的,但我会将其保留在这里以供那些使用旧版本的人使用。


原答案

注意:以下答案仅与 MediatR.Extensions.Microsoft.DependencyInjection 包的

startup.cs 文件中的AddMediatR() 扩展方法的调用会做很多事情来初始化MediatR:

它扫描应用程序域以查找当前加载的程序集 它扫描每个当前加载的程序集以查找从 MediatR 基类型(IRequestHandlerINotificationHandlerIRequestPreProcessorIRequestPostProcessor)继承的每个类 它将这些 MediatR 基类型中的每一个注册到容器中以供以后使用

考虑到以上几点,了解 .NET CLR 如何加载引用的程序集非常重要。 Rick Strahl 有一个非常有趣的blog post 进行了详细介绍,但我将在此引用一段话来总结:

简而言之,引用的程序集不会立即加载 - 它们会根据需要即时加载。因此,无论您在***项目中是否有程序集引用,或者依赖程序集程序集通常会根据需要加载,除非用户代码显式加载。依赖程序集也是如此。

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

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

此问题的解决方案是确保在调用AddMediatR() 之前加载包含您希望注册到 MediatR 容器的类型的程序集。

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

Manually load您引用的程序集 从 MyApi2 项目中引用位于 MyInfra 项目中的类型/函数

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

无论您选择哪个选项,请确保在添加 MediatR 之前执行此操作。否则你会遇到同样的问题。

【讨论】:

以上是关于当实体在不同的项目中时,为啥 Mediator 不解析方法?的主要内容,如果未能解决你的问题,请参考以下文章

当 Recyclerview 在 NestedScrollview 中时,如何避免绑定所有项目?

当元素存在于数组中时,为啥 index = -1? [复制]

Javascript:当元素存在于数组中时,为啥 indexOf 函数返回 -1?

当我尝试将用户上传的图像保存在视图中时,为啥 ImageField 中的 upload_to 根本不起作用

当 XAMPP 不在分区的根目录中时,为啥它不能工作?

当设备位于我的用户目录中时,为啥 Android 模拟器会报告“未知虚拟设备”?