NAILED IT! - ASP.NET Core Blazor Plugin API For Quartz
February 5, 2024
• 933 views
blazorblazor tutorialblazor web applearn blazorblazor tutorial 2021razor pagesblazor webdotnet 8blazor appblazor tutorial for beginnersblazor web app tutorial.net blazorasp.net core blazorblazor app tutorialblazor c#blazor tutorial c#blazor introductionasp.net core 8blazor pluginsquartz dotnetquartz.net scheduler example c#quartzquartz c#c# job schedulertask schedulerdotnet job scheduledotnet job schedulerdotnetcsharp
In our ASP.NET Core Blazor build series, we hit a bit of a barrier with our Blazor plugin API not being fully compatible with Quartz. How we'd like to schedule our dotnet jobs means that we need to revisit our plugin API to better fit with Quartz.
Join me as we explore building our ASP.NET Core Blazor web app and experiment with integrating Quartz .NET for job scheduling!
Have you subscribed to my weekly newsletter yet? A 5-minute read every weekend, right to your inbox, so you can start your...
View Transcript
and it turns out I can't quite do exactly what I wanted to at least the way I had things set up we're going to continue on our journey of building a Blazer web app where we left off before was leveraging quartz to try and do job scheduling and where this gets interesting is that we have this plug-in system that we need to interface with this job scheduler but the API that we started to spec out isn't really going to fit anymore and that's because instead of having one big job that's going to do all of the plugins it's looking like it's going to be a better fit to have one job per plugin that we want to support So if you're just jumping into this series you can check out the previous video right here and where we're trying to head with this now is
come up with the new API spec for getting our job scheduling to work with our plugins we're getting really close to the point where we can start getting data from the internet and putting it into a database so it's going to be very interesting to see where we get to now before I head over to the code just a quick reminder to check that pin comment for a link to my newsletter and courses that I'm working on all right let's check out some code so where we left off was coming up with this idea that we'd have something like this interface that I have highlighted here from line 52 to 60 and the idea with this plug-in interface is that each plugin could provide some type of mechanism to have information about the different types of jobs they would need now that started to look
like some unique job identifier and this is not per instance of the job it's really to identify the type of job so that was important so we have this job type ID and we were saying that the job type this list alnet type here is probably not a good fit but how this was being leveraged if you watch the previous video was that we were using dependency injection to resolve the implementation of the job and then we also had this interval but where I really wanted to focus on in this video is this dependency resolution because the implementation of the job being in the plug-in is still a little bit weird and the reason that it's weird is that it requires that each plug-in has a dependency on quartz I would really like for the core application to have the dependency and have the plugins
be agnostic to that I'd like them just to have an implementation of the work they need to have done and then the core application is really the one that's aware of Courts being the scheduler so let's go have a look at what we might need to change to make that happen we had this Twitter job that we started to spec out and my opinion was that we probably need to go back towards having more of a generic approach here and the generic approach really com down to the fact that when we go to execute the job we have to do some type of fetching with the internet and then we have to be able to not write hello world but instead put this content into a database that we haven't come up with the schema for yet so I would like to personally try and
transform this job into something that's generic and then what we can do is when we construct this we can pass in the necessary information from the plugin that really is going to have something like the data fetcher so we can pass that in kind of like this parameter right here and then we can also when we get to having Entity framework pass in the context creation and sort of the access pattern for getting to write that data out to the database all right so all that I've done to start with is basically rename all of the stuff from the Twitter job to be just this generic fetching job and you'll also notice if I correct myself here I want to pass in isocial data fetcher instead of a Twitter one specifically and that means what we're going to not do is register this type of
class on the dependency injection framework and we're not going to register this one either what we are going to do is make sure that we have some type of Provider that's in the plugin itself so one for Twitter where we can say hey Twitter what's your social data fetcher give us the instance of that and then what we can do is take that instance and create a new fetching job with that fetching job instance we can add that into quartz so if I scroll back up to this interface that we were looking at before instead of using this job type recall that this job type was really just here to be able to do the dependency uh injection the resolution to get that particular implementation of the job type I'm instead just going to ask for a social data fetcher and what we need to
do at this point is go make the Twitter implementation of this because when we do that we can ask all of the plugins give us all of your Social job providers then for each one of those we can say what's your unique job type ID because if I scroll back up you can see that we need something unique per job and we're going to use that in here as well and just a pen trigger on the end this is just to start off and then instead of using this add job like this we're going to have to register that job in a different way but we're going to create that job instance just like we saw below with that new class just the renaming we did and pass in the instance of the data fetcher from here what I'd like to do is start thinking
about how we can register the Twitter dependencies and get one of these social job providers set up specifically for Twitter so to start what I'm going to do is just go make a generic implementation of this because I think that I can just leverage it sort of like a container a dto of sorts and then that way we might not even need the interface itself I can literally just have a dto and simplify it all right so with the interface gone I just have this new record instead it's literally the same type of thing just not an interface it's going to be a sealed record that we can reuse all right and then when it comes to dependency resolution full disclosure I come from using using autofac for many many years I'm not very used to using the built-in. net dependency injection framework so this
is probably going to evolve as we go through this but I'm learning with you here so what I've done is just change this registration that we used to have right at this point that instead of having the Twitter job which I need to go delete now I'm creating a new instance of a social job provider I'm going to give it this unique ID for Twitter it's just going to be a string for now I don't think we need anything more intense than that at this moment then what I'm going to do is resolve this Twitter data fetcher that I've registered up here and that's going to be our isocial data fetcher that gets passed into this dto and then I've just said that we're going to go run this every hour I have no idea what this interval needs to be for now and to
be completely honest we may find that later on we don't want each plug-in to be able to be configurable this way maybe the core application should be responsible for this but we don't know yet this is the very first plugin that we're going through to try and Implement and that's something that I want to call out with plug-in architectures is that you may not know early on I didn't do a ton of upfront design for this I haven't specked out a ton of different things because I'm not working with a lot of different people I'm just kind of doing this fluidly and feeling it out and if I don't get to a point where I have 50 plugins if I have to Pivot and change the API it really shouldn't be so hard now this is where it's going to get interesting because I am
used to autofac and not the built-in. net dependency injection framework so what I would like to do at this point is replace this pattern here instead of creating a Twitter one specifically what I would like to do is go ask for all of the social job providers that we have up here right now there's only one of course but I would ask for each one of them iterate through them and then from there go add them into quarts and it turns out I can't quite do exactly what I wanted to at least the way I had things set up I had to take a little bit of a break to go figure it out because doing it on camera was just too ridiculous so here's what I ended up with the first problem and this is again just because I'm new to the net dependency
injection framework coming from autofac and I'm trying to practice with this a little bit more as we go build this Blazer app now in here this is where I wanted to try and do the job registration but the problem with that was that our dependency container is not yet fully configured so what I was attempting to do was try and resolve the dependencies that we have up here within this method but they're not ready yet so asking for them was yielding nothing that I wanted and it was blowing up pretty fast so that was the first Challenge and the Second Challenge is that the way that we register jobs inside of quartz does not work the way that I wanted to jobs inside of quartz must be resolved through dependency injection and that means that I can't easily compose them fortunately I found a stack
Overflow answer thank God because I was totally puzzled as to how I would do this now I would think that they would just offer you some option to be able to compose one on the Fly it's just not the case now what we are able to do is make a generic one and then we can resolve parameters in a different way so let's go check out all the changes that I made and prove that we have this working now CU it's really exciting so the first part like I mentioned is that I moved the code that was trying to to schedule the jobs from here from line 24 to 27 down to a lower part in the code so if I scroll down a little bit lower you can see right before we start the application which is right here on line 80 right before
we go to do that what I'm doing instead is doing all of the registration this is what I tried to write above now it's going to be a little bit complicated and it's changed a little bit from what we saw earlier but let me explain what's going on the first thing is that we need to resolve the scheduler Factory from quartz this is one of the services that gets put onto the dependency injection framework from the methods that we called above once we have the scheduler Factory we can ask to go create the scheduler and we get this quartz instance here that allows us to go do the scheduling work the next part and this is what I was trying to do with the dependency injection framework is that I want to go ask for all of the social job providers with autofac the way
that I'm normally able to do this is that if I ask for dependencies during registration it figures out the right way to order them such that when I go to ask for them it says oh I have to go get the prerequisites let me go figure that out first in this particular case I didn't quite have that luxury so what we're doing is doing all of this work later on after we've created the app but before we've started it so we get this collection of all of these social job providers which right now is just the Twitter implementation that we have or it said more clearly it's sort of this DT that we have where I passed in the Twitter data now what we do is we make the job key and this comes right off of the provider so the job type ID is
going to be just Twitter in this case and nice simple string and we can see that this job Builder had to take in this type here and I'm going to show in just a moment how I had to alter this fetching job because we can't pass in the data fetcher through the Constructor anymore and that's because we can't provide parameters from specific places on on the Constructor this way but what we do have access to is this job data map and this job data map is effectively like mapping parameters into the job it's just that they're not through the Constructor so it feels a little bit clunky to me but what's really cool about this is that we only put it in one spot so this is a common thing that I come across when I'm building framework type things where Sometimes some of the
code it's ugly it sucks and I don't like it but it's in one spot and that means that to go extend this to go add more plugins all of those locations become nice and clean and the trade-off is that I have one spot where I don't like looking at it and I can live with that so again we'll see this in just a moment on the consuming side but then we end up adding the ID which is just the job key we added above and then we create the trigger very much like we did before so we get the job type ID and Tac Dash trigger onto the end of it and then from there we just ask to schedule the job this is pretty simple pretty pretty straightforward and it's what I was hoping it would look like except for this part's a little
bit gross and where we're running this is a little bit later than I expected but overall it's still before the application starts up which is pretty cool and I think that if we needed to do something later where we want to dynamically drop in plugins instead of having to kill off the whole app I think we could come up with something pretty cool later on if we need to now the thing that I had to go change with respect to the job is the parameter passing through the Constructor that had to go away you can see that this class now has no Constructor at all it's just the default one that's built in and instead the way that we get parameters passed in is through this merged job data map and what I'm able to do is just ask for a parameter by a name
so I have the I'm just using the name of the interface I didn't want to come up with a a very intricate key mapping right now I'm not very creative that way so if we need to do something more we will later I I figure just asking for the name based on the interface that we're looking for makes it kind of obvious when we go to read it so I ask for this uh I social data fetcher and then I'm just using a debug assert because right now as I'm exploring this I want this kind of stuff to blow up early if uh it's not working as I expect now I'm not calling the social data fetcher because we haven't gone and fully implemented what we need for Twitter with respect to the API keys and secrets but if we put hello world right here
and I go run this we'll see that we get it printed out let's go try it before I show you the output I just wanted to show you that as we're debugging this you can see that it's enumerating and getting the social job providers so this is me stepping through and having it create the one for Twitter so it goes ahead and schedules the job now I'm just going to press F5 and we'll see the output saying hello world in the console and as you can see we've gone into the fetching job and we hit the console right line so all is well this is where we will later on go change this part to not say hello world but instead we'll have this uncommented and what we need to do next is figure out how we're going to write this data through Entity framework
and I think that's a good spot for us to pause and think about what's going to come up in the next episode of this series so now at this point we have a pretty basic implementation for an API of plugins that we could work with we can see that we can register jobs from from something like Twitter and we'll see if it's going to apply well to the other plugins as we start to go do them so that's why we're keeping it nice and lightweight for now but we have a plug-in API that lets us create jobs dynamically so that's pretty cool we have this concept of a social media data fetcher that we can go Implement and again the API for that might have to change as we explore but I think we're at this cool turning point where we can start to say
awesome let's go pull data from the internet and what do we want to do with it and I think in the next episode what we should look at is trying to model some Entity framework let's go see in the next video if we can start to write this data out to a database thanks for watching and I hope you enjoyed this take care
Frequently Asked Questions
What changes are being made to the job scheduling API for the plugins?
We're shifting from a single job handling all plugins to having one job per plugin. This allows for better organization and management of the jobs, making it easier to interface with the job scheduler.
Why is the dependency resolution for jobs considered weird?
It's considered weird because each plugin currently has a dependency on Quartz, which I want to change. Ideally, the core application should manage the Quartz dependency, allowing plugins to focus solely on their specific implementations.
What is the next step after implementing the basic job scheduling for plugins?
These FAQs were generated by AI from the video transcript.The next step is to model the Entity Framework so we can start writing the fetched data to a database, which will be an exciting advancement in our project.
