当实体在不同的项目中时,Mediatr为什么不解析方法? [英] Why doesn't Mediatr resolve method when entites are in different projects?

查看:154
本文介绍了当实体在不同的项目中时,Mediatr为什么不解析方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

因此,如果我从移动到MyApiMyApi,它可以工作,但是我应该可以将其引用为项目,对吧?

解决方案

更新

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

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

这将在每个引用的程序集中进行MediatR基本类型(IRequestHandlerINotificationHandlerIRequestPreProcessorIRequestPostProcessor)的注册,这完全由用户控制和决定.

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

而不是:services.AddMediatR();

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

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


原始答案

注意:以下答案仅与<版本有关. MediatR.Extensions.Microsoft.DependencyInjection 包的7.0.0. >

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

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

请牢记以上几点,了解.NET CLR如何加载引用的程序集很重要.有一个非常有趣的博客帖子,其中有详细介绍,但在这里我将用引号进行总结:

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

为什么这很重要?

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

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

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

  • 手动加载您引用的程序集
  • MyApi2项目引用MyInfra项目中的类型/函数

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

选择哪个选项,请确保在添加MediatR之前先进行了选择.否则,您将遇到相同的问题.

I have a simple project to try out Mediatr issue. When the concrete class of my handler in the SAME project of my API, it WORKS. But, when I take that handler class in to a different project (and API references that project ofc), it does NOT resolve the registry.

I'm getting this error:

Handler was not found for request of type MediatR.IRequestHandler`2[MyBiz.GetTokenModelRequest,MyBiz.TokenModel]. Register your handlers with the container. See the samples in GitHub for examples.

I have this structure on my project and also shown where it works and where it doesn't:

For more clarification here are the codes:

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());
        }
    }
}

So, if I MOVE TokenQueryHandler from MyInfra to MyApi it works but I should be able to put it a references Project, right?

解决方案

Update

As of version 7.0.0 of the MediatR.Extensions.Microsoft.DependencyInjection package, the AppDomain no longer gets automatically scanned for loaded assemblies containing MediatR base types to register, when calling the AddMediatR() extension method.

In fact, the parameter-less overload of said function has been completely removed from the package, requiring users to pass in the assemblies (or types) to scan through instead.

This makes the registration of the MediatR base types (IRequestHandler, INotificationHandler, IRequestPreProcessor and IRequestPostProcessor) within each referenced assembly, explicitly at the user's control and discretion.

So if we have some MediatR base types in the imaginary assemblies Assembly1 and Assembly2 that we want to register with the MediatR container:

Instead of doing: services.AddMediatR();

You'll need to do: services.AddMediatR(typeof(Assembly1), typeof(Assembly2));

This makes my original answer (below) redundant for anyone using version 7 (and perhaps greater) of this package but I will keep it here for those using older versions.


Original Answer

Note: The following answer is only relevant for versions < 7.0.0 of the MediatR.Extensions.Microsoft.DependencyInjection package.

The call to the AddMediatR() extension method in your startup.cs file does many things to initialise MediatR:

  • It scans the App Domain for the currently loaded assemblies
  • It scans through each of those currently loaded assemblies to find every class that inherits from the MediatR base types (IRequestHandler, INotificationHandler, IRequestPreProcessor and IRequestPostProcessor)
  • It registers each of those MediatR base types to the container for later use

With the points above in mind, it is important to understand how the .NET CLR loads referenced assemblies. There is a really interesting blog post by Rick Strahl that goes into the details, but I will summarise it here with a quote:

In a nutshell, referenced assemblies are not immediately loaded - they are loaded on the fly as needed. So regardless of whether you have an assembly reference in a top level project, or a dependent assembly assemblies typically load on an as needed basis, unless explicitly loaded by user code. The same is true of dependent assemblies.

Why is this important to know?

Well, in your MyApi2 project, you reference the MyInfra project but you do not actually use it in any way. This means that the assembly will not get loaded by the CLR, and thus MediatR will fail to find it in the App Domain's currently loaded assemblies. As a result, your IRequestHandler will not be registered (nor any other MediatR base types in that project).

The solution to this problem is to ensure that the assembly containing the types you wish to have registered with the MediatR container is loaded before the call to AddMediatR().

You could do either of the following:

  • Manually load your referenced assembly
  • Reference a type/function that lies within your MyInfra project from your MyApi2 project

The latter option is the most typical, as you will usually have some functionality that sits in your referenced assembly that you will want to invoke (as opposed to just having an assembly that contains types).

Whichever option you go for, ensure that you do it prior to adding MediatR. Otherwise you will run into the same issue.

这篇关于当实体在不同的项目中时,Mediatr为什么不解析方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆