question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Implementing IMiddlewareFactory with Simple Injector for ASP.NET Core

See original GitHub issue

Hi Steven (@dotnetjunkie),

I’m working on the advanced IMiddleware/IMiddlewareFactory topic for the ASP.NET Core docs, and I’ve hit a snag with scope lifetimes that I can’t work out. I probably have created a completely bonkers wiring-up job and/or factory! lol

The introductory topic that compares conventional middleware activation with factory-based activation is here 👉 https://docs.microsoft.com/aspnet/core/fundamentals/middleware/extensibility

I’m working on a new topic that uses the factory with Simple Injector. Everything seemed to be going along fairly well until I hit ye 'olde scoping problems, such as …

The AppDbContext is registered as ‘Async Scoped’ lifestyle, but the instance is requested outside the context of an active (Async Scoped) scope.

Exception has occurred: CLR/SimpleInjector.ActivationException
An exception of type 'SimpleInjector.ActivationException' occurred in SimpleInjector.dll but was not handled in user code: 'The AppDbContext is registered as 'Async Scoped' lifestyle, but the instance is requested outside the context of an active (Async Scoped) scope.'
   at SimpleInjector.Scope.GetScopelessInstance[TImplementation](ScopedRegistration`1 registration)
   at SimpleInjector.Advanced.Internal.LazyScopedRegistration`1.GetInstance(Scope scope)
   at SimpleInjector.InstanceProducer.GetInstance()
   at SimpleInjector.Container.GetInstance(Type serviceType)
   at MiddlewareExtensibilitySample.Middleware.SimpleInjectorMiddlewareFactory.Create(Type middlewareType) in C:\Users\guard\Documents\GitHub\MiddlewareExtensibilitySample2\Middleware\SimpleInjectorMiddlewareFactory.cs:line 22
   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass5_1.<<UseMiddlewareInterface>b__1>d.MoveNext()

The middleware is unremarkable … just writes a query string value to an in-memory dB:

public class SimpleInjectorActivatedMiddleware : IMiddleware
{
    private readonly AppDbContext _db;

    public SimpleInjectorActivatedMiddleware(AppDbContext db)
    {
        _db = db;
    }

    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        var keyValue = context.Request.Query["key"];

        if (!string.IsNullOrWhiteSpace(keyValue))
        {
            _db.Add(new Request()
                {
                    DT = DateTime.UtcNow, 
                    MiddlewareActivation = "SimpleInjectorActivatedMiddleware", 
                    Value = keyValue
                });

            await _db.SaveChangesAsync();
        }

        await next(context);
    }
}

I’d like the factory to get that going. I can’t confirm this works because of the scoping issue, but here’s what I was thinking …

public class SimpleInjectorMiddlewareFactory : IMiddlewareFactory
{
    private readonly Container _container;

    public SimpleInjectorMiddlewareFactory(Container container)
    {
        _container = container;
    }

    public IMiddleware Created { get; private set; }
    public IMiddleware Released { get; private set; }

    public IMiddleware Create(Type middlewareType)
    {
        Created = _container.GetInstance(middlewareType) as IMiddleware;

        return Created;
    }

    public void Release(IMiddleware middleware)
    {
        Released = middleware;
    }
}

… an unremarkable pipeline for this sample (trying to keep the Startup class lean, so I moved InitializeContainer bits up to ConfigureServices) …

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
    }

    app.UseSimpleInjectorActivatedMiddleware();

    app.UseStaticFiles();
    app.UseMvc();
}

… and the wiring up bits in ConfigureServices look like this (again, dropping the IntegrateSimpleInjector format to keep things lean) …

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<SimpleInjectorActivatedMiddleware>();
    services.AddTransient<IMiddlewareFactory>(_ =>
    {
        return new SimpleInjectorMiddlewareFactory(container);
    });

    services.AddMvc();

    container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();

    container.Register<AppDbContext>(() => 
    {
        var optionsBuilder = new DbContextOptionsBuilder<DbContext>();
        optionsBuilder.UseInMemoryDatabase("InMemoryDb");
        return new AppDbContext(optionsBuilder.Options);
    }, Lifestyle.Scoped);

    container.Register<SimpleInjectorMiddlewareFactory>();

    container.Register<SimpleInjectorActivatedMiddleware>();

    container.Verify();
}

The full project is over here 👉 https://github.com/guardrex/MiddlewareExtensibilitySample2

… and the scopes you see in the GH project and here ☝️ I hacked around with trying to resolve this but no luck. It actually seems like lifestyles aren’t my problem and that I haven’t wired Simple Injector up properly or I built a broken factory.

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:13

github_iconTop GitHub Comments

1reaction
guardrexcommented, Feb 4, 2018

Ah! I see. Thanks. As an aside, I hope [@]DamianEdwards received that feedback. Anywho … the workaround in #362 makes perfect sense. Thanks again! This sample is almost there!

0reactions
guardrexcommented, Feb 7, 2018

@dotnetjunkie As of this moment, I’m just following orders. I DO stand ready to create a new sample using another container if they order such a move. In the meantime, I hope I reduce your concerns a tad with some very strong language in the note that will be added to the topic on https://github.com/aspnet/Docs/pull/5382.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Middleware activation with a third-party container in ASP. ...
The sample app demonstrates middleware activation by an IMiddlewareFactory implementation, SimpleInjectorMiddlewareFactory . The sample uses the ...
Read more >
ASP.NET Core Integration Guide
To integrate with Simple Injector, you need to implement ASP.NET Core's IMiddleware interface on your middleware classes. This UseMiddleware overload ensures ...
Read more >
ASP.NET Core and ASP.NET Core MVC Integration Guide
The practice with Simple Injector is to use Simple Injector to build up object graphs of your application components and let the built-in...
Read more >
ASP.NET Core MVC Integration Guide
The following code snippet shows how to use the integration package to apply Simple Injector to your web application's Startup class.
Read more >
ASP.NET Core MVC Integration Guide
The practice with Simple Injector is to use Simple Injector to build up object graphs of your application components and let the built-in...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found