Передача зависимости от Виндзорского замка в параллельную нить - проблема Dispose () - PullRequest
5 голосов
/ 20 марта 2011

Я использую ASP.NET MVC с Castle Windsor в качестве контейнера IoC с образом жизни компонента, установленным в PerWebRequest. Мой репозиторий (который является внедренной зависимостью) создает экземпляр ObjectContext Entity Framework в конструкторе, и я сохраняю его в закрытой переменной экземпляра. Мой репозиторий реализует IDisposable, и внутри моего метода Dispose я располагаю ObjectContext. Я думаю, что все это довольно стандартно, и вот упрощенная иллюстрация:

Repository:

 public class Repository : IRepository {

    private MyContext _dc; // MyContext inherits from ObjectContext

    public Repository() {
        _dc = new MyContext();
    }

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

Чтобы убедиться в отсутствии утечки памяти и вызове Dispose () моего репозитория, я переопределяю метод ReleaseController DefaultControllerFactory для освобождения контейнера Виндзора:

public class WindsorControllerFactory : DefaultControllerFactory {
        IWindsorContainer _container;

        public WindsorControllerFactory(IWindsorContainer container) {
            _container = container;
            // Do stuff to register all controller types
        }

        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) {
            // Do stuff to resolve dependency
        }

        public override void ReleaseController(IController controller) {
            // by releasing the container, Windsor will call my Dispose() method on my repository
            _container.Release(controller);
            base.ReleaseController(controller);
        }
    }

Я думаю, что все это довольно стандартно. Однако я хотел бы выделить параллельный поток, и внутри этого параллельного потока использовать зависимость IRepository. Моя проблема в том, что мой репозиторий уже будет удален к тому времени, когда я его использую:

public class HomeController : Controller {

    IRepository _repository;

    public HomeController(IRepository repository) {
        _repository = repository;
    }

    public ActionResult Index() {
        var c = _repository.GetCompany(34);

        new Task(() => {
            System.Threading.Thread.Sleep(2000); // simulate long running task
            // will throw an error because my repository (and therefore, ObjectContext) will have been disposed.
            var c2 = _repository.GetCompany(35);
        }).Start();

        return Content(c.Name, "text/plain");
    }
}

Как другие люди решают эту проблему? Как вы передаете свои зависимости параллельному потоку?

Заранее спасибо.

1 Ответ

4 голосов
/ 20 марта 2011

Создать новый экземпляр репозитория. ObjectContexts недорого построить.

ИЛИ, вы можете возложить ответственность за утилизацию репозитория на длительный поток. Я возился с этим, я думаю, что эти изменения в вашем коде решат вашу проблему:

В вашем WindsorControllerFactory

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        IController controller;
        // Do stuff to resolve dependency
        if(controller is LongTaskController)
        {
            ((LongTaskController) controller).CompleteLongTask += (sender, args) => _container.Release(controller);
        }
    }

    public override void ReleaseController(IController controller)
    {
        // by releasing the container, Windsor will call my Dispose() method on my repository
        if(!(controller is LongTaskController && ((LongTaskController)controller).HasLongTask)
        {
            _container.Release(controller);
        }
        base.ReleaseController(controller);
    }

В HomeController

public class HomeController : LongTaskController
{
    private readonly IRepository _repository;
    public HomeController(IRepository repository)
    {
        _repository = repository;
    }
    public ActionResult Index()
    {
        var c = _repository.GetCompany(34);
        DoLongTask(() =>
        {
            Thread.Sleep(200);
            var c2 = _repository.GetCompany(35);
        });
        return Content(c.Name, "text/plain");
    }
}

И новый базовый контроллер

public abstract class LongTaskController: Controller,IHasLongTask
{
    private bool _hasLongTask;
    public bool HasLongTask { get { return _hasLongTask; } }
    public event EventHandler CompleteLongTask;
    void IHasLongTask.DoLongTask(Action action) { DoLongTask(action); }
    protected void DoLongTask(Action action)
    {
        _hasLongTask = true;
        if (CompleteLongTask == null)
        {
            throw new NullReferenceException("Controller.CompleteLongTask cannot be null when Controller does a long running task.");
            action += () => CompleteLongTask(this, EventArgs.Empty);
        }
        new Task(action).Start();
    }
}
public interface IHasLongTask
{
    bool HasLongTask { get; }
    void DoLongTask(Action action);
    event EventHandler CompleteLongTask;
}
...