How does Hangfire locate the correct concrete class instance for performing a job enqueued based off of an interface?

Hello,

My Question

When performing a background job, how does Hangfire know exactly which concrete class to use for performing a job? FYI, this is not documented – that is, enqueuing a job when all you know about is the interface that has a particular instance method.

NOTE: If the solution to this question is documented, would someone please be so kind as to point me to the link? Thank you.

The Situation

Let’s go back to the EmailSender class from the documentation – but, this time, it implements an IEmailSender interface. Let’s suppose the definition of the IEmailSender interface is such:

public interface IEmailSender
{
  void Send(int userId, string message);
}

Listing 1. The definition of the IEmailSender interface.

Let’s suppose that a concrete class, EmailSender, implements the IEmailSender interface, and is defined thus:

public class EmailSender : IEmailSender
{
    public void Send(int userId, string message)
    {
        var dbContext = new DbContext();
        var emailService = new EmailService();

        // Some processing logic
    }
}

Listing 2. The definition of the 'IEmailSender` class.

Now, let me create a C# Windows Service project in VS2022, and name it MyEmailingService, that sends emails. The EmailWindowsService class is derived from System.ServiceProcess.ServiceBase, and I am writing my Windows Service using .NET Framework 4.8.

Now, I install Hangfire.Core and Hangfire.SqlServer into my project, using the Package Manager Console of VS2022. I implement my EmailWindowsService class thusly:

using System.ServiceProcess;
using Hangfire;
using Hangfire.SqlServer;

namespace MyEmailingService
{
    public partial class EmailWindowsService : ServiceBase
    {
        private BackgroundJobServer _server;

        public EmailWindowsService()
        {
            InitializeComponent();

            GlobalConfiguration.Configuration.UseSqlServerStorage("connection_string");
        }

        protected override void OnStart(string[] args)
        {
            _server = new BackgroundJobServer();
        }

        protected override void OnStop()
        {
            _server.Dispose();
        }
    }
}

Listing 3. The definition of the EmailWindowsService class.

Of course, connection_string is just a placeholder for the actual connection string I am utilizing. Let’s pretend that it has the correct information in it that is needed to connect to a SQL Server database.

Suppose that the .csproj that contains my EmailWindowsService only can reference a EmailSenders.Interfaces.csproj project in my Solution that exposes the IEmailSender interface. If I have circular references, then I cannot refer to the concrete class, EmailSender, that implements that interface. Okay, now I enqueue a background job in my OnStart() method:

protected override void OnStart(string[] args)
{
    _server = new BackgroundJobServer();
    
    BackgroundJobServer.Enqueue<IEmailSender>(x => x.Send(13, "Hello"));
}

Listing 4. The definition of the OnStart method, with the enqueuing of a background job added.

My question, again, is: I’ve passed IEmailSender, not the concrete class, EmailSender, to the generic parameter of the Enqueue method. How does Hangfire know which concrete class, that implements the IEmailSender interface, to actually go ahead and create, to actually perform the work?

You register the service. It’s part of the dependency injection.

https://docs.hangfire.io/en/latest/getting-started/aspnet-core-applications.html#registering-services