Schedule Jobs! - Beginner's Guide to Quartz.NET in ASP.NET Core
July 25, 2024
• 11,505 views
Building a web service that has to do some heavy lifting? Here's what you probably DON'T want to do:
Block the request for 3 minutes while you do the work.
How about if you need to run some work on your server on a schedule? Can we do better than Task.Delay every X hours to go get things fired off?
An answer to both of these questions can be found with Quartz.NET! In this video, I'll walk you through one of the ways that you can get this job scheduling framework set up in your ASP.NET Core applications.
View Transcript
running background scheduled work for your service is a pretty common task but it turns out it's not necessarily straightforward to go roll your own system like this hi my name is Nick centino and I'm a principal software engineering manager at Microsoft in this video I'm going to walk through setting up quartz.net using sqlite as a backing store so that we can have jobs get scheduled now this video is going to walk us through getting this set up in asp.net core one way and if you stay to the end of this video we can see another way how to do this keep in mind this is going to be for SQL light but you can take the Concepts and go ahead and apply them for other backing stores as well if that sounds interesting remember to subscribe to the channel and check out that pin comment
for my courses on dome train with that said let's jump over to visual studio check out an aspor app and get Quartz set up okay to start things off here I just have a very simple application if you look at the very top here this is a really lightweight single minimal API app we're not even going to be using the minimal API itself this is just to get going to launch our application I do want to start off with some n get package though so I'm going to jump over to the project file here and the ones that I want you to pay attention to are quartz so I'm using quartz 3.11 for this video we're going to need quartz serialization system text Json also at 3.11 this is going to be for later so you don't need this one for this video currently and
then we're going to need system data SQL light and like I mentioned at the beginning of this video if you don't want to use SQL light as the backing store that's okay you can pick a different database the concepts in this video are going to be the same it's just that we're going to be using sqlite to keep things simple okay so at the beginning of the application we are setting up an asp on at cor app and you'll notice I'm not calling run just yet if we scroll a little bit lower you can see that I have the app run down here and you'll notice that there's a bunch of code in the middle here before we call that app.run line so I'll explain what this is doing what parts are mandatory and what parts are just for testing right here so if we
go up a little bit further we can see that there's this part here that's going to be setting up a Schuler and this is kind of the core part of what we're looking at in this video so you can see that I'm using a schedule Builder I'm creating it out of a set of properties and this is just a name value collection that you can see here you can basically configure a lot of these properties right off of this sort of fluent Builder pattern syntax that we have here as well but you can also do it with these key value pairs quartz allows you to have a configuration file I'm often not a huge fan of messing around with configuration files to get things going I like to kind of see how they work in code and then maybe when it comes time for production
if I want to be able to configure these things I'll pull that kind of logic back out into a config file but the config file itself is really just a set of key value Pairs and you can find a lot of these configuration settings on the courts.net website so this one right here is just going to be setting the table prefix because we are going to be persisting the jobs from courts into a database the prefix is going to be qrt zore and those tables when they get added to our database we'll use these prefixes I just have a couple of other settings just to play around with them on here it's not really going to be applicable for this demonstration here but just to illustrate that you can use this fluent syntax to configure other things so if we wanted to we could go
set some other things on here as well so if we go into this block use persistence store this is where we're going to be seeing how we can configure sqlite in particular so use properties is going to allow us to uh take these values here so we'll be able to read in the value of that properties collection the use system text Json serializer is coming from that nougat package that we imported so there are other serializers that you can use but I want to use the system Json serializer and we can see that online 240 this is the part that's going to be unique for sqlite in particular so the string that we're passing in is the connection string just for demonstration I have the database just as quartz. DB in the running directory so I'm not doing anything fancy with my connection string if
you were using you know postgress MySQL SQL Server you might want to load that connection string from a configuration file and then be able to pass it in right to here and this is so that quartz again has a persistent store mine is just going to be sqlite here before we move on this is just a reminder that I do have courses available on D train if you want to level up in your C programming if you head over to dome train you can see that I have a course bundle that has my getting started and deep dive courses on C between the two of these that's 11 hours of programming in the C language taking you from absolutely no programming experience to being able to build basic applications you'll learn everything about variables Loops a bit of async programming and objectoriented programming as well
make sure to check it out I do have this uh set to true for performance scheme of validation but I do believe that this setting is true by default but you can do other configuration here so you can set retry intervals all these other things and you can also see that these are the providers that are sort of built in and provided by default so postgress Oracle MySQL Firebird so yeah lots of different things in here and I believe that there's nougat packages to extend this so again Concepts that you see here can be applicable to other data stores now the other thing before we get into some of the code below is that I wanted to call out that with this kind of thing going on here with the quartz tables the idea is that we're not getting this set up by Quartz in
the first place so if we were to just go run this right now sort of all this logic up until line 250 we're going to have it crash and the problem is that quartz does not yet have the schema created it doesn't go and create that schema for you if you're watching this and you know of a setting that we can go use or a method call to basically go instantiate that for us and set up all of our schema would be great to hear but at the time of recording this I'm not aware of that but if you do check out their GitHub for all of the different providers that they have including sqlite like this example I'm going to go expand this block of code here this is basically the code right from the repository where we can go run these SQL commands
to go stand up that schema for ourselves so I ran it once kind of like an Entity framework core migration right I just wanted to go run this thing once now that my scheme is set up for my database I don't have to go run this code again you will want to consider this how this is going to look when you're standing up a database for the first time but just to have a quick look here I'm just opening a connection and setting the uh making a new command and then setting the text to be this entire Block it's pretty big but I just pulled this right from their GitHub and then I execute that query right at the end you don't have to worry about the schema because if again if you go to their GitHub for quartz.net if you go into the databases
folder there you will see that they have all of these uh SQL commands that you can go run so don't have to worry too much but I wanted to call that out because if you just follow this bit of tutorial so far go run it you'll say what the heck's happening why is it exploding that's why you don't have the table set up so this stuff is a onetime thing and you're going to want to keep this in place but then the Schuler start is sort of that next interesting part that's important for us because without starting the scheduler you won't be able to go run any jobs which brings us to the next point if we have a look here from line 252 I guess technically down to 280 we do want to be able to use this kind of code to test out
running a job what are the pieces of jobs inside of of Courts well jobs are uh one part and then there's a trigger that's associated with it so in their architecture and their design they've sort of broken these things apart so it doesn't mean that you create a job and as part of the job's definition it just has a schedule or some sort of timer built into it you actually Define these things separately and that way you can have a job independent of a trigger that needs to go run it so it's kind of an interesting pattern but it can make things a little bit confusing if you're not totally familiar with why these things are separate and the fact that they are separate in the first place so what we're doing here is building a trigger and you can see that I'm saying this
trigger is going to be for a job and a job is defined here by a key and you'll notice a little bit below uh there is this job detail which I'll get to in just a second but the trigger itself is going to be assigned to this job key so they're linked together by this I'm saying that I want to start this trigger 5 seconds from now and then I'm going to build this trigger and then the other part here is just giving it an identity the first part's a name and the second part is a group and that's the same thing that we see for this job key so this is a trigger key and this is a job key but this part is the name and this part is a group if you were to go look at the schema in the database
you'll notice that they have columns for these things so it's just a a sort of organization for you now the job detail is an interesting piece because again it's going to be assigned an identity that's coming from the job key right so the job detail will be linked to the job trigger bu this job key if you look on line 255 and 266 you can see the same job key in both spots the job Builder that's going to create this job detail you'll see that it takes in this typ parameter and that says our test job so if I scroll a little bit lower you can see that I am defining a i job here right here called our test job and the interface method that you need to implement is this execute one that we see on line 289 it takes in this job
execution context and then we're a able to go run things asynchronously in here for this example I'm just going to print hello world to the console very simple nothing fancy now once we have the job detail and the job trigger set up you'll notice on line 280 that I'm telling the scheduler hey I want to go schedule this job with this trigger a little detail to call out is that usually when we're running asynchronous things the pattern for naming is to suffix them with async you'll notice in ports that's not the pattern that they're using so start is asynchronous but there is no async on the end schedule job is asynchronous there's no async on the end and execute as well right it's a returning a task this can run asynchronously but they're not suffixing it so just something to pay attention to visual studio
does a great job of warning you for these types of things so if you're removing the await it will say hey look looks like you're about to run a task here are you sure that's what you want to do if you're not awaiting it it will help you catch these types of things but just pay attention to it cuz I noticed that a couple of times for myself at this point what we have is an asp.net core application right that's the very beginning we are setting up quartz and we're going to basically hook that up to a sqlite database called quartz DB in the running directory we have a couple of parameters that we're setting here but there's lots of other options that you can play with we start that scheduler now once that's up and running and before we start the asp.net core app
online 282 we are going to create this little job that prints hello world to the console so it looks like this is a lot of work to go set this up right but once we have the job defined we can basically go configure different triggers this one is just going to fire it off 5 seconds from now but I should have called out that there are these other ways to go schedule so you could basically have um a crw job set up and set different intervals and things like that which is super cool you can do that this one's just going to trigger it in 5 seconds we schedule that on line 280 and then we say to go run the asp.net core app if you didn't want to use asp.net core this is an example of how you could go set up all of
this stuff for quartz right I could go remove the app.run and all of the stuff with the minimal API we could just use quartz as it was set up here and this would allow us to go schedule things let's go ahead and run this example we should see that after the app starts up approximately 5 seconds after because the app itself has some startup time we should see that we have hello World printed to the console so let's go run it and check it out okay so our app has started up here and you can see that in the console it printed hello world so that was probably my bad for hiding the console window behind if we try again let me see if I can pull this up in time nope there we go and hello world gets printed so you can see I
managed to do it quick enough that time to pull the window in front but we do get hello world written to the console and that is coming from that Court's scheduled job if you were to open up the database while that's happening you would be able to see that you have a database row get written for the job in the trigger and then that way when courts is uh checking the database State it's able to see that there's a job pending this is a super quick way that you can get set up with courts like I said we did look at this for SQL light but you can apply this to different storage means and the way that we configured this was to work with asp.net core but like I said a little bit earlier it's not directly coupled to it we could have run
this in a console application as well so it does not rely on asp.net core it's just the way that we set this up in this case now I did mention at the beginning of this video that if you wanted to see an alternate way that we could set up quartz to hang out to the end so if you want to see how we can set up quartz specifically inside of asp.net core you can go ahead and check out this video next thanks and I'll see you next time
Frequently Asked Questions
What is Quartz.NET and how is it used in ASP.NET Core?
Quartz.NET is a powerful and flexible job scheduling library that allows you to run background tasks in your applications. In this video, I demonstrate how to set it up in an ASP.NET Core application using SQLite as the backing store, allowing you to schedule and manage jobs effectively.
Do I need to use SQLite as the database for Quartz.NET?
No, you don't have to use SQLite. While I used SQLite in this video for simplicity, the concepts I cover can be applied to other databases like PostgreSQL, MySQL, or SQL Server. You can choose the backing store that best fits your needs.
What do I need to do to set up the database schema for Quartz.NET?
Quartz.NET does not automatically create the database schema for you. You need to run specific SQL commands to set up the schema yourself. I provide a code snippet in the video that you can use to create the necessary tables, which you can find on the Quartz.NET GitHub repository.
These FAQs were generated by AI from the video transcript.