2

I would like to put in a bit of infrastructure on my project to SaveChanges on my db context at the end of every request.

So I create a simple piece of Owin middleware

app.Use(async (ctx, req) => {
     await req();
     var db = DependencyResolver.Current.GetService<MyDbContext>();
     await db.SaveChangesAsync();
});

This does not work and throws the error

Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it has already been disposed.

If I resolve the db before completing the request

app.Use(async (ctx, req) => {
     var db = DependencyResolver.Current.GetService<MyDbContext>();
     await req();
     await db.SaveChangesAsync();
});

It doesn't throw an error but it also doesn't work (as in changes aren't saved to the db and viewing db in the debugger shows the DbSet's Local property throwing an InvalidOperationException about it being disposed.

I've tried with and without async, registering the middleware before and after autofac configuration (app.UseAutofacMiddleware(container)) and resolving the LifetimeScope directly from the Owin environment. All give me the same results.

I've done something like this before with Structuremap, but can't seem to figure the correct way to get Autofac to play nice.

George Mauer
  • 117,483
  • 131
  • 382
  • 612
  • How did you register your context ? using `InstancePerLifetimeScope` or `InstancePerRequest` ? – Cyril Durand May 11 '15 at 08:36
  • 1
    Calling `SaveChanges` at that point in the request is a bad idea. Please read [this q/a](https://stackoverflow.com/questions/10585478/one-dbcontext-per-web-request-why). To quote the answer: "... at the end of the request. Many people however, incorrectly assume that this is also the place to Commit the unit of work. However, at that point in the application, you simply can't determine for sure that the unit of work should actually be committed. e.g. If the business layer code threw an exception that was caught higher up the callstack, you definitely don't want to Commit.". – Steven May 11 '15 at 08:37
  • @CyrilDurand `b.RegisterType().InstancePerRequest();` @Steven I appreciate the intricacies and the above is a simplification of what I want to do. Thanks for the discussion though. – George Mauer May 11 '15 at 14:04
  • 1
    @GeorgeMauer it works on my machine. My code sample is here : http://pastebin.com/bSz8qnQ0 – Cyril Durand May 11 '15 at 15:20
  • Hmm @CyrilDurand is that running via `Microsoft.Owin.Host.SystemWeb` or as a console Owin application? – George Mauer May 11 '15 at 15:25
  • @GeorgeMauer nop. I try again and instead of using `DependencyResolver.Current` I used `ctx.GetAutofacLifetimeScope()` and it works – Cyril Durand May 11 '15 at 16:03

1 Answers1

1

Steven is right about the fact that you should not be committing on the request disposal because you cannot be sure if you really want to commit there, unless you abstract your UoW from DbContext and keep a success attribute there, checking if on disposal and conditionally commit.

For your specific question, there are two things to clarify.

  1. DbContext or UoW need to be registered with InstancePerRequest()
  2. Instead of using Owin middleware, you should use OnRelease(context => context.SaveMyChangesIfEverythingIsOk()) native Autofac API

For example, this is how it would look like for RavenDb

   builder.Register(x =>
        {
            var session = x.Resolve<IDocumentStore>().OpenAsyncSession();
            session.Advanced.UseOptimisticConcurrency = true;
            return session;
        })
            .As<IAsyncDocumentSession>()
            .InstancePerRequest()
            .OnRelease(x =>
            {
                x.SaveChangesAsync();
                x.Dispose();
            });
Alexey Zimarev
  • 17,944
  • 2
  • 55
  • 83
  • I see. Thanks Alexey. Do you know why that wouldn't work with Owin? presumably, if I register my middleware after the Autofac middleware it will get a chance to deal with the db before Autofac has a chance to dispose of it. Is this one of those weird things when using Owin with IIS? – George Mauer May 11 '15 at 14:07
  • 1
    I would in any case assume since Autofac is responsible for the dependency lifecycle, let it do this. Autofact is tracking when a request starts and ends and disposes the resolves/disposes dependencies accordingly. If you are registering some middleware, it has nothing to do with the container and the moment of disposal is not guaranteed. OnRelease is the native Autofac API and I would use that instead. You also will have no dependency on Owin since you might want to change it later to something like K. – Alexey Zimarev May 11 '15 at 17:18
  • I'm far more likely to want to change Autofac than Owin :) – George Mauer May 11 '15 at 18:21
  • AFAIK for ASP.NET vNext you will have to rewrite the middleware, however Autofac support will be more native thanks to BYOC strategy. Microsoft even implemented the first Autofac adapter for vNext themselves (being now replaced by the Autofac own adapter). So, I would argue about this. Also, I personally used ServiceStack on System.Net.Http and it was perfectly OK until I needed SignalR, I just installed Owin and SignalR but not really use its features but everything still works thanks to Autofac DI. – Alexey Zimarev May 11 '15 at 18:30