Business Extensions – Reprise

Business Extensions – Reprise

Last time I did a quick walkthrough of a server-side Blazor application that uses the CG.Business library to dynamically locate and load a repository assembly, at runtime.

This time I’ll extend that walkthrough the deal with an issue that so often accompanies the use of database technologies – namely, the need for startup logic. As in, creating and/or dropping databases, applying migrations, seeding initial data, etc.

Let’s do that now.

Usually, if we follow the pattern taken by ASP.NET and/or Blazor, we would put pipeline related logic into the Startup.Configure method. I envision something like this:

public class Startup
{
    public void Configure(
        IApplicationBuilder app, 
        IWebHostEnvironment env
        )
    {
         // TODO : put pipeline related code here.   
    }
}

By the way, when I say “pipeline”, I really mean any kind of startup logic – not just ASP.NET pipeline stuff.

So, if we were adding an EFCORE repository to a website, for instance, then the Startup.Configure method seems like the perfect place to take care of any EFCORE startup, right? I imagine something like this:

public class Startup
{
    public void Configure(
        IApplicationBuilder app, 
        IWebHostEnvironment env
        )
    {
         var ctx = app.ApplicationServices.GetRequiredService<MyDbCtx>();
         ctx.Database.ApplyMigrations();
    }
}

That’s fine if there’s no abstraction between the presentation and data access layers. But, what if we’ve deliberately created a design seam using the CG.Business library? In that case, we can’t put EFCORE code directly into the Startup.Configure method – it would destroy everything we’ve achieved.

Luckily, CG.Business has a way to extend the startup mechanism represented by the Startup.Configure method in exactly the same way it extends the registration mechanism represented by the Startup.ConfigureServices method.

From here on in, I’ll assume you’ve read my last post and have access to the Visual Studio project that we created in that article. If you haven’t read that post, please stop and do that now.

To start, we simply have to write two extension methods. The first will go into the ClassLibrary2 library. Create a file, in ClassLibrary2, called ApplicationBuilderExtensions.cs, and make it look like this:

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Hosting;

namespace ClassLibrary2
{
    public static partial class ApplicationBuildExtensions
    {
        public static IApplicationBuilder UseMyRepositories(
            this IApplicationBuilder applicationBuilder,
            IHostEnvironment hostEvironment
            )
        {
            // Here is where we put any kind of database specific startup
            // code.

            return applicationBuilder;
        }
    }
}

I haven’t actually added any EFCORE code here because my walkthrough repository library doesn’t really need EFCORE. Instead, I added a helpful comment. See? Isn’t that comment helpful?

The second extension method goes into the ClassLibrary1 library. Create a file, this time in ClassLibrary1, called ApplicationBuilderExtensions.cs, and make it look like this

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;

namespace ClassLibrary1
{
    public static partial class ApplicationBuildExtensions
    {
        public static IApplicationBuilder UseMyLibrary(
            this IApplicationBuilder applicationBuilder,
            IHostEnvironment hostEvironment,
            IConfiguration configuration
            )
        {
            applicationBuilder.UseRepositories(
                   hostEvironment,
                   configuration.GetSection("Repositories")
                   );

            return applicationBuilder;
        }
    }
}

This is where we call the UseRepositories method, which is part of the CG.Business library. That method is where CG.Business uses the configuration section we already added, in the last post, to our appSettings.json file, in the BlazorApp1 project. By reading that configuration information, the UseRepositories method has all the information it needs to dynamically load our ClassLibrary2 assembly and locate our UseMyRepositories extension method.

All that’s left to do now is call our UseMyLibrary extension method, from the Startup.Configure method, in the BlazorApp1 project. Here is what that should look like:

using ClassLibrary1;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;

namespace BlazorApp1
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            // Existing code removed for clarity ...

            app.UseMyLibrary(
                env,
                Configuration.GetSection("BlazorApp1")
                );
        }
    }
}

And that’s how we can execute database specific startup code, from our presentation layer, without destroying any of the abstractions we’ve created to isolate the various bits of our code.

So that’s it. Now, thanks to CG.Business, a few extension methods, and a bit of JSON, we have an application that literally knows nothing about it’s repository until runtime – as it should be.

The source code for the associated project is located HERE.

By the way, this process, in CG.Business, also works almost exactly the same way for strategies.

Photo by Chang Duong on Unsplash