1

Plain and simple as the title suggests, is this a possible thing using Autofac dependency injection? I have been trying and searching everywhere.. I'm losing hope here.

My class has to be singleton - that can't change. I want it to take a factory of unit of works - for database transactions.

Please help me figure this one out, I'm deseperate.

Tried Func<> and registering my unit of work in every possible way (all sorts of lifetimes, externally owned or not) but failed because the DbContext within the unit of work is disposed and not created again after the first request

Edit:

Added code that will hopefully help understanding my problem:

public class SingletonDataService : IDataService
{
    private _uowFactory;

    public SingletonDataService(Func<IEFUnitOfWork> uowFactory)
    {
        _uowFactory = uowFactory
    }

    public List<Folder> GetAllFolders ()
    {
        using (uow = uowFactory())
        {
            return uow.FoldersRepository.GetAll();
        }
    }
}

public MyDbContext : DbContext
{
    public DbSet<Folder> Folders {get; set;}

    public DbSet<Letter> Letters {get; set;}

    public MyDbContext() : base("myContext...")
}

public EFUnitOfWork : IEFUnitOfWork, IDisposable
{
    public IRepository<Folder> FoldersRepository;
    public IRepository<Letter> LettersRepository;

    private DbContext _context;

    public EFUnitOfWork(IRepository<Folder> folders, IRepository<Letter> letters, DbContext context)
    {
        _folders = folders;
        _letters = letters;
        _context = context;
    }

    private bool disposed = false;

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                _context.Dispose();
            }

            disposed = true;
        }
    }
}

public Repository<T> : IRepository<T> where T: BaseEntity
{
    private DbContext _context;
    private DbSet<T> _set

    public Repository(DbContext context)
    {
        _context = context;
        _set = _context.Set<T>();
    }
}


public LettersController : ApiController
{
    private IDataService _dataService;

    public LettersController(IDataService dataService)
    {
        _dataService = dataService;
    }

    [HttpGet]
    public IHttpActionResult GetAllLetters()
    {
        return Ok(_dataService.GetAllLetters());
    }
}


// Must be singleton
builder.Register<SingletonDataService>().As(IDataService).SingleInstance();

builder.RegisterGeneric(typeof(Repository<>))
       .As(typeof(IRepository<>))
       .InstancePerLifetimeScope();

builder.Register<EFUnitOfWork>().As(IEFUnitOfWork).InstancePerLifetimeScope();

builder.Register<DbContext>().As(AppDbContext).InstancePerLifetimeScope();

In the first request everything works fine, in the second third and so on I get this exception :

system.ObjectDisposedException: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.

I clearly see it happens because the context in my repository is now null, but I don't how to to change that with the DI.

What I want to achieve is so easy without DI :

How can I achieve the following with Autofac??

public class UowFactory
{
    public UowFactory()
    {

    }

    public IEFUnitOfWork Create()
    {
        var context = new AppDbContext()
        var uow = new EFUnitOfWork(new Repository<Folder>(context), new Repository<Letter>(context), context);
        return uow;
    }
}
  • what lifetime did you register the `DbContext` with? – qujck Mar 01 '16 at 17:00
  • I tried everything.. PerLifetimeScope, PerRequest, PerOwned.. Nothing seems to be working –  Mar 01 '16 at 17:02
  • you'll need to show some code, something that demonstrates the failure. – qujck Mar 01 '16 at 17:03
  • Edited and added my code. –  Mar 01 '16 at 17:18
  • Great, please also include the full exception with the call stack – qujck Mar 01 '16 at 17:21
  • Added the exception. I can not provide the call stack, it is clearly thrown because no context is injected the from the second time and so on –  Mar 01 '16 at 17:28
  • 1
    you shouldn't do `using (uow = uowFactory())` - this is the line that is disposing of the uow rather than leaving it to the container to decide. Change that line to `uow = uowFactory();` – qujck Mar 01 '16 at 17:37
  • I tried it and it behaves the same.. I forgot to mention that I register the Cotnext as ExternallyOwned.. meaning I need to take care of the disposal –  Mar 01 '16 at 17:42
  • So where is it getting disposed now that you have removed the `using`? and how does the container know when you have disposed of it? This is your issue, taking the control from the container. If you want to control the lifetime then you should supply the correct instance to the container each time the container needs to resolve the type. – qujck Mar 01 '16 at 17:44
  • I'm pretty sure I tried it also letting the container handle disposal and it didn't work as well, but I'll be able to confirm it only tomorrow. From what I understood in all my try outs, the factory is creating each Unit of work in the root container since it's in a singleton. It holds reference to the context, and after it is disposed first by the container, it later on uses a null context because components resolved from the root holds to their dependencies for the container lifetime.. –  Mar 01 '16 at 18:11
  • It's not working, the context is never disposed that way, meaning I'm working with a single context which I don't want –  Mar 02 '16 at 07:17
  • What lifetime do you want for the context? You need to configure the container so that it recycles as you would expect, e.g. PerLifetimeScope or PerRequest. – qujck Mar 02 '16 at 07:20
  • My registrations are described in my question above. I want to acheive functionality similar to the UowFactory in the lower section of my question –  Mar 02 '16 at 07:30
  • I see, you are using `InstancePerLifetimeScope` - you will need to use `container.BeginLifetimeScope()` to manage the lifetime of the context as documented [here](http://docs.autofac.org/en/latest/lifetime/instance-scope.html#instance-per-lifetime-scope) – qujck Mar 02 '16 at 08:28
  • But the container is only constructed at the root.. How can I create new lifetime scopes manually? Isn't it bad practice? –  Mar 02 '16 at 09:29

2 Answers2

1

Your problem is that you have a singleton (SingletonDataService) depend on a service that has a shorter lifetime (EFUnitOfWork). When the SingletonDataService instance is created by autofac, it gets an instance of EFUnitOfWork, but this instance will always stay the same (its actual lifetime will be longer than you expect) and thus gets disposed and used again, giving errors.

You have two possible solutions:

One is to create a UowFactory class like the one you defined at the bottom (but with dependencies on IRepository<Folder>, IRepository<Letter>, DbContext), register that as anything (for example singleton, but it won't matter) and make the SingletonDataService depend on it. This will likely not be a viable solution to you though, since it will also extend the lifetime of the IRepository<Folder>, IRepository<Letter>, DbContext instances and create problems there.

The proper solution is to remove the reason why you would want the SingletonDataService to be a singleton, probably some cache. And move it to a new service (CacheService?) on which the SingletonDataService depends and make that new service a singleton.

mnwsmit
  • 1,198
  • 2
  • 15
  • 31
  • The true reason for it to be singleton is that it is also created inside a singleton and so on. I can not change the whole architecture and change all singletons from the root –  Mar 02 '16 at 09:20
  • 1
    @S.Peter the other answer by BatteryBackupUnit will work for you. I did not know about the Owned of autofac. – mnwsmit Mar 02 '16 at 12:18
1

Issue

You are registering the critial components with InstancePerLifetimeScope()

When the autofac container is built it also creates a root ILifetimeScope which lives until IContainer.Dispose() is called. Now unless you create nested ILifetimeScope somewhere in the chain to the SingletonDataService, the ILifetimeScope which is used your components is the root ILifetimeScope and InstancePerLifetimeScope() effectly becomes equivalent to SingleInstance().

Solution

One of the possible solutions is to create an ILifetimeScope per IEFUnitOfWork and its children. Autofac facilitates this by providing the Owned<T> type. We can use it in conjunction with a Func<> factory (also see documentation):

public class SingletonDataService : IDataService
{
    private Func<Owned<IEFUnitOfWork>> _uowFactory;

    public SingletonDataService(Func<Owned<IEFUnitOfWork>> uowFactory)
    {
        _uowFactory = uowFactory
    }

    public List<Folder> GetAllFolders ()
    {
        using (var uow = _uowFactory())
        {
            return uow.Value.FoldersRepository.GetAll();
        }
    }
}

This should play nicely with the following registrations:

// Must be singleton
builder.Register<SingletonDataService>().As(IDataService).SingleInstance();

builder.RegisterGeneric(typeof(Repository<>))
       .As(typeof(IRepository<>))
       .InstancePerLifetimeScope();

builder.Register<EFUnitOfWork>().As(IEFUnitOfWork).InstancePerLifetimeScope();

builder.Register<DbContext>().As(AppDbContext).InstancePerLifetimeScope();

However, note, that DbContext being bound with InstancePerLifetimeScope basically makes the manual disposal in EFUnitOfWork redundant.

Sidenote on proper Disposal

Since IDisposable types should support graceful multi-disposal , one should be able to simplify EFUnitOfWork.Dispose() to

public void Dispose()
{
    _context.Dispose();   
}

Also note, that i left out the call GC.SuppressFinalize(this);. This call is only relevant in case the class implements a custom finalizer (~EFUnitOfWork method) - or a deriving class could do so, otherwise the object is not put on the finalizer queue anyway.

BatteryBackupUnit
  • 12,934
  • 1
  • 42
  • 68
  • Thank you so much! I'm so glad I could finally manage through, I almost gave up!! I abstracted Autofac.Owned with my own and registered my own abstraction as externally owned and it works perfectly!!!!!!!! I would like another explaination in about why disposal in my case should only be _context.dispose(); I didn't quite get it. –  Mar 02 '16 at 13:00
  • See [here](http://www.blackwasp.co.uk/IDisposable.aspx) - note that it contains *performance* improvements which are *not necessary* unless you do have performance issues. Also please don't forget to vote / accept the answer ;-) – BatteryBackupUnit Mar 02 '16 at 13:10
  • What do you mean by managed or unmanaged? What is the difference? And of course I will upvote! If I could I would upvote a million times!! –  Mar 02 '16 at 13:13
  • 1
    Sorry, in the meantime i've edited my previous comment to provide a link which explains things more extensively. However, it's a "complicated" topic so you might want to see [here](http://stackoverflow.com/questions/3607213/what-is-meant-by-managed-vs-unmanaged-resources-in-net) and [here](https://msdn.microsoft.com/en-us/library/498928w2%28v=vs.110%29.aspx) as well.. consider though that if you want to learn about this and understand it properly you should expect investing several hours! – BatteryBackupUnit Mar 02 '16 at 13:17