'notification does not implement $INotification C#

I have a multi-tenancy project using mediator and CQRS, for some reason my CRUD from order request are returning this error, I already try refactor my handler sometimes including by removing the cancelation token from the request handler class, but don´t work.

All the http methods return exactly the same error but in differently handlers, we are using Azure Service Bus for the queue.

Any tips? Thanks!

Error:

System.Exception
 ---> System.ArgumentException: notification does not implement $INotification
   at MediatR.Mediator.Publish(Object notification, CancellationToken cancellationToken)
   at Project.RequestHandler.MarketPlace.Order.OrderGetRequestHandler.Handle(OrderGetRequest request, CancellationToken cancellationToken) in C:\git\Project\project-apis\03.Services\Project.RequestHandler\MarketPlace\Order\OrderGetRequestHandler.cs:line 27
   at MediatR.Internal.RequestHandlerWrapperImpl`2.<>c__DisplayClass1_0.<Handle>g__Handler|0()
   at MediatR.Pipeline.RequestExceptionProcessorBehavior`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next)
   at MediatR.Pipeline.RequestExceptionProcessorBehavior`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next)
   at MediatR.Pipeline.RequestExceptionActionProcessorBehavior`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next)
   at MediatR.Pipeline.RequestExceptionActionProcessorBehavior`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next)
   at MediatR.Pipeline.RequestPostProcessorBehavior`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next)
   at MediatR.Pipeline.RequestPreProcessorBehavior`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next)
   at Project.MarketPlaceAPI.Controllers.OrderController.Get() in C:\git\Project\project-apis\01.Application\Project.MarketPlaceAPI\Controllers\OrderController.cs:line 28
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.Policy.AuthorizationMiddlewareResultHandler.HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Project.Infra.CrossCutting.Middlewares.IPFilterMiddleWare.Invoke(HttpContext context, IOptions`1 applicationOptionsAccessor, ILogger`1 logger) in C:\git\Project\project-apis\04.Infra\04.02.CrossCutting\Project.Infra.CrossCutting\Middlewares\IPFilterMiddleware.cs:line 37
   --- End of inner exception stack trace ---
   at Project.Infra.CrossCutting.Middlewares.IPFilterMiddleWare.Invoke(HttpContext context, IOptions`1 applicationOptionsAccessor, ILogger`1 logger) in C:\git\Project\project-apis\04.Infra\04.02.CrossCutting\Project.Infra.CrossCutting\Middlewares\IPFilterMiddleware.cs:line 82
   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

HEADERS
=======
Cache-Control: no-cache
Connection: keep-alive
Accept: */*
Accept-Encoding: gzip, deflate, br
Authorization: Bearer <REMOVED SECURITY TOKEN>
Host: localhost:<PORT>
User-Agent: PostmanRuntime/7.29.0
Postman-Token: <TOKEN>

Entity:

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
#nullable disable

namespace Project.Domain.Entities
{
    public class Order : UpdatedEntity
    {
        public Order() { }
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int OrderId { get; init; }
        [ForeignKey("Tenant")]
        public int TenantId {get;set;}
        [ForeignKey("TargetTenant")]
        public int TargetTenantId {get;set;}
        public virtual Tenant Tenant { get; set; }
        public virtual Tenant TargetTenant { get; set; }
    }
}

OrderGetDTO:

using System;
using Project.Domain.Entities;

namespace Project.Domain.DTO.MarketPlace.Order
{
    public class OrderGetDTO
    {
        public int OrderId { get; set; }
        public int TenantId { get; set; }
        public int TargetTenantId { get; set; }
        public DateTime CreatedAt { get; set;  }
        public DateTime UpdatedAt { get; set;  }
    }
}

OrderGetResponse:

using System.Collections.Generic;
using Project.Domain.DTO.MarketPlace.Order;
using Project.Domain.Responses.Generics;
using MediatR;

namespace Project.Domain.Responses.MarketPlace.Order
{
    public class OrderGetResponse : RowsResponse<OrderGetDTO>
                                  , INotification
    {
        public OrderGetResponse (bool success, string message) : base(success, message) { }
        public OrderGetResponse (IEnumerable<OrderGetDTO> rows) : base(rows) { }
    }
}

OrderGetRequest:

using Project.Domain.Interfaces.Requests;
using Project.Domain.Responses.MarketPlace.Order;
using MediatR;

namespace Project.Domain.Requests.MarketPlace.Order
{
    public class OrderGetRequest : BaseListTenantUserRequest
                                 , IRequest<OrderGetResponse>
                                 , ITargetTenantRequest  
    {
        public int OrderId { get; set; }
        public int TargetTenantId {get;set;}
    }
}

IOrderGetRepository:

using System.Threading.Tasks;
using Project.Domain.Requests.MarketPlace.Order;
using Project.Domain.Responses.MarketPlace.Order;

namespace Project.Domain.Interfaces.Repository.MarketPlace.Order
{
    public interface IOrderGetRepository
    {
        Task<OrderGetResponse> Handle(OrderGetRequest request);
    }
}

OrderGetRequestHandler:

using MediatR;
using System.Threading;
using System.Threading.Tasks;
using Project.Domain.Interfaces.Repository.MarketPlace.Order;
using Project.Domain.Requests.MarketPlace.Order;
using Project.Domain.Responses.MarketPlace.Order;

namespace Project.RequestHandler.MarketPlace.Order
{
    public class OrderGetRequestHandler : IRequestHandler< OrderGetRequest
                                                         , OrderGetResponse>
    {
        private readonly IOrderGetRepository _repository;
        private readonly IMediator _mediator;
        
        public OrderGetRequestHandler( IOrderGetRepository repository,
                                       IMediator mediator)
        {
            _repository = repository;
            _mediator = mediator;
        }
        
        public Task<OrderGetResponse> Handle( OrderGetRequest request
                                            , CancellationToken cancellationToken)
        {
            var returnValue = _repository.Handle(request);
            _mediator.Publish(returnValue, cancellationToken);
            return returnValue;
        }
    }
}

OrderGetRepository:

using System.Linq;
using Project.Infra.Data.Context;
using Microsoft.EntityFrameworkCore;
using System.Threading.Tasks;
using Project.Domain.DTO.MarketPlace.Order;
using Project.Domain.Interfaces.Repository.Tenants;
using Project.Domain.Interfaces.Repository.MarketPlace.Order;
using Project.Domain.Requests.MarketPlace.Order;
using Project.Domain.Responses.MarketPlace.Order;

namespace Project.Infra.Data.Repository.MarketPlace.Order
{
    public class OrderGetRepository : IOrderGetRepository
    {
        private readonly ProjectContext _context;
        private readonly ITenantRepository _tenantRepository;
        
        public OrderGetRepository( ProjectContext context
                                 , ITenantRepository tenantRepository)
        {
            _context = context;
            _tenantRepository = tenantRepository;
        }

        public async Task<OrderGetResponse> Handle(OrderGetRequest request)
        {
            var tenantRecord = await _tenantRepository.Handle(request);
            if (tenantRecord == null)
            {
                return new OrderGetResponse(false, "Tenant not found") {
                    IsNotFound = true
                };
            }
            
            request.Query = string.IsNullOrEmpty(request.Query) ||
            request.Query.ToLower().Equals("[null]") ? string.Empty : 
            request.Query.ToLower();
            
            return new OrderGetResponse ((
                    await _context.Orders
                                .Where(r=> r.TenantId.Equals(tenantRecord.TenantId) &&
                                           r.OrderId.Equals(request.OrderId))
                                .Select(r => new OrderGetDTO() {
                                        OrderId = r.OrderId,
                                        TenantId = r.TenantId,
                                        TargetTenantId = r.TargetTenantId,
                                        CreatedAt = r.CreatedAt,
                                        UpdatedAt = r.UpdatedAt ?? r.CreatedAt
                                })
                                .ToListAsync()));
        }
    }
}

Order Controller:

using MediatR;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using Project.Infra.Shared.Controllers;
using Microsoft.AspNetCore.Authorization;
using Project.Domain.Requests.MarketPlace.Order;
using Project.Domain.Responses.MarketPlace.Order;

namespace Project.MarketPlaceAPI.Controllers
{
    [ApiController]
    [Authorize]
    [Route("api/v2/[controller]")]
    public class OrderController : BaseApiController
    {
        private readonly IMediator _mediator;
        public OrderController(IMediator mediator)
        {
            _mediator = mediator;
        }

        [HttpGet]
        [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(OrderGetResponse))]
        [ProducesResponseType(StatusCodes.Status401Unauthorized)]
        public async Task<IActionResult> Get()
        {
            return ProcessResponse(await _mediator.Send(new OrderGetRequest()
            {
                TenantId = TenantId,
                UserId = UserId
            }));
        }
        
        [HttpGet]
        [Route("{id}")]
        [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(OrderGetResponse))]
        [ProducesResponseType(StatusCodes.Status401Unauthorized)]
        public async Task<IActionResult> Get(int id)
        {
            return ProcessResponse(await _mediator.Send(new OrderGetRequest()
            {
                TenantId = TenantId,
                UserId = UserId,
                OrderId = id
            }));
        }

        [HttpPost]
        [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(OrderGetResponse))]
        [ProducesResponseType(StatusCodes.Status401Unauthorized)]        
        public async Task<IActionResult> Post([FromBody] OrderGetRequest request)
        {
             request.TenantId = TenantId;
             request.UserId = UserId;
             return ProcessResponse(await _mediator.Send(request));
        }

        [HttpPut]
        [Route("{id}")]
        [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(OrderGetResponse))]
        [ProducesResponseType(StatusCodes.Status401Unauthorized)]
        public async Task<IActionResult> Put([FromBody] OrderGetRequest request
                                                      , int id)
        {
             request.TenantId = TenantId;
             request.UserId = UserId;
             request.OrderId = id;
             return ProcessResponse(await _mediator.Send(request));
        }
        
        [HttpPatch]
        [Route("{id}")]
        [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(OrderGetResponse))]
        [ProducesResponseType(StatusCodes.Status401Unauthorized)]
        public async Task<IActionResult> Patch([FromBody] OrderGetRequest request
                                                        , int id)
        {
             request.TenantId = TenantId;
             request.UserId = UserId;
             request.OrderId = id;
             return ProcessResponse(await _mediator.Send(request));
        }

        [HttpDelete]
        [Route("{id}")]
        [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(OrderDeleteResponse))]
        [ProducesResponseType(StatusCodes.Status401Unauthorized)]
        public async Task<IActionResult> Delete([FromBody] OrderDeleteRequest request
                                                          , int id)
        {
            request.TenantId = TenantId;
            request.UserId = UserId;
            request.OrderId = id;
            return ProcessResponse(await _mediator.Send(request));
        }
    }
}


Solution 1:[1]

I was googling for a solution to the same error: notification does not implement $INotification

Couldn't find anything, but figured out the problem in my code, so perhaps you're dealing with the same issue. I was calling:

_mediator.Publish(MyRequest...)

when in fact I should've been calling

_mediator.Send(MyRequest...)

You can only Publish Notifications. You can only Send Requests. The error is complaining about trying to Publish an object that's not a Notification. Try changing the following line from Publish to Send and see if it works, or verify that the object you are trying to Publish is actually a notification (it needs to implement INotification interface:

_mediator.Publish(returnValue, cancellationToken);

EDIT: I took another look at your code and I believe the issue is that you are not awaiting the returnValue in the first line below:

var returnValue = _repository.Handle(request);
_mediator.Publish(returnValue, cancellationToken);

That causes the returnValue to be of Task<INotification> which is not what the Publish function below expects. You should do the following instead:

var returnValue = await _repository.Handle(request);
await _mediator.Publish(returnValue, cancellationToken);

You will also need to make the function signature async for it to compile: public async Task<OrderGetResponse> Handle( OrderGetRequest request...

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1