将Windsor Castle注入依赖关系传递给并行线程 - Dispose()问题

时间:2011-03-20 04:03:51

标签: c# asp.net-mvc dependency-injection castle-windsor ioc-container

我正在使用带有Castle Windsor的ASP.NET MVC作为我的IoC容器,其组件生活方式设置为PerWebRequest。我的存储库(它是注入的依赖项)在构造函数中创建了Entity Framework的ObjectContext实例,并将其存储在私有实例变量中。我的存储库实现了IDisposable,在我的Dispose方法中,我处理了ObjectContext。我认为所有这些都是非常标准的,这是一个简化的例子:

存储库:

 public class Repository : IRepository {

    private MyContext _dc; // MyContext inherits from ObjectContext

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

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

为了确保没有内存泄漏并且我的Repository的Dispose()被调用,我覆盖DefaultControllerFactory的ReleaseController方法来释放Windsor的容器:

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 个答案:

答案 0 :(得分:4)

创建一个新的Repository实例。 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;
}