Ninject在处置使用它的父对象之前处置子对象

  • 本文关键字:对象 Ninject c# ninject
  • 更新时间 :
  • 英文 :


当我在DataTest的[TestCleanup]方法中调用IKernel.Dispose()时,我的应用程序崩溃了,因为要处置的第一个对象仍在被另一个对象使用。

下面的代码重现了这个问题:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Ninject;
using Ninject.Modules;
[TestClass]
public class NinjectDisposeIssue
{
private IKernel kernel;
[TestInitialize]
public void TestInitialize()
{
kernel = new StandardKernel(new Module());
}
[TestCleanup]
public void TestCleanup()
{
kernel.Dispose();
}
[DataTestMethod]
[DataRow]
[DataRow]
public void DataTestMethod()
{
var parent = kernel.Get<Parent>();
parent.Run();
}
}
public class Module : NinjectModule
{
public override void Load()
{
Bind<Parent>().ToSelf().InSingletonScope();
Bind<Service>().ToSelf().InSingletonScope();
Bind<Database>().ToSelf().InSingletonScope();
// create 2 workers
Bind<Worker>().ToSelf().InSingletonScope();
Bind<Worker>().ToSelf().InSingletonScope();
}
}
public class Parent
{
private readonly Service service;
private readonly IEnumerable<Worker> workers;
public Parent(Service service, IEnumerable<Worker> workers)
{
this.service = service;
this.workers = workers;
}
public void Run()
{
for (var i = 0; i < 2; i++)
{
foreach (var worker in workers)
{
worker.StartWork();
}
service.DoSomething();
}
}
}
public class Service
{
private readonly Database database;
public Service(Database database)
{
this.database = database;
}
public void DoSomething()
{
var value = database.Get();
// do something with the value
}
}
public class Worker : IDisposable
{
private readonly Database database;
private Task workTask;
private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
private bool disposedValue;
public Worker(Database database)
{
this.database = database;
}
public void StartWork()
{
if (workTask == null)
{
workTask = Task.Run(WorkLoop);
}
}
private void WorkLoop()
{
while (!cancellationTokenSource.Token.IsCancellationRequested)
{
var value = database.Get();
// do some work with the value...
}
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
cancellationTokenSource.Cancel();
workTask.Wait();
workTask.Dispose();
}
disposedValue = true;
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
public class Database : IDisposable
{
private readonly ThirdPartyDb thirdPartyDb = new ThirdPartyDb();
private bool disposedValue;
public string Get()
{
return thirdPartyDb.Get("foo");
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
thirdPartyDb.Dispose();
}
disposedValue = true;
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
public class ThirdPartyDb : IDisposable
{
private bool isDisposed = false;
public void Dispose()
{
isDisposed = true;
}
public string Get(string key)
{
if (isDisposed)
{
throw new ObjectDisposedException(nameof(ThirdPartyDb));
}
return "bar";
}
}

Database对象的Dispose()方法在2个Worker对象的Dispose()方法之前被调用。

问题:我可以在绑定中做些什么来强制Workers在Database之前被处理,或者我配置这个的方式从根本上是错误的?

作为一个简单的解决方案,您可以在Database中添加另一个CancellationTokenSource。这样,您将确保在处置之后不会调用thirdPartyDb

public class Database : IDisposable
{
private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
private readonly ThirdPartyDb thirdPartyDb = new ThirdPartyDb();
private bool disposedValue;
public string Get()
{
if (cancellationTokenSource.Token.IsCancellationRequested) return ""; //Return anything acceptable for you
return thirdPartyDb.Get("foo");
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
cancellationTokenSource.Cancel();
thirdPartyDb.Dispose();
}
disposedValue = true;
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}

最新更新