Category Archives: ASP.NET Core

Asynchronous initialization in ASP.NET Core, revisited

Initialization in ASP.NET Core is a bit awkward. There are well defined places for registering services (the Startup.ConfigureServices method) and for building the middleware pipeline (the Startup.Configure method), but not for performing other initialization steps (e.g. pre-loading data, seeding a database, etc.).

Using a middleware: not such a good idea

Two months ago I published a blog post about asynchronous initialization of an ASP.NET Core app using a custom middleware. At the time I was rather pleased with my solution, but a comment from Frantisek made me realize it wasn’t such a good approach. Using a middleware for this has a major drawback: even though the initialization will only be performed once, the app will still incur the cost of calling an additional middleware for every single request. Obviously, we don’t want the initialization to impact performance for the whole lifetime of the app, so it shouldn’t be done in the request processing pipeline.

A better approach: the Program.Main method

There’s a piece of all ASP.NET Core apps that’s often overlooked, because it’s generated by a template and we rarely need to touch it: the Program class. It typically looks like this:

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}

Basically, it builds a web host and immediately runs it. However, there’s nothing to prevent us from doing something with the host before running it. In fact, it’s a pretty good place to perform the app initialization:

    public static void Main(string[] args)
    {
        var host = CreateWebHostBuilder(args).Build();
        /* Perform initialization here */
        host.Run();
    }

As a bonus, the web host exposes a service provider (host.Services), configured with the services registered in Startup.ConfigureServices, which gives us access to everything we might need to initialize the app.

But wait, didn’t I mention asynchronous initialization in the title? Well, since C# 7.1, it’s possible to make the Main method async. To enable it, just set the LangVersion property to 7.1 or later in your project (or latest if you always want the most recent features).

Wrapping up

While we could just resolve services from the service provider and call them directly in the Main method, it wouldn’t be very clean. Instead, it would be better to have an initializer class that receives the services it needs via dependency injection. This class would be registered in Startup.ConfigureServices and called from the Main method.

After using this approach in two different projects, I put together a small library to make things easier: AspNetCore.AsyncInitialization. It can be used like this:

  1. Create a class that implements the IAsyncInitializer interface:

    public class MyAppInitializer : IAsyncInitializer
    {
        public MyAppInitializer(IFoo foo, IBar bar)
        {
            ...
        }
    
        public async Task InitializeAsync()
        {
            // Initialization code here
        }
    }
    
  2. Register the initializer in Startup.ConfigureServices, using the AddAsyncInitializer extension method:

    services.AddAsyncInitializer<MyAppInitializer>();
    

    It’s possible to register multiple initializers.

  3. Call the InitAsync extension method on the web host in the Main method:

    public static async Task Main(string[] args)
    {
        var host = CreateWebHostBuilder(args).Build();
        await host.InitAsync();
        host.Run();
    }
    

    This will run all registered initializers.

There you have it, a nice and clean way to initialize your app. Enjoy!

Handling multipart requests with JSON and file uploads in ASP.NET Core

Suppose we’re writing an API for a blog. Our "create post" endpoint should receive the title, body, tags and an image to display at the top of the post. This raises a question: how do we send the image? There are at least 3 options:

  • Embed the image bytes as base64 in the JSON payload, e.g.

    {
        "title": "My first blog post",
        "body": "This is going to be the best blog EVER!!!!",
        "tags": [ "first post", "hello" ],
        "image": "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="
    }
    

    This works fine, but it’s probably not a very good idea to embed an arbitrarily long blob in JSON, because it could use a lot of memory if the image is very large.

  • Send the JSON and image as separate requests. Easy, but what if we want the image to be mandatory? There’s no guarantee that the client will send the image in a second request, so our post object will be in an invalid state.

  • Send the JSON and image as a multipart request.

The last approach seems the most appropriate; unfortunately it’s also the most difficult to support… There is no built-in support for this scenario in ASP.NET Core. There is some support for the multipart/form-data content type, though; for instance, we can bind a model to a multipart request body, like this:

public class MyRequestModel
{
    [Required]
    public string Title { get; set; }
    [Required]
    public string Body { get; set; }
    [Required]
    public IFormFile Image { get; set; }
}

public IActionResult Post([FromForm] MyRequestModel request)
{
    ...
}

But if we do this, it means that each property maps to a different part of the request; we’re completely giving up on JSON.

There’s also a MultipartReader class that we can use to manually decode the request, but it means we have to give up model binding and automatic model validation entirely.

Custom model binder

Ideally, we’d like to have a request model like this:

public class CreatePostRequestModel
{
    [Required]
    public string Title { get; set; }
    [Required]
    public string Body { get; set; }
    public string[] Tags { get; set; }
    [Required]
    public IFormFile Image { get; set; }
}

Where the Title, Body and Tags properties come from a form field containing JSON and the Image property comes from the uploaded file. In other words, the request would look like this:

POST /api/blog/post HTTP/1.1
Content-Type: multipart/form-data; boundary=AaB03x
 
--AaB03x
Content-Disposition: form-data; name="json"
Content-Type: application/json
 
{
    "title": "My first blog post",
    "body": "This is going to be the best blog EVER!!!!",
    "tags": [ "first post", "hello" ]
}
--AaB03x
Content-Disposition: form-data; name="image"; filename="image.jpg"
Content-Type: image/jpeg
 
(... content of the image.jpg file ...)
--AaB03x

Fortunately, ASP.NET Core is very flexible, and we can actually make this work, by writing a custom model binder.

Here it is:

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;

namespace TestMultipart.ModelBinding
{
    public class JsonWithFilesFormDataModelBinder : IModelBinder
    {
        private readonly IOptions<MvcJsonOptions> _jsonOptions;
        private readonly FormFileModelBinder _formFileModelBinder;

        public JsonWithFilesFormDataModelBinder(IOptions<MvcJsonOptions> jsonOptions, ILoggerFactory loggerFactory)
        {
            _jsonOptions = jsonOptions;
            _formFileModelBinder = new FormFileModelBinder(loggerFactory);
        }

        public async Task BindModelAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext == null)
                throw new ArgumentNullException(nameof(bindingContext));

            // Retrieve the form part containing the JSON
            var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.FieldName);
            if (valueResult == ValueProviderResult.None)
            {
                // The JSON was not found
                var message = bindingContext.ModelMetadata.ModelBindingMessageProvider.MissingBindRequiredValueAccessor(bindingContext.FieldName);
                bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, message);
                return;
            }

            var rawValue = valueResult.FirstValue;

            // Deserialize the JSON
            var model = JsonConvert.DeserializeObject(rawValue, bindingContext.ModelType, _jsonOptions.Value.SerializerSettings);

            // Now, bind each of the IFormFile properties from the other form parts
            foreach (var property in bindingContext.ModelMetadata.Properties)
            {
                if (property.ModelType != typeof(IFormFile))
                    continue;

                var fieldName = property.BinderModelName ?? property.PropertyName;
                var modelName = fieldName;
                var propertyModel = property.PropertyGetter(bindingContext.Model);
                ModelBindingResult propertyResult;
                using (bindingContext.EnterNestedScope(property, fieldName, modelName, propertyModel))
                {
                    await _formFileModelBinder.BindModelAsync(bindingContext);
                    propertyResult = bindingContext.Result;
                }

                if (propertyResult.IsModelSet)
                {
                    // The IFormFile was sucessfully bound, assign it to the corresponding property of the model
                    property.PropertySetter(model, propertyResult.Model);
                }
                else if (property.IsBindingRequired)
                {
                    var message = property.ModelBindingMessageProvider.MissingBindRequiredValueAccessor(fieldName);
                    bindingContext.ModelState.TryAddModelError(modelName, message);
                }
            }

            // Set the successfully constructed model as the result of the model binding
            bindingContext.Result = ModelBindingResult.Success(model);
        }
    }
}

To use it, just apply this attribute to the CreatePostRequestModel class above:

[ModelBinder(typeof(JsonWithFilesFormDataModelBinder), Name = "json")]
public class CreatePostRequestModel

This tells ASP.NET Core to use our custom model binder to bind this class. The Name = "json" part tells our binder from which field of the multipart request it should read the JSON (this is the bindingContext.FieldName in the binder code).

Now we just need to pass a CreatePostRequestModel to our controller action, and we’re done:

[HttpPost]
public ActionResult<Post> CreatePost(CreatePostRequestModel post)
{
    ...
}

This approach enables us to have a clean controller code and keep the benefits of model binding and validation. It messes up the Swagger/OpenAPI model though, but hey, you can’t have everything!

Asynchronous initialization in ASP.NET Core with custom middleware

Update: I no longer recommend the approach described in this post. I propose a better solution here: Asynchronous initialization in ASP.NET Core, revisited.

Sometimes you need to perform some initialization steps when your web application starts. However, putting such code in the Startup.Configure method is generally not a good idea, because:

  • There’s no current scope in the Configure method, so you can’t use services registered with "scoped" lifetime (this would throw an InvalidOperationException: Cannot resolve scoped service ‘MyApp.IMyService’ from root provider).
  • If the initialization code is asynchronous, you can’t await it, because the Configure method can’t be asynchronous. You could use .Wait to block until it’s done, but it’s ugly.

Async initialization middleware

A simple way to do it involves writing a custom middleware that ensures initialization is complete before processing a request. This middleware starts the initialization process when the app starts, and upon receiving a request, will wait until the initialization is done before passing the request to the next middleware. A basic implementation could look like this:

public class AsyncInitializationMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;
    private Task _initializationTask;

    public AsyncInitializationMiddleware(RequestDelegate next, IApplicationLifetime lifetime, ILogger<AsyncInitializationMiddleware> logger)
    {
        _next = next;
        _logger = logger;

        // Start initialization when the app starts
        var startRegistration = default(CancellationTokenRegistration);
        startRegistration = lifetime.ApplicationStarted.Register(() =>
        {
            _initializationTask = InitializeAsync(lifetime.ApplicationStopping);
            startRegistration.Dispose();
        });
    }

    private async Task InitializeAsync(CancellationToken cancellationToken)
    {
        try
        {
            _logger.LogInformation("Initialization starting");

            // Do async initialization here
            await Task.Delay(2000);

            _logger.LogInformation("Initialization complete");
        }
        catch(Exception ex)
        {
            _logger.LogError(ex, "Initialization failed");
            throw;
        }
    }

    public async Task Invoke(HttpContext context)
    {
        // Take a copy to avoid race conditions
        var initializationTask = _initializationTask;
        if (initializationTask != null)
        {
            // Wait until initialization is complete before passing the request to next middleware
            await initializationTask;

            // Clear the task so that we don't await it again later.
            _initializationTask = null;
        }

        // Pass the request to the next middleware
        await _next(context);
    }
}

We can then add this middleware to the pipeline in the Startup.Configure method. It should be added early in the pipeline, before any other middleware that would need the initialization to be complete.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseMiddleware<AsyncInitializationMiddleware>();

    app.UseMvc();
}

Dependencies

At this point, our initialization middleware doesn’t depend on any service. If it has transient or singleton dependencies, they can just be injected into the middleware constructor as usual, and used from the InitializeAsync method.

However, if the dependencies are scoped, we’re in trouble: the middleware is instantiated directly from the root provider, not from a scope, so it can’t take scoped dependencies in its constructor.

Depending on scoped dependencies for initialization code doesn’t make a lot of sense anyway, since by definition scoped dependencies only exist in the context of a request. But if for some reason you need to do it anyway, the solution is to perform initialization in the middleware’s Invoke method, injecting the dependencies as method parameters. This approach has at least two drawbacks:

  • Initialization won’t start until a request is received, so the first requests will have a delayed response time; this can be an issue if the initialization takes a long time.
  • You need to take special care to ensure thread safety: the initialization code must run only once, even if several requests arrive before initialization is done.

Writing thread-safe code is hard and error-prone, so avoid getting in this situation if possible, e.g. by refactoring your services so that your initialization middleware doesn’t depend on any scoped service.