0

We have a .NET Core application that uses dependency injection heavily. Within our class we have a raised event, however, in this event we want to make use of one of the instances of an DI object. This however doesn't exist, which is understandable.

How can we make use of this instance from within the event?

public class GetSQLDependancyHandler : IRequestHandler<SQLDependancyQuery>
{
    private readonly ISqlDependancy _sqlDependancy;
    private readonly IMapper _autoMapper;
    private readonly IMediator _mediator;
    private  CancellationToken _cancellationToken;

    public GetSQLDependancyHandler( IMapper autoMapper, 
        ISqlDependancy sqlDependancy,
        IMediator mediator)
    {
        _autoMapper = autoMapper;
        _sqlDependancy = sqlDependancy;
        _mediator = mediator;
    }

    public async Task<Unit> Handle(SQLDependancyQuery request, CancellationToken cancellationToken)
    {
       await _sqlDependancy.IngnightSQLDependancy("ObservationAdmission", "ServiceBroker");
        _sqlDependancy.OnTableUpdate += _sqlDependancy_OnTableUpdate1;
        _cancellationToken = cancellationToken;

        return Unit.Value;
    }

    private void _sqlDependancy_OnTableUpdate1(object sender, BroadCast e)
    {
        // THIS IS THE PART THAT DOESN'T EXIST
        _mediator.Publish(new SQLDependancyDTO { Id = 1,  Date = DateTime.Now, Message = "testmessage" }, _cancellationToken);
    }
}

Any advice or where I should be looking would be greatly appreciated.

janw
  • 8,758
  • 11
  • 40
  • 62
Simon
  • 1,412
  • 4
  • 20
  • 48
  • 1
    What is the lifetime of `IMediator`? Is it scoped, a singleton, ...? – janw Oct 08 '21 at 19:58
  • I can't really tell you exactly the right answer, but I can tell you that subscribing to OnTableUpdate should not be done in a handler. Either pull the OnTableUpdate1 code into the Handle method or create a separate handler for that code – Dennis VW Oct 08 '21 at 20:03
  • When you say "does not exist" do you mean it's null? For starters change the constructor to use `_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));` so you can ensure it's being constructed properly. Secondly check the lifetime of this instance, is it being disposed before the handler is called? – Ian Mercer Oct 08 '21 at 20:03

2 Answers2

3

The problem is most likely the lifetime of _mediator: The OnTableUpdate event may fire long after whatever uses GetSQLDependancyHandler has completed and the object got cleaned up.

The common solution is injecting an IServiceScopeFactory object (which is a singleton), which you can then use in your event handler:

private void _sqlDependancy_OnTableUpdate1(object sender, BroadCast e)
{
    using var scope = serviceScopeFactory.CreateScope();
    var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
    mediator.Publish(new SQLDependancyDTO { Id = 1,  Date = DateTime.Now, Message = "testmessage" }, _cancellationToken);
}

In any case, subscribing to an event of an injected object while being in a handler is probably unsafe: If the _sqlDependancy object is ever reused, the event handler subscription stays and may surprisingly be executed again in the future.

janw
  • 8,758
  • 11
  • 40
  • 62
0

Not sure if this addresses the problem, but you could try capturing the dependency in a closure when subscribing.

public async Task<Unit> Handle(SQLDependancyQuery request, CancellationToken cancellationToken)
{
   await _sqlDependancy.IngnightSQLDependancy("ObservationAdmission", "ServiceBroker");
    _sqlDependancy.OnTableUpdate += (s,e) => _sqlDependancy_OnTableUpdate1(s,e,_mediator);
    _cancellationToken = cancellationToken;

    return Unit.Value;
}

private void _sqlDependancy_OnTableUpdate1(object sender, BroadCast e, IMediator mediator)
{
    mediator.Publish(new SQLDependancyDTO { Id = 1,  Date = DateTime.Now, Message = "testmessage" }, _cancellationToken);
}
John Wu
  • 50,556
  • 8
  • 44
  • 80