How to correctly dispose objects registered with Autofac

╄→尐↘猪︶ㄣ 提交于 2019-12-08 11:58:41

问题


I've implemented Unit of Work/Repository pattern, as described here, but I'm also using autofac and constructor injection, so I registered UnitOfWork and DbContext (PsyProfContext) class like this:

 builder.Register(context => new PsyProfContext()).InstancePerHttpRequest();
 builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerHttpRequest();

And everything works great!

Except for one thing: I'm also using enterprise library logging block, and I have implemented CustomTraceListener which is using Entity Framework to write log entry into the database.

My controller looks like this (it is empty because at the moment I just tried to verify if all the things (IoC, logging, entity framework) are working):

public class HomeController : Controller
{
    private readonly UnitOfWork unitOfWork;

    public HomeController(IUnitOfWork unitOfWork)
    {
        this.unitOfWork = (UnitOfWork) unitOfWork;
    }

    //
    // GET: /Home/
    public ActionResult Index()
    {
        throw new HttpException();
        return View();
    }

    protected override void Dispose(bool disposing)
    {
        unitOfWork.Dispose();
        base.Dispose(disposing);
    }
}

And in the Write method of the CustomTraceListener class, I've tried to Resolve UnitOfWork:

DependencyResolver.Current.GetService<IUnitOfWork>() as UnitOfWork;

But I get an instance which is already disposed! so I've put some breakpoints and found out that Dispose method of the controller is called before the Write method of the CustomTraceListener class, so in the end I didn't found other solution than using DbContext (PsyProfContext) directly:

public override void Write(object o)
    {
        using (var conext = new PsyProfContext())
        {
            var customLogEntry = o as CustomLogEntry;

            if (customLogEntry != null)
            {
                var logEntry = new LogEntry
                                   {
                                       //a bunch of properties
                                   };

                conext.Exceptions.Add(logEntry);
                conext.SaveChanges();
            }
        }
    }

But I don't like this solution! What's the point to use UnitOfWork and Repository pattern if you access DbContext object directly. Or what's the point in using DI in project if you create a registered object manually in some cases.

So I wanted to hear your opinion, about how to deal with this kind of situations? Is my current implementation fine, or it is definitely wrong and I should think about another one.

Any help will be greatly appreciated and any ideas are welcome!


回答1:


It looks like you may have a couple of problems.

First, if you're manually disposing the unit of work object in your controller, your controller should take an Owned<IUnitOfWork> in the constructor. When the request lifetime is disposed it will automatically dispose of any IDisposable components - including the controller and any resolved dependencies - unless you specify somehow that you're going to take over ownership of the lifetime. You can do that by using Owned<T>.

public class HomeController : Controller
{
  Owned<IUnitOfWork> _uow;
  public HomeController(Owned<IUnitOfWork> uow)
  {
    this._uow = uow;
  }
  protected override void Dispose(bool disposing)
  {
    if(disposing)
    {
      this._uow.Dispose();
    }
    base.Dispose(disposing);
  }
}

(Note a minor logic fix in the Dispose override there - you need to check the value of disposing so you don't double-dispose your unit of work.)

Alternatively, you could register your units of work as ExternallyOwned, like

builder
  .RegisterType<UnitOfWork>()
  .As<IUnitOfWork>()
  .ExternallyOwned()
  .InstancePerHttpRequest();

ExternallyOwned also tells Autofac that you'll take control of disposal. In that case, your controller will look like it does already. (Generally I like to just let Autofac do the work, though, and not take ownership if I can avoid it.)

In fact, looking at the way things are set up, you might be able to avoid the disposal problem altogether if you let Autofac do the disposal for you - the call to DependencyResolver would return the unit of work that isn't disposed yet and it'd be OK.

If that doesn't fix it... you may want to add some detail to your question. I see where your controller is using the unit of work class, but I don't see where it logs anything, nor do I see anything in the listener implementation that's using the unit of work.

(Also, as noted in the first comment on your question, in the constructor of your controller you shouldn't be casting your service from IUnitOfWork to UnitOfWork - that's breaking the abstraction that the interface was offering in the first place.)



来源:https://stackoverflow.com/questions/15741417/how-to-correctly-dispose-objects-registered-with-autofac

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!