<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Valentin Fritz (aka. VFRZ) - A .NET enthusiast blog]]></title><description><![CDATA[.NET Core, Blazor, compilers, OpenGL & more]]></description><link>https://blog.vfrz.fr/</link><image><url>https://blog.vfrz.fr/favicon.png</url><title>Valentin Fritz (aka. VFRZ) - A .NET enthusiast blog</title><link>https://blog.vfrz.fr/</link></image><generator>Ghost 3.42</generator><lastBuildDate>Sun, 15 Mar 2026 11:39:51 GMT</lastBuildDate><atom:link href="https://blog.vfrz.fr/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[2D silhouette effect in OpenGL]]></title><description><![CDATA[In this blog post, I'll explain how to make a cool 2D silhouette effect in OpenGL. For this we are going to write a simple fragment shader, and use the stencil and depth buffers.]]></description><link>https://blog.vfrz.fr/2d-silhouette-effect-in-opengl/</link><guid isPermaLink="false">6030223b95369700018f251a</guid><category><![CDATA[OpenGL]]></category><category><![CDATA[Silhouette]]></category><category><![CDATA[Shader]]></category><category><![CDATA[Stencil]]></category><category><![CDATA[Depth]]></category><category><![CDATA[C#]]></category><category><![CDATA[GLSL]]></category><dc:creator><![CDATA[Valentin Fritz]]></dc:creator><pubDate>Sat, 20 Feb 2021 15:23:58 GMT</pubDate><content:encoded><![CDATA[<p>In this blog post, I'll explain how to make a cool 2D silhouette effect in OpenGL. For this we are going to write a simple fragment shader, and use the stencil and depth buffers.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.vfrz.fr/content/images/2021/02/FinalResult.gif" class="kg-image"><figcaption>Final result</figcaption></figure><p>Since my favorite programming language is C#, I am going to use C#, but the code should be easily translatable to other languages. (Shaders are in GLSL). For the rendering I will use my own game engine which has a basic 2D renderer. Finally this post isn't for total OpenGL beginners (I won't use any advanced or niche features), so I advise you to visit this amazing website if you don't know much about OpenGL: <a href="https://learnopengl.com/">https://learnopengl.com/</a></p><h2 id="drawing-the-player-tank-and-the-masking-object-wood-crate-">Drawing the player (tank) and the masking object (wood crate)</h2><p>I will be using a free "tank assets pack" from Kenney, available here: <a href="https://kenney.nl/assets/tanks">https://kenney.nl/assets/tanks</a></p><p>First we need to enable blend for transparency and depth test:</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">GL.Enable(EnableCap.Blend);
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
GL.Enable(EnableCap.DepthTest);
</code></pre>
<!--kg-card-end: markdown--><p>Then we can simply render our player's tank and a wooden crate like this:</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">_renderer.Begin(_camera.GetProjection());
_renderer.Draw(_tank, _tankPosition, Color4.White, _tankPosition.Y / 720f);
_renderer.Draw(_crate, new  Vector2(64, 64), Color4.White, 64f / 720f);
_renderer.End();</code></pre>
<!--kg-card-end: markdown--><p><em>(720 is the height of the game window, you might want to adjust this value so the depth value is always between 0 and 1)</em></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.vfrz.fr/content/images/2021/02/image.png" class="kg-image"><figcaption>The tank is behind the crate, everything is fine.</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.vfrz.fr/content/images/2021/02/image-1.png" class="kg-image"><figcaption>The tank is in front of the crate, but it looks like we have a transparency problem...</figcaption></figure><p><a href="https://www.khronos.org/opengl/wiki/Transparency_Sorting">Transparency sorting</a> is a (hated) famous and common problem. In our case we are going to use a simple solution, which is discarding pixels with alpha value of 0. You can add those line at the end of the main function of the main fragment shader:</p><!--kg-card-begin: markdown--><pre><code class="language-c">if (fragColor.a == 0)
{
    discard;
}
</code></pre>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.vfrz.fr/content/images/2021/02/image-2.png" class="kg-image"><figcaption>Now the tank can get in front of the crate without transparency problem (or maybe not?)</figcaption></figure><p>If you have sharp eyes you can see a sort of outline around the tank. This is because the tank texture has some semi-transparent pixels on the edges for making them smoother. Since this is not the main subject of this blog post, I won't go any further.</p><h2 id="drawing-the-tank-s-silhouette">Drawing the tank's silhouette</h2><p>For this we will need to write a custom fragment shader which output the tank's shape as a static color:</p><!--kg-card-begin: markdown--><pre><code class="language-c">#version 330

in vec2 vertTexturePosition;
in vec4 vertColor;

uniform sampler2D uSampler;

out vec4 fragColor;

void main()
{
    fragColor = vec4(vertColor.rgb, texture(uSampler, vertTexturePosition).a);
}
</code></pre>
<!--kg-card-end: markdown--><p>We only use the texture to get the alpha value.</p><p>Then we are going to enable and disable the depth buffer to get the desired effect. When we disable the depth test, it use the draw order. Here is the drawing code:</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">// Draw the crate
GL.Enable(EnableCap.DepthTest);
_renderer.Begin(_camera.GetProjection());
_renderer.Draw(_crate, new  Vector2(64, 64), Color4.White, 64f / 720f);
_renderer.End();

// Draw the tank's silhouette
GL.Disable(EnableCap.DepthTest);
_renderer.Begin(_camera.GetProjection(), _silhouetteShaderProgram);
_renderer.Draw(_tank, _tankPosition, Color4.Red);
_renderer.End();

// Draw the tank
GL.Enable(EnableCap.DepthTest);
_renderer.Begin(_camera.GetProjection());
_renderer.Draw(_tank, _tankPosition, Color4.White, _tankPosition.Y / 720f);
_renderer.End();</code></pre>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.vfrz.fr/content/images/2021/02/StepByStepDrawing1-1.png" class="kg-image"><figcaption>Tank is behind the crate: rendering step by step, using <a href="https://renderdoc.org/">RenderDoc</a></figcaption></figure><p>The first line is the color buffer and the second is the depth buffer. So this is what happens:</p><ol><li>We enable depth test.<br>We draw the crate with a depth value of 0.5 (example value), so we can see the crate on the depth buffer.</li><li>We disable depth test.<br>We draw the silhouette using the custom shader, the depth buffer doesn't change.</li><li>We enable depth test.<br>We draw the tank with a depth value of 0.4, because of the depth test the part behind the crate isn't visible and so we can still see the silhouette.</li></ol><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.vfrz.fr/content/images/2021/02/StepByStepDrawing2.png" class="kg-image"><figcaption>Tank is in front of the crate: rendering step by step, using <a href="https://renderdoc.org/">RenderDoc</a></figcaption></figure><p>As previously, first line is the color buffer and second is the depth buffer:</p><ol><li>We enable depth test.<br>We draw the crate with a depth value of 0.5 (example value), so we can see the crate on the depth buffer.</li><li>We disable depth test.<br>We draw the silhouette using the custom shader, the depth buffer doesn't change.</li><li>We enable depth test.<br>We draw the tank with a depth value of 0.6, because of the depth test the tank is drawn over the crate and because of the draw order, in front of the silhouette too.</li></ol><p>We could stop here, but again if you have sharp eyes (and a red silhouette) you could see something disturbing. There is a red outline all over the tank, because we are always rendering the silhouette and the tank has some semi-transparent pixels on the edges. If your textures doesn't have semi-transparent pixels, you won't have this problem, but I'll still show you a way to fix it.</p><h2 id="draw-the-silhouette-only-where-needed">Draw the silhouette only where needed</h2><p>This part is like a bonus and should be applied only if your texture has semi-transparent pixels because we will be doing some extra computation (and every optimization is welcome in game development).</p><p>The goal here is to draw the silhouette only where the tank is on the crate. To do this we will be using the <a href="https://www.khronos.org/opengl/wiki/Stencil_Test">stencil buffer</a> as follows:</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">GL.Enable(EnableCap.DepthTest);
GL.DepthFunc(DepthFunction.Less);
GL.Enable(EnableCap.StencilTest);
GL.StencilFunc(StencilFunction.Always, 1, 0xFFFFFF);
GL.StencilOp(StencilOp.Keep, StencilOp.Keep, StencilOp.Replace);
_renderer.Begin(_camera.GetProjection());
_renderer.Draw(_crate, new Vector2(64, 64), Color4.White, 64f / 720f);
_renderer.End();

GL.StencilFunc(StencilFunction.Equal, 1, 0xFFFFFF);
GL.StencilOp(StencilOp.Keep, StencilOp.Keep, StencilOp.Incr);
GL.ColorMask(false, false, false, false);
GL.DepthMask(false);
_renderer.Begin(_camera.GetProjection());
_renderer.Draw(_tank, _tankPosition, Color4.White, _tankPosition.Y / 720f);
_renderer.End();

GL.ColorMask(true, true, true, true);
GL.DepthMask(true);
GL.Disable(EnableCap.DepthTest);
_renderer.Begin(_camera.GetProjection(), _silhouetteShaderProgram);
_renderer.Draw(_tank, _tankPosition, Color4.Red);
_renderer.End();

GL.Enable(EnableCap.DepthTest);
GL.Disable(EnableCap.StencilTest);
_renderer.Begin(_camera.GetProjection());
_renderer.Draw(_tank, _tankPosition, Color4.White, _tankPosition.Y / 720f);
_renderer.End();
</code></pre>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.vfrz.fr/content/images/2021/02/StepByStepDrawing3-1.png" class="kg-image"><figcaption>Tank is behind the crate: rendering step by step, using <a href="https://renderdoc.org/">RenderDoc</a></figcaption></figure><p>Now we have a third line corresponding to the stencil buffer. Here is what's happening:</p><ol><li>We enable depth test and stencil test, we set the stencil function to "<strong>always</strong>" with value of <strong>1 </strong>and a default mask (0xFFFFFF), we set the stencil operations to keep/keep/replace. Basically it means that for each drawn pixel we will put the value 1 in the stencil buffer. We draw the crate, we can see both the depth buffer and stencil buffer have changed.</li><li>We set the stencil function to "<strong>equal</strong>"<strong> </strong>with value of 1 and same default mask, we set the stencil operations to keep/keep/incr.  It means that we will increment the stencil buffer by 1 wherever the current value is already 1 (so where the crate has been previously drawn). We set the color mask to false/false/false/false to disable color buffer writing and the depth mask to false to disable depth buffer writing.<br>We draw the tank and no buffer is affected because the tank is behind the crate.</li><li>We set back the color mask to true/true/true/true and the depth mask to true. We disable the depth test.<br>We draw the silhouette and because if the stencil test, it is drawn only where the stencil buffer has a value of 1 which correspond of where the crate has been drawn.</li><li>We enable depth test again and disable stencil test.<br>We draw the final tank and it looks good! No red outline is visible anymore.</li></ol><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.vfrz.fr/content/images/2021/02/StepByStepDrawing4.png" class="kg-image"><figcaption>Tank is in front of the crate: rendering step by step, using <a href="https://renderdoc.org/">RenderDoc</a></figcaption></figure><p>The process is pretty much the same when the tank is in the front except the stencil buffer on the second step is incremented by 1 where the tank is drawn. So the silhouette isn't drawn at all during step 3.</p><p>Hope it has been useful, feel free to comment if you have any question or suggestion.</p>]]></content:encoded></item><item><title><![CDATA[Blazor - Redirect non-authenticated user to login page]]></title><description><![CDATA[Learn how to redirect a non-authenticated user to a login page with Microsoft's Blazor framework.]]></description><link>https://blog.vfrz.fr/blazor-redirect-non-authenticated-user/</link><guid isPermaLink="false">5dcaf360601b110001d4faf7</guid><category><![CDATA[C#]]></category><category><![CDATA[.NET Core]]></category><category><![CDATA[Blazor]]></category><dc:creator><![CDATA[Valentin Fritz]]></dc:creator><pubDate>Fri, 29 Nov 2019 10:00:00 GMT</pubDate><content:encoded><![CDATA[<p><a href="https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor"><strong>Blazor</strong> </a>is a fantastic <strong>free </strong>and <strong>open-source framework</strong> for creating client web application / SPA using <strong>C#</strong>. There is two versions / hosting modes currently available : client-side and server-side. I won't make a complete Blazor presentation in this post, so you should check the official documentation for more information.</p><p>The goal is to redirect a non-authenticated user to a login page automatically. The code works for both client-side and server-side. For this we are going to create a component that will check the authentication state on each (protected) page.</p><p>First step is to create the component. I named it <strong>RedirectToLogin.razor</strong>, below is the code:</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">@inject NavigationManager Navigation

@code {

    [CascadingParameter]
    private Task&lt;AuthenticationState&gt; AuthenticationStateTask { get; set; }

    protected override async Task OnInitializedAsync()
    {
        var authenticationState = await AuthenticationStateTask;

        if (authenticationState?.User?.Identity is null || !authenticationState.User.Identity.IsAuthenticated)
        {
            var returnUrl = Navigation.ToBaseRelativePath(Navigation.Uri);

            if (string.IsNullOrWhiteSpace(returnUrl))
                Navigation.NavigateTo(&quot;auth/login&quot;, true);
            else
                Navigation.NavigateTo($&quot;auth/login?returnUrl={returnUrl}&quot;, true);
        }
    }

}
</code></pre>
<!--kg-card-end: markdown--><p>The component uses the <strong>AuthenticationState</strong>, so you'll need to implement your own <strong>AuthenticationStateProvider </strong>or use an existing one. The code <strong>only checks the user authentication</strong> (not any authorization check). If the user is not authenticated, it redirects him to the "auth/login" page or "auth/login?returnUrl=xxx" if the user tried to access a specific page.</p><p>The next step is to modify the <strong>App.razor </strong>file in order to load the component when the user isn't authorized:</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">&lt;Router AppAssembly=&quot;@typeof(Program).Assembly&quot;&gt;
    &lt;Found Context=&quot;routeData&quot;&gt;
        &lt;AuthorizeRouteView RouteData=&quot;routeData&quot; DefaultLayout=&quot;@typeof(MainLayout)&quot;&gt;
            &lt;NotAuthorized&gt;
                &lt;RedirectToLogin&gt;&lt;/RedirectToLogin&gt;
            &lt;/NotAuthorized&gt;
        &lt;/AuthorizeRouteView&gt;
    &lt;/Found&gt;
    &lt;NotFound&gt;
        &lt;CascadingAuthenticationState&gt;
            &lt;LayoutView Layout=&quot;@typeof(MainLayout)&quot;&gt;
                &lt;p&gt;Sorry, there's nothing at this address.&lt;/p&gt;
            &lt;/LayoutView&gt;
        &lt;/CascadingAuthenticationState&gt;
    &lt;/NotFound&gt;
&lt;/Router&gt;
</code></pre>
<!--kg-card-end: markdown--><p>Now if you try to access a protected page (using the Authorize attribute) without being authenticated, you should be redirected to the login page automatically!</p><p><em>P.S. This actual implementation doesn't prevent open redirect attacks, I suggest you to visit this documentation to learn about it: <a href="https://docs.microsoft.com/en-us/aspnet/core/security/preventing-open-redirects">https://docs.microsoft.com/en-us/aspnet/core/security/preventing-open-redirects</a></em></p><p>Hope you guys enjoyed it! Please comment if you have any question or suggestion.</p>]]></content:encoded></item><item><title><![CDATA[Entity Framework Core 3.0 - Put DbContext, entities and migrations in a separated library project]]></title><description><![CDATA[In this post, I will show you how to put your DbContext, entities and migrations in a separated library project when using Entity Framework Core 3.0]]></description><link>https://blog.vfrz.fr/ef-core-dbcontext-entities-migrations-separated-project/</link><guid isPermaLink="false">5dc000dc601b110001d4f990</guid><category><![CDATA[Entity Framework Core]]></category><category><![CDATA[C#]]></category><category><![CDATA[.NET Core]]></category><dc:creator><![CDATA[Valentin Fritz]]></dc:creator><pubDate>Thu, 14 Nov 2019 15:00:00 GMT</pubDate><content:encoded><![CDATA[<p>.NET Core and more generally .NET applications source code is split between multiple <strong>projects </strong>(.csproj file) which can be grouped into what is called a <strong>solution </strong>(.sln file).<strong> </strong>When you are creating a small application, you can put everything in only one project, but I would rarely recommend it. As soon as the application become bigger, you will need to separate different parts of it which is a design principle known as <a href="https://en.wikipedia.org/wiki/Separation_of_concerns">separation of concerns</a>. </p><p>A typical application's solution would look like this:</p><figure class="kg-card kg-image-card"><img src="https://blog.vfrz.fr/content/images/2019/11/image-1.png" class="kg-image"></figure><p>I will not go in details about the whole structuring of a .NET Core application in this post, but only show you how to separate all EF Core/database related stuff (DbContext, entities, migrations) from your main project and put it into a library project.</p><p><strong>Let's begin!</strong></p><p>Before doing anything on your project, I recommend you to install the EF Core global tools by typing this command:</p><p><code>dotnet tool install --global dotnet-ef</code></p><p>Having the dotnet-ef tool installed as global will enables you to use it wherever you want without adding it to a specific project. This tool allows you to manage migrations, your database and more.</p><p>The first step is to create the new library project and add it to your solution. Either by using an IDE or typing the following commands: (no project naming convention is required but I usually use "<em>ApplicationName</em>.Database"):</p><p><code>dotnet new classlib -n MyApplication.Database -f netcoreapp3.0</code> //Create the project</p><p><code>dotnet sln add MyApplication.Database</code> //Add the project to the solution</p><p>Once the project is created and added to the solution, you can import it to whatever projects needing it.</p><p>Next step is to import the required NuGet packages: <code><a href="https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Design/">Microsoft.EntityFrameworkCore.Design</a></code> and <code><a href="https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Abstractions/">Microsoft.EntityFrameworkCore.Abstractions</a></code></p><p>If you already have a DbContext, some migrations and entities in another project, you can move them to the new project and fix namespace/missing libraries issues (<a href="https://www.jetbrains.com/resharper/">ReSharper</a> or <a href="https://www.jetbrains.com/rider/">Rider</a> is highly recommended here). Else you can create one simply by adding a new class inheriting DbContext and adding a constructor like this:</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">public class MyApplicationDbContext : DbContext
{
    public MyApplicationDbContext(DbContextOptions&lt;MyApplicationDbContext&gt; options) : base(options)
    {
    }
}
</code></pre>
<!--kg-card-end: markdown--><p>Do not forget to add the DbContext to your service collection if needed by calling <code>serviceCollection.AddDbContext&lt;MyApplicationDbContext&gt;</code> in your startup class.</p><p>Then you can create your different entities and add them to your context as usual. Your DbContext might end up looking like this:</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">public class MyApplicationDbContext : DbContext
{
    public DbSet&lt;Post&gt; Posts { get; set; }
    
    public DbSet&lt;Comment&gt; Comments { get; set; }
    
    public DbSet&lt;Account&gt; Accounts { get; set; }
    
    public MyApplicationDbContext(DbContextOptions&lt;MyApplicationDbContext&gt; options) : base(options)
    {
    }
}
</code></pre>
<!--kg-card-end: markdown--><p>Right now, if you try to generate a migration, it should throws an exception saying "Unable to create an object of type 'MyApplicationDbContext'..." This is totally normal, all you need to do is to tell EF Core how to create a DbContext.</p><p>Actually, there is two ways to do it: specify the "startup" project when using dotnet ef commands or create a <code>IDesignTimeDbContextFactory&lt;&gt;</code> in the database project.</p><p>To specify your application startup project when using dotnet ef commands, you simply need to add <code>-s <em>pathToStartupProject</em></code><em> </em>to your command. Example to generate the initial/first migration and then update the database:</p><p><code>dotnet ef migrations add InitialMigration -p <em>pathToDatabaseProject </em>-s <em>pathToStartupProject</em></code></p><p><code>dotnet ef database update -p <em>pathToDatabaseProject</em></code></p><p><strong>When specifying a startup project, it will use the connection string set in the startup project.</strong></p><p>If you don't want to specify the startup project on every command or you'd like to use a database specifically for design time, the other option is to create a custom <code>IDesignTimeDbContextFactory</code> in your "database project" like the following:</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">public class MyApplicationDbContextFactory : IDesignTimeDbContextFactory&lt;MyApplicationDbContext&gt;
{
    public MyApplicationDbContext CreateDbContext(string[] args)
    {
        var optionsBuilder = new DbContextOptionsBuilder&lt;MyApplicationDbContext&gt;()
            .UseSqlite(&quot;Data Source=app.db&quot;);
        return new MyApplicationDbContext(optionsBuilder.Options);
    }
}
</code></pre>
<!--kg-card-end: markdown--><p>This example uses the official SQLite provider, but you can use whatever you want <em>(it still needs to be the same provider used in your startup project).</em></p><p>Now you can create migrations and update database without specifying the startup project: </p><p><code>dotnet ef migrations add InitialMigration -p <em>pathToDatabaseProject</em></code></p><p><code>dotnet ef database update -p <em>pathToDatabaseProject</em></code></p><p>If you open your terminal directly in the database project directory, you don't even need to set the project parameter (-p).</p><p>Hope you guys enjoyed it! Please comment if you have any question or suggestion.</p><h3 id="what-s-next">What's next?</h3><p>You can put entity classes in another project too, if you need even more separation.</p>]]></content:encoded></item><item><title><![CDATA[Use Quartz.Net for background and recurring jobs within an ASP.NET Core 3.0 application]]></title><description><![CDATA[In this post, I will show you how to use the fantastic Quartz.Net scheduler library within a ASP.NET Core 3.0 application. It allows to do background, delayed and recurring jobs with ease.]]></description><link>https://blog.vfrz.fr/quartz-asp-net-core-3-0/</link><guid isPermaLink="false">5db74d3c0c001c0001feac72</guid><category><![CDATA[ASP.NET Core]]></category><category><![CDATA[C#]]></category><category><![CDATA[.NET Core]]></category><category><![CDATA[Quartz.Net]]></category><dc:creator><![CDATA[Valentin Fritz]]></dc:creator><pubDate>Wed, 30 Oct 2019 19:00:00 GMT</pubDate><content:encoded><![CDATA[<p>Doing background processing in a computer software is something very common. The idea behind it is to be able to process data without blocking or freezing the user interface or even trigger a function at a regular interval.</p><p>In this post, I'll show you how to implement background jobs, recurring jobs and delayed jobs within an ASP.NET Core 3.0 application using <a href="https://www.quartz-scheduler.net/">Quartz.Net</a>. <em>(All the code should work with ASP.NET Core 2 too)</em> When it comes to background job in .NET, there is two well-known solutions: <a href="https://www.hangfire.io/">Hangfire</a> and Quartz.Net. I would say Hangfire is the most modern and the easier one for basic usage. It is a turnkey solution but whenever you want to do more complex stuff, it becomes tricky or impossible. This is why I've chosen to use Quartz.Net on some of my personal projects and why I'm writing this article.</p><p>For the following, I am assuming you know the basics of C# and ASP.NET Core application development. If it's not the case and you're interested in building awesome web applications using one of the <a href="https://insights.stackoverflow.com/survey/2019#most-loved-dreaded-and-wanted">most loved programming languages</a>, I encourage you to watch the official tutorials by Microsoft right <a href="https://dotnet.microsoft.com/learn/aspnet/hello-world-tutorial/intro">here</a>.</p><h3 id="let-s-start-">Let's start!</h3><p>The first thing you'll need is an ASP.NET Core application, if you don't have one already, you can create one using the command line or your favorite IDE. For this post, I've created a Web API project without authentication using .NET Core 3.0.</p><p>Next you'll need to add a package reference to <a href="https://www.nuget.org/packages/Quartz/">Quartz.Net NuGet package</a> by typing the following command:</p><p><code>dotnet add package Quartz</code></p><p>Then we create and inject a <code>IScheduler</code> as a singleton in our service collection (Startup.cs):</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    var scheduler = StdSchedulerFactory.GetDefaultScheduler().GetAwaiter().GetResult();
    services.AddSingleton(scheduler);
}
</code></pre>
<!--kg-card-end: markdown--><p>The role of the scheduler is to poll and fire jobs. It is possible to use multiple schedulers in one application, but for simplicity, we will only use one <em>(singleton)</em> in this example.</p><p>Next step is to create a new class implementing <code><a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.ihostedservice">IHostingService</a></code> which will start and stop the scheduler for our application:</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">public class QuartzHostedService : IHostedService
{
    private readonly IScheduler _scheduler;

    public QuartzHostedService(IScheduler scheduler)
    {
        _scheduler = scheduler;
    }

    public Task StartAsync(CancellationToken ct)
    {
         return _scheduler.Start(ct);
    }

    public Task StopAsync(CancellationToken ct)
    {
        return _scheduler.Shutdown(ct);
    }
}
</code></pre>
<!--kg-card-end: markdown--><p>Do not forget to register the hosted service in the services collection as follows:</p><!--kg-card-begin: markdown--><p><code>services.AddHostedService&lt;QuartzHostedService&gt;();</code></p>
<!--kg-card-end: markdown--><p>Then we create a job class implementing the <code>IJob</code> interface from Quartz.Net:</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">public class TestJob : IJob
{
    public Task Execute(IJobExecutionContext context)
    {
        Console.WriteLine(&quot;Hello from a job!&quot;);
        return Task.CompletedTask;
    }
}
</code></pre>
<!--kg-card-end: markdown--><p>In order to start a <code>TestJob</code> on a fire-and-forget mode from a service, you need to create a <code>IJobDetail</code> using the <code>JobBuilder</code>, create a <code>ITrigger</code> using the <code>TriggerBuilder</code> and finally call the <code>IScheduler.ScheduleJob()</code> method:</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">public Task StartTestJob()
{
    var jobDetails = JobBuilder
        .CreateForAsync&lt;TestJob&gt;()
        .WithIdentity(&quot;MyJobName&quot;)
        .WithDescription(&quot;My job description&quot;)
        .Build();
        
    var trigger = TriggerBuilder
        .Create()
        .StartNow()
        .Build();

    return _scheduler.ScheduleJob(jobDetails, trigger);
}
</code></pre>
<!--kg-card-end: markdown--><p><em>(Be careful: a lot of methods inside Quartz.Net are asynchronous but does not have the 'Async' suffix in their names)</em></p><p>The <code>JobBuilder</code> static class is used to wrap a job in a <code>IJobDetail</code> containing the job type and some metadata. In the other hand, the <code>TriggerBuilder</code> is used to create a trigger for jobs. In our example it is a simple trigger which will fire the job once directly after registering it to the scheduler.</p><p>From now, whenever you call the method <code>StartTestJob()</code>, you should see a <code>Hello from a job!</code> message in the console. You can call the service method from a simple HTTP GET controller method for testing purpose.</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">[HttpGet]
public void Get()
{
    _testService.StartTestJob();
}
</code></pre>
<!--kg-card-end: markdown--><h3 id="dependency-injection">Dependency injection</h3><p>Next big step is to use <strong>dependency injection</strong> within our jobs. The solution I'll propose is pretty simple and straightforward. For this we are going to create a custom <code>IJobFactory</code> like this:</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">public class MyJobFactory : IJobFactory
{
    private readonly IServiceProvider _serviceProvider;

    public MyJobFactory(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }
    
    public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
    {
        return ActivatorUtilities.CreateInstance(_serviceProvider, bundle.JobDetail.JobType) as IJob;
    }
    
    public void ReturnJob(IJob job)
    {
        if (job is IDisposable disposableJob)
            disposableJob.Dispose();
    }
}
</code></pre>
<!--kg-card-end: markdown--><p>And then add it to the scheduler:</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">var scheduler = StdSchedulerFactory.GetDefaultScheduler().GetAwaiter().GetResult();
scheduler.JobFactory = new MyJobFactory(services.BuildServiceProvider());
services.AddSingleton(scheduler);
</code></pre>
<!--kg-card-end: markdown--><p>As of now we can inject services via the job class' constructor like this:</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">public class TestJob : IJob
{
    private readonly ILogger&lt;TestJob&gt; _logger;
    
    public TestJob(ILogger&lt;TestJob&gt; logger)
    {
        _logger = logger;
    }
    
    public Task Execute(IJobExecutionContext context)
    {
        _logger.LogInformation(&quot;Hello from job logger!&quot;);
        return Task.CompletedTask;
    }
}
</code></pre>
<!--kg-card-end: markdown--><p>Then you should see the hello world message coming from the logger in the console:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.vfrz.fr/content/images/2019/10/image.png" class="kg-image"></figure><h3 id="recurring-and-or-delayed-job">Recurring and/or delayed job</h3><p>To <strong>delay </strong>a job, you simply need to replace <code>.StartNow()</code> on the trigger builder with <code>.StartAt(dateTimeOffset)</code> . Example for starting a job after 15 minutes:</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">var trigger = TriggerBuilder
    .Create()
    .StartAt(DateTimeOffset.Now.AddMinutes(15))
    .Build();
</code></pre>
<!--kg-card-end: markdown--><p>There is two ways to create <strong>recurring jobs</strong>: using <a href="https://en.wikipedia.org/wiki/Cron">CRON</a> expressions or the Quartz' <code>SimpleScheduleBuilder</code> with a fluent flow.</p><p>Example of a CRON trigger to fire a job every 5 minutes:</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">var trigger = TriggerBuilder
    .Create()
    .StartNow()
    .WithCronSchedule(&quot;*/5 * * * *&quot;)
    .Build();
</code></pre>
<!--kg-card-end: markdown--><p>Same trigger but using the <code>SimpleScheduleBuilder</code> :</p><!--kg-card-begin: markdown--><pre><code class="language-csharp">var trigger = TriggerBuilder
    .Create()
    .StartNow()
    .WithSimpleSchedule(builder =&gt;
        builder.WithIntervalInMinutes(5)
            .RepeatForever()
    )
    .Build();
</code></pre>
<!--kg-card-end: markdown--><p><em>To be precise, these two triggers will not work exactly the same. If you register the job at 6:03, the first trigger using CRON will fire the job "at every 5 minutes" like 6:05, 6:10, 6:15... But the second one will fire the job every 5 minutes starting from now, so 6:03, 6:08, 6:13... I invite you to read the </em><a href="https://en.wikipedia.org/wiki/Cron"><em>Wikipedia page of CRON</em></a><em> to learn more about it.</em></p><p>Hope you guys enjoyed it! Please comment if you have any question or suggestion.</p><h3 id="what-to-do-next">What to do next?</h3><p>You could try to save jobs in a database, so you won't lose them when the application stops. Quartz.Net has a <a href="https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/job-stores.html">persistence feature</a>, so you can store your jobs in a database easily with multiple provider available (SqlServer, PostgreSQL, SQLite and more).</p><p>One another idea would be to make your jobs resilient (retry on error).</p>]]></content:encoded></item></channel></rss>