background

Recently, I was working on a requirement, which was to catch exceptions in Job and push them to designated personnel by email or message. I encountered an injection problem in the process of requirement realization, which I found very interesting and hereby record.

If you think there is a better way, please tell me, we can discuss together, if there is something wrong, please do not hesitate to correct.


Problems encountered

Because there are many jobs with different functions, each Job needs to send messages when exceptions occur. The existing jobs look like this

In order to realize this requirement and for better maintenance in the future, I plan to implement the mode of event delegation, which will separate the business logic for sending abnormal messages to ensure a single responsibility. The idea is as follows:

  1. Add an IExceptionJobHandler interface between the Job and IJob as a constraint.

  2. The interface defines an event called PushException that sends an exception message.

  3. Then each Job triggers the event when an exception occurs, and the external world only needs to subscribe to one event.

So far this is a good step to take and you might say why do you use an interface instead of a superclass, because the interface is not as tight as the superclass, so if you use it somewhere else, you can modify it a little bit, right It’s not a perfect design, but it’s still pretty clear, but there’s another problem. How do I inject this Job via Autofac?


Problem analysis

It took me about 4 hours to find a solution to this problem, researching dependency injection methods and searching the Internet for a long time, but I didn’t make any progress. Then I combed through the framework dependency injection code and realized that it was the wrong way .

To use Quartz in WebAPI, add the following code to AppStart:

 var builder = new ContainerBuilder();
 builder.RegisterModule(new QuartzAutofacFactoryModule
 {
     ConfigurationProvider = c => schedulerConfig
 });
Copy the code

The QuartzAutofacFactoryModule there must be a I want to know something, just download on making Autofac. Extras. Quartz source, open on see, found it indeed as expected.

It has an AutofacJobFactory class that receives an ILifetimeScope, which is used to build service instances that the Job executes for the scope cycle.


The solution

  1. AutofacJobFactory is the factory that builds the Job service instance, so we should start with it and find the code to build the service

    • When I call the ResolveJobInstance method, I will return an IJob. How do I inject my custom interface IExceptionJobHandler into the Job instance?
  2. IExceptionJobHandler inherits from IJob, IJob, IExceptionJobHandler

  3. We observe that the method used to build the service at the AutofacJobFactory is a virtual method, so we can use inheritance to solve this problem. We create a new ExtendAutofacJobFactory to override it by replacing Ijob with IExceptionJobHandler

public class ExtendAutofacJobFactory : AutofacJobFactory { protected override IJob ResolveJobInstance(ILifetimeScope nestedScope, IJobDetail jobDetail) {// Verify whether the Job is derived from IExceptionJobHandler if (typeof(IExceptionJobHandler).IsAssignableFrom(jobDetail.JobType)) { IExceptionJobHandler instance = null; instance = (IExceptionJobHandler)CreateIJob(nestedScope, jobDetail); / / register event unified handling entry instance. PushException + = pushExceptionMessageManager. Value. ExceptionJobHandler return instance; } else { return base.ResolveJobInstance(nestedScope, jobDetail); }}}Copy the code
  1. Next we need to rewrite the construction of the factory in QuartzAutofacFactoryModule, but QuartzAutofacFactoryModule also did not provide set custom factory entrance, the only still choose to inherit way to rewrite it.

  2. Then change QuartzAutofacFactoryModule loading services the service structure factory with our own ExtendAutofacJobFactory extension.

  1. In AppStart Autofac registration service place will QuartzAutofacFactoryModule custom ExtendQuartzAutofacFactoryModule instead, thus completed the custom injection.
builder.RegisterModule(new ExtendQuartzAutofacFactoryModule
{
    ConfigurationProvider = c => schedulerConfig
});
Copy the code

conclusion

When being asked about the characteristics of the object-oriented, 99% of people who can answer: [package] [inheritance] [polymorphic], even for questions of people deeply in the heart, despise, the xx how to ask this question, but after a period of time after the development experience of the precipitation and accumulation, found that often the simplest and most basic knowledge, is the most important.