The Marten Webinar is Online, and Answering Some Questions

JetBrains was gracious enough to let me record an introductory webinar last week on the Marten project that lets .Net developers successfully treat Postgresql as a fully ACID-compliant, document database and event store. The recording posted this morning and there’s a link to it right below:

Questions from the Webinar

These are some of the leftover questions we weren’t able to get to during the webinar:

Is there an ability to do faceted searches in Marten?

I don’t know why, but I just couldn’t make out the word “faceted” during the talk and this one slipped through.

In Marten itself, no, but we’re sitting on top of Postgresql that does support faceted searching. In the longer run, we’ve talked about having some easy to use facilities that allow you to either “project” a Marten-persisted document type to a flat database view or to possible write a “read side” table for reporting during document writes. My attitude on these kinds of features is to try to lean on Postgresql wherever possible to try to keep Marten’s already very large feature set and codebase from getting (more) bloated.

Have you any experience running Marten against Citus?

No, but I’d be very curious to see how that goes. I’ve purposely put off any kind of software based sharding with Marten in hopes that Citus just works. Volunteering? 😉

How did you convince your company to build marten from the ground up instead of using existing docdb?

Marten was originally conceived of and executed as a near drop in replacement for our existing document database (RavenDb) that was causing us quite a few production issues. At the time, we theorized that it would be easier to build an in place replacement for RavenDb than to convert a massive, existing project to some completely different database and persistence framework. We were a very OSS-friendly shop at the time, and Marten was actually my then manager’s concept.

Can you extract values from the json in explicit table fields?

Marten can only work against tables that it controls with an expected structure, so no, sorry.

Can we use map-reduce queries like in RavenDB? And is there async index creation, with map-reduce?

Indexes are just Postgresql indexes, even when calculated against a JSON search. We don’t directly support map-reduce, and I don’t actually think we’ll need to in the long run. See the section on faceted search above too.

Will you be posting the code used in the webinar somewhere?

Yep, it’s in GitHub here.

Successfully Running an xUnit Suite in Parallel

TL:DR: Don’t call Task.Wait() in your xUnit tests if you want things running faster and in parallel. In other words, async turtles all the way down. This is a requested post from my buddy Jim Holmes.

In my recent OSS efforts like Marten, Jasper, and Lamar, I have tended to lean much more heavily on top down integration tests than having lots of intermediate and low level unit tests. Putting aside the wisdom of that approach aside for another time, depending so much on integration testing has made the main testing suite in Jasper run too slowly for my comfort as the project has grown.

Like many xUnit users, the second I hit issues with test suites locking up or failing unpredictably, I lazily slap on the directive to prevent parallel test execution like so:

using Xunit;

[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]

As I said, the Jasper suite got too slow for productive, quick twitch development, so I finally broke down and committed enough time to eliminate all the issues in the Jasper code and test suite that was stopping us from running the tests in parallel. It’s a huge, squashed commit, but you can see what I did here.

In a few places, I was using static members to record actions during integration tests as just a mechanically cheap way of asserting correct behavior. I had to move all of that to object instances that were scoped to the test run. Not that big of a deal.

The bigger problem by far was deadlock issues in bootstrapping a Jasper application, which was kind of a problem where there are ~200 tests that each try to bootstrap a Jasper application as part of the test. To optimize the “cold start” time of Jasper, I heavily parallelize startup activities through Task objects. The synchronous version of bootstrapping has to eventually do a couple Task.GetAwaiter().GetResult() calls (once in Jasper, once in Lamar where it uses StructureMap’s old trick of parallelizing the type scanning), and that was prone to deadlocks.

The original pattern of many integration tests looked like this:

[Fact]
public void some_name()
{
    using (var runtime = JasperRuntime.For(_ => { // some configuration}))
        // do stuff and run assertions
    });
}

After re-plumbing the bootstrapping and adding purely asynchronous bootstrapping and teardown methods to both Jasper and the underlying Lamar IoC container, I mostly moved to this pattern instead:

[Fact]
public async Task some_name()
{
    var runtime = await JasperRuntime.ForAsync(_ => {
        // some configuration
    });

    try {
        // do stuff and run assertions
    }
    finally {
        // shutdown the running app w/ 100% async API calls
        await runtime.Shutdown();
    }
}

Long story short, it sucked, but now the tests can happily run in parallel and it made a huge difference in the test suite runtime and I’ve been able to be much more productive.

Lamar v0.9 — Decorators and “MVP”

Lamar, which started life as “BlueMilk,” is the now permanent codename for my next generation IoC tool that is meant to replace StructureMap in new .Net Core projects. I did a little bit of work this morning to add in at least the basic functionality for decorators similar to what StructureMap has supported since 3.0 (but no other kinds of interception for now):

[Fact]
public void decorator_example()
{
    var container = new Container(_ =>
    {
        // This usage adds the WidgetHolder as a decorator
        // on all IWidget registrations
        _.For<IWidget>().DecorateAllWith<WidgetHolder>();
        
        // The AWidget type will be decorated w/ 
        // WidgetHolder when you resolve it from the container
        _.For<IWidget>().Use<AWidget>();
        
        _.For<IThing>().Use<Thing>();
    });

    // Just proving that it actually works;)
    container.GetInstance<IWidget>()
        .ShouldBeOfType<WidgetHolder>()
        .Inner.ShouldBeOfType<AWidget>();
}

This change, and a few other miscellaneous fixes for integration with Jasper, are available on Nuget in Lamar 0.9.0. At this point, I feel like Lamar is already at the feature set I intend to support for a 1.0 release. It’s going to at least go through some load testing at my work over the next month, and still lacks documentation (but most of it behaves identically to StructureMap or the ASP.Net Core DI compliance). All the same,  I’m declaring it ready for real usage if anybody is up for trying it out and hopefully sharing any feedback. It should *mostly* be a drop in replacement for StructureMap if you’re not doing too much weird stuff.

For more information, see some of my previous blog posts about Lamar/BlueMilk:

 

Integrating Jasper into ASP.Net Core

Continuing a blog series on Jasper functionality:

  1. Jasper’s Configuration Story 
  2. Jasper’s Extension Model
  3. Integrating Marten into Jasper Applications
  4. Durable Messaging in Jasper 
  5. Integrating Jasper into ASP.Net Core (this one)
  6. Jasper’s HTTP Transport
  7. Jasper’s “Outbox” Support within ASP.Net Core Applications

There will be some need for completely headless services written with Jasper that rely strictly on TCP connections or yet to come queueing transports, but I expect that most of the systems at work where we’ll use Jasper will be within ASP.Net Core applications.

Moreover, as a nasty lesson learned from my hubristic attempts at creating a freestanding development ecosystem with FubuMVC, Jasper is meant to be merely a good citizen within the greater server side ASP.Net Core ecosystem. In regards to this blog post, that means using as much of the standard Hosting model as possible. For example, Jasper supports the IHostedService model from ASP.Net Core out of the box for long running background services or startup and shutdown actions.

As of Jasper 0.6, I pulled the HTTP support and ASP.Net Core integration into a separate Jasper.Http Nuget. This might feel like the tail wagging the dog, but I really only did this to optimize the core Jasper testing suite because bootstrapping ASP.Net Core on every integration test was slowing the automated build down too much. If I can find a way to optimize or at least parallelize much more of the bootstrapping with the messaging, I will consider merging things back together again later.

When Jasper is integrated into an ASP.Net Core system, it:

  • Adds more service registrations to the application
  • Bootstraps the JasperRuntime object and places that within the container so that the Jasper transports will be cleanly shut down when the IWebHost is disposed
  • Replaces the built in DI container with Lamar (Jasper only works with Lamar at this point)
  • Jasper also sneaks in some ASP.Net Core middleware to add its own routes into the application, which I’ll show off in the next post about Jasper’s HTTP messaging transport

All of this is documented in the Jasper Getting Started page and in the specific documentation for ASP.Net Core integration.

Longer term, I might try to move Jasper closer to the existing ASP.Net Core bootstrapping mechanisms.

Bootstrapping ASP.Net Core the Idiomatic Jasper Way

The first option is really about adding HTTP support to an idiomatic Jasper application. In this case, you just use the JasperHttpRegistry from the Jasper.Http library as the base class for your application definition like so:

public class AppWithMiddleware : JasperHttpRegistry
{
    public AppWithMiddleware()
    {
        // Do the normal stuff you do to configure
        // service registrations, configuration, and
        // messaging support

        Http.Configure(app =>
        {
            app.UseMiddleware<CustomMiddleware>();

            // Explicitly control the order in which the Jasper
            // middleware is placed within the ASP.Net Core
            // pipeline. 
            app.AddJasper();

            // Just to show how you can configure ASP.Net Core
            // middleware that runs after Jasper's RequestDelegate,
            // but do note that Jasper has its own default "not found"
            // behavior
            app.Run(c =>
            {
                c.Response.StatusCode = 404;

                return c.Response.WriteAsync("Not found");
            });
        });
    }
}

A couple things to note:

  • The Http property in the class shown above is just the IWebHostBuilder interface you’re already used to if you use ASP.Net Core today
  • If the call to IApplicationBuilder.AddJasper() is omitted, Jasper will add its own middleware to the very end of the pipeline
  • The HTTP bootstrapping in the idiomatic model is somewhat parallelized with the messaging support bootstrapping
  • I’d argue that this usage makes the ASP.Net Core StartUp conventional configuration model unnecessary, but you’re perfectly able to continue using that if you want.

I hope to do more optimizations to the cold startup time in the future for the idiomatic Jasper approach that would make this option be more attractive. Right now, the biggest reason to use this approach over the following is to be able to use Jasper’s console application harness and Storyteller integration.

 

Adding Jasper to an Existing ASP.Net Core System

You can also add Jasper to an existing ASP.Net Core system using its idiomatic bootstrapping approach. In this case, you still start with the JasperHttpRegistry base class from the Jasper.Http library, but you mostly use this to configure the messaging support:

public class SimpleJasperBusApp : JasperHttpRegistry
{
    public SimpleJasperBusApp()
    {
        // Enable the HTTP messaging transport
        Http.Transport.EnableListening(true);
        
        // Listen for TCP messages at port 2222
        Transports.LightweightListenerAt(2222);
    }
}

Then, to add the Jasper support to your ASP.Net Core application, you would add these calls:

var builder = new WebHostBuilder();
builder
    .UseKestrel()
    .UseUrls("http://localhost:3003")
    .UseStartup<Startup>()
    
    // This *has* to be the last call 
    // to your IWebHostBuilder
    .UseJasper<SimpleJasperBusApp>();


theHost = builder.Build();

theHost.Start();

I hate this from a usability perspective, but for right now, the call to UseJasper() has to be added after any other IStartUp registration including the UseStartup<T>() method. You still have the same ability to explicitly control the order of the Jasper middleware within your ASP.Net Core middleware pipeline.

 

 

 

Asynchronous Commands with Oakton 1.4

Oakton is a little command line parsing and execution library that I use within Storyteller, Marten, and Jasper. The benefit of Oakton over the other bazillion command line parsing tools in .Net is the way it completely separates command execution from string parsing.

The big downside and most frequent request to Oakton on its initial rollout was that it lacked support for asynchronous commands (it’s a descendent of code I originally wrote into FubuCore back in 2010 when nobody cared about asynchronous code yet). Not to worry thought, because Andy Dote just added support for that shows up in the Oakton 1.4.0 release.

To write an asynchronous command, use the new AsyncOaktonCommand base class like so:

    [Description("Say my name", Name = "say-async-name")]
    public class AsyncSayNameCommand : OaktonAsyncCommand
    {
        public AsyncSayNameCommand()
        {
            Usage("Capture the users name").Arguments(x => x.FirstName, x => x.LastName);
        }

        public override async Task Execute(SayName input)
        {
            await Console.Out.WriteLineAsync($"{input.FirstName} {input.LastName}");

            return true;
        }
    }

To execute commands asynchronously all the way through your Program.Main() function, use the new asynchronous overloads in CommandExecutor like so:

        static Task<int> Main(string[] args)
        {
            var executor = CommandExecutor.For(_ =>
            {
                // Find and apply all command classes discovered
                // in this assembly
                _.RegisterCommands(typeof(Program).GetTypeInfo().Assembly);
            });

            return executor.ExecuteAsync(args);
        }

 

Marten 2.6/2.7 and why we don’t support Sql Server (yet)

Between Lamar, a new Jasper release, and two Marten releases, I’ve been furiously trying to clear my plate of outstanding OSS work the past couple weeks to allow myself to concentrate on new efforts. As any experienced OSS author can probably tell you though, making a release is like leaving the screen door open and letting all the flies in as new GitHub issues and user questions come streaming in. My best advice — and some day I’ll take it myself — is to treat your OSS project as a continuous process rather than a series of short sprints.

I pushed Marten 2.6.0 with a lot of bug fixes and community pull requests on Friday. That seemed to open the door for more user issues, so I made an additional Marten 2.7.0 release yesterday with a couple pull requests I’d missed and some new convenience methods that had been requested for the event sourcing support.

The API’s are completely backwards compatible with a couple new additions in the event sourcing for finer grained live aggregation and additional options in the ViewProjection feature. I did let something slip by me in a pull request that negates binary compatibility, so you may need to recompile your code that uses Marten (optional arguments got added to a couple public methods and that breaks binary compatibility).

Thank you as always to the greater Marten community for all your contributions and ideas that continue to push Marten forward.

What about Sql Server/Oracle/MySql Support?

I’m frequently asked what it would take for Marten to support multiple database engines with the leading contender being Sql Server, unsurprisingly since this is coming from .Net folks. First off, I’m not opposed to doing this some time in the future when Sql Server’s JSON support catches up to where Postgresql is now. I’ve even promised our Sql Server-centric database team at work we will support Sql Server at some undetermined point in the future.

Putting any discussion of Postgresql vs. Sql Server aside, I do not believe that it’s feasible to run Marten on top of Sql Server 2016 at this time because:

  • Marten depends on Postgresql and Npgsql specific constructs like array types that do not exist in Sql Server today
  • Postgresql has quite a few JSON operators that have no equivalent in Sql Server 2016
  • Sql Server does not have an equivalent to the Postgresql JSONB type for more efficient storage and querying
  • Several features in Marten depend on being able to use embedded Javascript inside the database engine. Postgresql supports that with the PLv8 extension, but I’m not aware of Sql Server having an equivalent
  • It would require a very heavy rebuild of much of the Linq parsing support, and I can’t even begin to explain how much that would suck
  • It would just flat out be too time consuming at the moment

The one thing — and it’s a big thing — that Sql Server gets right over Postgresql in my opinion is its JSON operators are based around JSONPath. Assuming that Postgresql gets sql/json support in v11, and the next version of Sql Server comes with some of the missing features from Postgresql I outlined above, then I see a major Marten 3.0 release happening that finally strives to be somewhat database engine agnostic.

Don’t agree with me that Sql Server is lacking in the JSON support that Marten needs? Prove me wrong and build it yourself because there is no project like Marten for Sql Server 2016 today.

 

Jasper v0.6: Better outbox usage, Lamar, ASP.Net Core integration changes

I’ve been a little distracted with the Lamar/BlueMilk work and Marten bug-fixing, but Jasper is still rolling along and about to get into production at work (a super early prototype is running in a low volume system now). I just pushed Jasper 0.6 to Nuget with some new improvements. The documentation has been updated and reflects the new changes described below.

Big changes:

  • Jasper uses Lamar for all IoC usage. I’m still very confident that the Jasper + Lamar (was “BlueMilk”) combination will lead to a very effective combination of flexibility and performance in the runtime pipeline. Lamar natively supports all the ASP.Net Core DI abstractions, so if you don’t care about any of its advanced features you don’t even need to care that it exists.
  • I broke the ASP.Net Core integration out into its own library, Jasper.Http. SeeAdding Jasper to an ASP.Net Core Application in the docs for the details.

    We’ve gone back and forth on whether Jasper is going to be modular or an easier to work with single library, but this one came down to the ASP.Net Core bootstrapping being somewhat expensive and making the main Jasper test suite be unnecessarily slow, so out it goes. I don’t think you’re going to notice the hit if all you do is bootstrap a single application in a test suite, but you sure do if you’re developing on Jasper itself and bootstrap and tear down 100+ applications during the integration tests;-)

  • The “outbox pattern” support was extended to cascading messages. This was an overdue improvement over its FubuMVC/FubuTransportation/RhinoServiceBus ancestors. I’ll have a blog post about this next week after every one is back from the MVP Summit.
  • IServiceBus was renamed to IMessageContext. This one will make a lot more sense with some documentation or a blog post on the outbox pattern work.
  • (Hopefully) Easier messaging support configuration. I tried hard to simplify the API underneath

Next Up…

Jasper is getting put into a production application at work within the month, and we’re doing some significant proof of concept work around using Consul for service discovery with Jasper applications and building out Octopus deployment steps for the dynamic subscriptions. Next week I’ll get back to blogging about Jasper’s integration with ASP.Net Core applications, the new HTTP transport option, and Jasper’s support for the “outbox” pattern.

Renaming BlueMilk to Lamar

BlueMilk was the early working name of a successor project to StructureMap that was originally ripped out of the new Jasper framework project. 

Most of the feedback on the name “BlueMilk” wasn’t positive. I wasn’t terribly attached to the name, so I’m officially renaming “BlueMilk” to “Lamar.” The first Nuget (v0.8) is published with the very latest work. I might be able to throw an OSS Friday sometime this week at finishing the remaining StructureMap features inside of Lamar and publishing some documentation, but we’ll see.

First, what the heck is Lamar? For most of you, it’s my intended successor to the venerable, well liked (by at least some people), and unfortunately slowpoke StructureMap library. It’s also the runtime code generation and compilation subsystem I pulled out of Jasper so folks could use that independently of Jasper.

For the moment, you can find way more information about Lamar under the “BlueMilk” tag on my blog.

What’s with the new name?

Other than Marten, most of my OSS efforts the past 2-3 years have really been working toward the Jasper framework we’re brewing up at work as a successor to FubuMVC. “Jasper” itself is just named after my ancestral hometown (Jasper, MO), and most of the other projects on the JasperFx organization are named after either other little towns around Jasper (Oakton, Alba) or local landmarks (Baseline). Fitting into that theme, Lamar is the next town up highway 71 and I’ve got plenty of family roots there as well.

Other notes that may only interest me:

  • Mirabeau B. Lamar was the 2nd president of the Republic of Texas, a hero of the TX revolution, and worlds of things in Texas are named after him. I didn’t know this until researching this post, but Lamar, MO is apparently named after him as well.
  • My wife is *this* close to completing a master’s program at Lamar University and I’m super proud of her
  • Lamar the town is the birthplace of Harry S. Truman
  • It was raided during the Civil War by Quantrill’s Raiders (think Jesse James)
  • Wyatt Earp was their first constable
  • I’m biased, but the Barton County Fair in Lamar may be the best small town fair in the entire state
  • If you’re a Modern Family friend, the Cameron character is supposedly from this area

Differences Between BlueMilk and StructureMap

If you’ve been following me lately on Twitter or in this blog, you know I’ve been busy working on a new project called BlueMilk that’s meant to be a much faster replacement for the venerable StructureMap library. There are some advanced StructureMap features that will not be included into BlueMilk. Otherwise though, I’m aiming to make BlueMilk a near drop in replacement for most StructureMap users in terms of behavior and API. That being said, there are some important differences in behavior that you’ll need to be aware of switching from StructureMap to BlueMilk. In all the cases discussed below, the change was to ensure better compatibility with how our ASP.Net Core overlords believe IoC tools work. Bitterness at the ASP.net team aside, some of these design changes led to almost dramatically simpler internals and performance in BlueMilk compared to StructureMap, so it’s still a win overall.

 

Terminology

I dropped some of the old StructureMap verbiage just to be more consistent with ASP.Net Core’s verbiage.

  • “PluginType” becomes “ServiceType”
  • “PluggedType” becomes “ImplementationType”
  • “Instance” is still the name of the BlueMilk unit of registration (it’s basically a superset of ServiceDescriptor in ASP.Net Core), but the API signatures change
  • “Container” is still the same

 

Constructor Selection

While this has been pluggable and configurable forever, by default StructureMap tries to use the “greediest” public constructor it can find on a concrete type. If there are multiple constructors with the same number of arguments, it uses the first one it encounters.

In BlueMilk, we follow the ASP.Net Core prescribed logic of choosing the greediest constructor where BlueMilk has some known registration for each argument. I’ve always hated this idea, purposely kept it out of StructureMap, and I feel like it’s a bad pattern to use with IoC tools because of the extra mental overhead in understanding what’s going on (it’s an addon for ASP.Net Core usage in StructureMap 4.*), but that’s not worth fighting over.

I don’t like this behavior because it can wallpaper over problems with registrations and give you some false positives. My advice when using an IoC container is to build your classes as if they’re always built by the IoC container. In my experience, folks get in trouble when they try to be half in and half out of IoC usage with multiple constructors and optional arguments.

 

Object Lifetime / Lifecycle

This one’s a big difference, so do watch out. BlueMilk changes to using the ASP.Net Core verbiage and logic for service lifecycles. Singleton and Scoped work identically in BlueMilk as they did in StructureMap, except that BlueMilk is a lot better about tracking IDisposable dependencies within singleton or scoped construction.

The new “Transient” really maps to StructureMap’s “UniquePerRequest” lifecycle. If you depended on StructureMap’s default “Transient” behavior from before to have objects scoped per logical request or within a nested container, you’ll probably want to switch to the “Scoped” lifecycle.

 

Use vs. Add No Longer Matters

In the very beginning — and remember that StructureMap is literally the first .Net IoC container so there was no prior art — I had the belief that in the case of multiple registrations for the same service type, the user should explicitly tell StructureMap which one is the default that gets resolved from a call to IContainer.GetInstance(type) and which registrations are just additional services that would be either resolved by name or by an enumerable of all of them.

BlueMilk just uses the ASP.Net Core compliant logic of saying that the very last registration against a service type is the default registration.

In more concrete terms, the old StructureMap Use() and Add() methods now mean the exact same thing, and the last one wins. I might mark one or the other as [Obsolete] just to make the transitions go a little easier. See this code:

var container = new Container(_ =>
{
    // In StructureMap, this would be the default
    _.For<IWidget>().Use<BlueWidget>();

    // In BlueMilk, this would be because it's last
    _.For<IWidget>().Add<GreenWidget>();
});

 

 

BlueMilk 0.8: It’s fast, runs MVC apps, and probably needs a new name

EDIT 4:30 PM CST on 2/21: Egg on my face. How’s this for the new world order of .Net Core? It works perfectly on OS X, but it blows up on the `RegistryPolicyResolver` class only on Windows. I’ll get a 0.8.1 up soon to fix whatever is different.

I just published the latest BlueMilk v0.8 to Nuget with quite a bit of performance optimization, some additional StructureMap functionality added in, and the ability to handle every kind of service registration that a basic MVC Core application will throw at an IoC container.

BlueMilk is the current codename for a new IoC tool that partially originated in Jasper. You can read about the goals and motivation for the project in Introducing BlueMilk: StructureMap’s Replacement & Jasper’s Special Sauce.

One of my colleagues made the observation yesterday that while being a Star Wars nerd and getting the reference, the name “BlueMilk” is off putting and we probably need to change that (plus it feels awkward to say out loud to me). Other than Marten and BlueMilk, all the projects in the JasperFx organization are named after other little towns or landmarks around my hometown. Once upon a time, I parked some of the code that’s not part of BlueMilk in another repository named “Lamar” that fits that naming scheme, plus my wife is doing a master’s program at Lamar University, I have a former coworker who played baseball there, and it’s also the name of a Texas revolutionary hero. If nobody has a better idea, I’ll probably rename BlueMilk to “Lamar.” My other idea was “Corsair,” but that’s really just too cool a name for yet another IoC tool.

Usage in MVC Core

I blogged last week that BlueMilk is Ready for Early Adopters. I was wrong of course, but thank you to Mark Warpool and others for actually trying to use it and giving me some important feedback. The big problems with 0.7 were that the code generation model that BlueMilk uses internally can’t handle internal types and didn’t allow for using generic types as constructor parameters. Wouldn’t you know it, ASP.Net Core MVC has quite a bit of both of those usages in its service registrations and BlueMilk was falling flat on its face in an MVC Core application (correction, “works on my box,” fails on a couple registrations in AppVeyor even though it’s the same version of the .Net SDK. Still saying it’s good to go at least until someone proves it’s a no go).

After a couple fixes (one big and one small), there’s now a test in BlueMilk that successfully builds every possible service registration from a basic MVC Core application. For those of you following along at home, I had to revert to using the dynamic Expression compiled to a Func<> trick to slide around the nonpublic types just to call non-public constructors.

Performance

A caveat here, it’s just not terribly likely that your IoC tool of choice is the performance bottleneck in your system. 

First off, Maksim Volkau has built some seriously cool stuff, and BlueMilk got quite a bit of the performance boost I’m talking about here from using his ImTools library (both Marten and StructureMap use FastExpressionCompiler as well).

One of my coworkers asked how BlueMilk compared to StructureMap in terms of performance, so I threw together some benchmarks where I was able to show BlueMilk being over 5X faster than StructureMap over a range of potential usages. I made the mistake of tweeting about that yesterday, and Eric Smith asked me how BlueMilk compared to the build in DI container inside of ASP.Net Core. After adding a comparison to the built in container to my BenchmarkDotNet metrics, I could see that BlueMilk lagged a bit (~30% slower over all). Several optimizations later, I can now say that BlueMilk is (barely) faster than the built in DI container and closing in on an order of magnitude faster than the latest StructureMap.

Using a barebones MVC Core application with logging added in as well, I built a series of metrics that just loops through the registered types and builds each possible type. It’s a lazy way of building up metrics, but it gave me a mix of registrations by type, by lambda, pre-built objects, and some deeper object graphs. It’s probably a bit bogus because this isn’t the way that an application is going to use the IoC tool at runtime and may weight more heavily on usages that don’t actually happen.

That being said, here’s the overall metrics on just creating every possible registered type in that minimal MVC Core application:

BenchmarkDotNet=v0.10.12, OS=macOS 10.12.6 (16G1212) [Darwin 16.7.0]
Intel Core i7-6920HQ CPU 2.90GHz (Skylake), 1 CPU, 8 logical cores and 4 physical cores
.NET Core SDK=2.1.4
  [Host]     : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT
  DefaultJob : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT


   Method | ProviderName |      Mean |     Error |    StdDev |
--------- |------------- |----------:|----------:|----------:|
 AllTypes |   AspNetCore |  73.98 us |  1.444 us |  1.976 us |
 AllTypes |     BlueMilk |  70.92 us |  1.408 us |  2.392 us |
 AllTypes | StructureMap | 646.28 us | 12.856 us | 27.398 us |

Getting much more specific, here are some finer grained metrics with an explanation of the different measurements below:

BenchmarkDotNet=v0.10.12, OS=macOS 10.12.6 (16G1212) [Darwin 16.7.0]
Intel Core i7-6920HQ CPU 2.90GHz (Skylake), 1 CPU, 8 logical cores and 4 physical cores
.NET Core SDK=2.1.4
  [Host]     : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT
  DefaultJob : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT


      Method | ProviderName |         Mean |        Error |        StdDev |       Median |
------------ |------------- |-------------:|-------------:|--------------:|-------------:|
 CreateScope |   AspNetCore |     429.0 ns |     8.288 ns |      7.347 ns |     428.2 ns |
     Lambdas |   AspNetCore |   1,784.4 ns |    25.886 ns |     22.948 ns |   1,777.8 ns |
   Internals |   AspNetCore |     914.2 ns |    17.575 ns |     15.580 ns |     912.6 ns |
     Objects |   AspNetCore |     810.2 ns |     7.723 ns |      6.449 ns |     808.7 ns |
  Singletons |   AspNetCore |  18,428.6 ns |   203.784 ns |    159.101 ns |  18,441.3 ns |
       Scope |   AspNetCore |     556.7 ns |     7.823 ns |      7.317 ns |     555.9 ns |
  Transients |   AspNetCore |  41,882.1 ns |   391.872 ns |    327.231 ns |  41,787.8 ns |
 CreateScope |     BlueMilk |     110.8 ns |     2.205 ns |      2.944 ns |     111.4 ns |
     Lambdas |     BlueMilk |   2,138.1 ns |    27.465 ns |     25.691 ns |   2,140.5 ns |
   Internals |     BlueMilk |     332.2 ns |     3.926 ns |      3.278 ns |     331.4 ns |
     Objects |     BlueMilk |     586.9 ns |    17.605 ns |     51.633 ns |     575.4 ns |
  Singletons |     BlueMilk |   9,852.8 ns |   196.721 ns |    548.380 ns |   9,780.1 ns |
       Scope |     BlueMilk |     330.8 ns |     5.781 ns |      4.828 ns |     332.1 ns |
  Transients |     BlueMilk |  54,439.2 ns | 1,083.872 ns |  2,967.082 ns |  53,801.7 ns |
 CreateScope | StructureMap |  16,781.0 ns |   334.284 ns |    948.307 ns |  16,584.2 ns |
     Lambdas | StructureMap |  12,329.5 ns |   244.697 ns |    686.155 ns |  12,121.9 ns |
   Internals | StructureMap |  10,585.0 ns |   209.617 ns |    393.712 ns |  10,519.9 ns |
     Objects | StructureMap |  17,739.9 ns |   430.679 ns |    560.005 ns |  17,606.7 ns |
  Singletons | StructureMap | 162,029.0 ns | 3,191.513 ns |  6,148.961 ns | 161,590.8 ns |
       Scope | StructureMap |   5,830.1 ns |   158.896 ns |    463.507 ns |   5,700.8 ns |
  Transients | StructureMap | 451,798.1 ns | 8,988.047 ns | 21,707.143 ns | 448,860.3 ns |

The metrics named in the first column are:

  1. “CreateScope” — measures how long it takes to create a completely new container scope as MVC Core and other frameworks do on each HTTP request.
  2. “Lambdas” — resolving services that were registered with some kind of Func<IServiceProvider, object> factory
  3. “Internals” — resolving non-public types
  4. “Objects” — resolving services that were registered with a pre-built object
  5. “Singletons” — all singleton registrations of all kinds
  6. “Scope” — bad name, but all registrations with the “Scoped” lifetime
  7. “Transients” — all registrations with the “Transient” lifetime

 

There’s still some performance fat in BlueMilk’s code, but I’m saying that I’ve hit the point of diminishing returns for now and I’m staying put on performance.

New Functionality

BlueMilk v0.8 adds in some old StructureMap behavior for:

Roadmap

I’m probably done working on BlueMilk for now other than the inevitable bug reports. When I do come back to it (or someone else picks it up), the next version (v0.9) will hopefully have support for decorators and interception similar to StructureMap’s existing model. I’d hope to have a 1.0 version out sometime this summer or fall after it’s been in production somewhere for awhile.