Marten, the Generic Host Builder in .Net Core, and why this could be the golden age for OSS in .Net

Before I dive into some newly improved mechanics for quickly integrating Marten into just about any possible kind of .Net 5+ application, I want to take a trip through yesteryear and appreciate just how much better the .Net platform is today than it’s every been. If you wanna skip down to the positive part of the post, just look for sub heading “The better future for .Net is already here!.”

I’ve been a .Net developer since the very ugly beginning when we were all just glad to be off VB6/COM and onto a “modern” programming language that felt like a better Java but not yet completely horrified at how disastrously bad WebForms was going to turn out to be or disappointed at how poorly all our new .Net stuff worked out when we tried to adopt Agile development practices like Test Driven Development.

I’ve also been very deeply invested in developing open source tools and frameworks on the .Net framework, starting with StructureMap, the very first production capable Inversion of Control library on .Net that first went into a production system in the summer of 2004.

As the maintainer of StructureMap throughout, I had a front row seat to the absolute explosion of adapter libraries for seemingly every possible permutation of framework, IoC tool, and logging library. And this happened because every framework of any note in the .Net world had a completely different way to abstract away the actual IoC container or allowed you to use different logging tools through the specific framework’s special adapter mechanisms.

I’m also old enough to remember when every tool took a direct dependency on log4net around the time they lost their public strong naming key and broke compatibility. That was loads of fun.

There also wasn’t a standard way to deal with application lifecycle events across different types of .Net applications or application frameworks (ASP.Net’s Global.ASAX being an exception I guess). Every framework at that time had a different way of being bootstrapped at application start up time, so developers had to do a lot of plumbing code as they pieced all this junk together with the myriad adapter libraries.

To recap the “old”, pre-Core .Net world:

  • Application framework developers had to create their own IoC and logging abstractions and support a plethora of adapter libraries as well as possibly having to create all new application lifecycle management facilities as part of their framework
  • Library developers had to support users through their usage of problematic framework integrations where the library in question was often used in non-optimal ways (my personal pet peeve, and I edited this post before publishing to eliminate some harsh criticism of certain well known .Net application frameworks).
  • Developers of actual .Net applications were rightfully intimidated by the complexity necessary to integrate open source tools, or simply struggled to do so as there was no standardization of how all those tools went about their integration into .Net applications.

I would argue that the situation in .Net as it was before .Net Core seriously impeded the development and adoption of open source tooling in our community — which also had the effect of slowing down the rate of innovation throughout the .Net community.

You might notice that I used the past tense quite a bit in the previous paragraphs, and that’s because I think…

The better future for .Net is already here!.

It’s hard to overstate this, but the generic host builder (IHostBuilder and IHost) in .Net Core / .Net 5 and the related is hugely advantageous for the .Net ecosystem. I tried to explain why I think this a bit in a recent episode of .Net Rocks, but it’s probably easier to demonstrate this by integrating Marten into a new .Net 5 web api project.

Assuming that you’re already familiar with the usage of the Startup class for configuring ASP.Net Core applications, I’m going to add Marten to the new service with the following code:

public void ConfigureServices(IServiceCollection services)
{
    // This is the absolute, simplest way to integrate Marten into your
    // .Net Core application with Marten's default configuration
    services.AddMarten(options =>
    {
        // Establish the connection string to your Marten database
        options.Connection(Configuration.GetConnectionString("Marten"));

        // If we're running in development mode, let Marten just take care
        // of all necessary schema building and patching behind the scenes
        if (Environment.IsDevelopment())
        {
            options.AutoCreateSchemaObjects = AutoCreate.All;
        }

        if (Environment.IsProduction())
        {
            // This is a new V4 feature I'll blog about soon...
            options.GeneratedCodeMode = TypeLoadMode.LoadFromPreBuiltAssembly;
        }

        // Turn on the async projection daemon in only one node
        options.Projections.AsyncMode = DaemonMode.HotCold;

        // Configure other elements of the event store or document types...
    });
}

Even if you’re a developer who is completely unfamiliar with Marten itself, the integration through AddMarten() follows the recent .Net idiom and would be conceptually similar to many other tools you probably already use. That by itself is a very good thing as there’s less novelty for new Marten developers (there is of course matching UseMarten() signatures that hang off of IHostBuilder that is useful for other kinds of .Net projects)

I purposely used a few bells and whistles in Marten to demonstrate more capabilities of the generic host in .Net Core. To go into more detail, the code above:

  • Registers all the necessary Marten services with the proper service lifecycles in the underlying system container. And that works for any IoC container that supports the standard DI abstractions in the Microsoft.Extensions.DependencyInjection namespace. There isn’t any kind of Marten.Lamar or Marten.DryIoC or Marten.WhateverIoCToolYouHappenToLike adapter libraries.
  • I’ve also turned on Marten’s asynchronous projection process which will run as a long running task in the application by utilizing the standard .Net Core IHostedService mechanism whose lifecycle will be completely managed by the generic host in .Net.
  • We were able to do a little bit of environment specific configuration to let Marten do all necessary schema management automatically at development time, but not allow Marten to change database schemas in production. I also opted into a new Marten V4 optimization for production cold starts when the application is running in “Production” mode. That’s easy to do now using .Net’s standard IHostEnvironment mechanism at bootstrapping time.
  • It’s not obvious from that code, but starting in V4.0, Marten will happily log to the standard ILogger interface from .Net Core. Marten itself doesn’t care where or how you actually capture log information, but because .Net itself now specifies a single set of logging abstractions to rule them all, Marten can happily log to any commonly used logging mechanism (NLog, Serilog, the console, Azure logging, whatever) without any other kind of adapter.
  • And oh yeah, I had to add the connection string to a Postgresql database from .Net Core’s IConfiguration interface, which itself could be pulling information any number of ways, but our code doesn’t have to care about anything else but that one standard interface.

As part of the team behind the Marten, I feel like our jobs are easier now than it was just prior to .Net Core and the generic host builder because now there are:

  • .Net standard abstractions for logging, configuration, and IoC service registration
  • The IHostedService mechanism is an easy, standard way in .Net to run background processes or even just to hook into application start up and tear down events with custom code
  • The IServiceCollection.Add[Tool Name]() and IHostBuilder.Use[Tool Name]() idioms provided us a way to integrate Marten into basically any kind of .Net Core / .Net 5/6 application through a mechanism that is likely to feel familiar to experienced .Net developers — which hopefully reduces the barrier to adoption for Marten

As some of you reading this may know, I was very heavily involved in building an alternative web development framework for .Net called FubuMVC well before .Net Core came on to the scene. When we built FubuMVC, we had to create from scratch lot of the exact same functionality that’s in the generic host today to manage application lifecycle events, bootstrapping the application’s IoC container through its own IoC abstractions, and hooks for extension with other .Net libraries. I wouldn’t say that was the primary reason that FubuMVC failed as a project, but it certainly didn’t help.

Flash forward to today, and I’ve worked off and on on a complete replacement for FubuMVC on .Net Core called Jasper for asynchronous messaging and an alternative for HTTP services. That project isn’t going anywhere at the moment, but Jasper’s internals are much, much smaller than FubuMVC’s were for similar functionality because Jasper can rely on the functionality and standard abstractions of the generic host in .Net Core. That makes tools like Jasper much easier for .Net developers to write.

Unsurprisingly, there are plenty of design and implementation decisions that Microsoft made with the generic host builder that I don’t agree with. I can point to a few things that I believe FubuMVC did much better for composition a decade ago than ASP.Net Core does today (and vice versa too of course). I deeply resent how the ASP.Net Core team wrote the IoC compliance rules that were rammed down the throats of all us IoC library maintainers. You can also point out that there are cultural problems within the .Net community in regards to OSS and that Microsoft itself severely retards open source innovation in .Net by stomping on community projects at will.

All of that may be true, but from a strictly technical perspective, I think that it is now much easier for the .Net community to build and adopt innovative open source tools than it ever has been. I largely credit the work that’s been done with the generic host, the common idioms for tool integration, and the standard interfaces that came in the .Net Core wave for potentially opening up a new golden age for .Net OSS development.

Leave a comment