制作自定义IoC - 如何实现具有范围的DI?

时间:2010-06-30 16:11:10

标签: c# dependency-injection inversion-of-control autofac

我正在为自己的学习/成长编写一个IoC容器。通常我会写如下内容:

using(DisposableObject dispObj = new DisposableObject())
{
    UserRepository users = new UserRepository(dispObj);

    // Do stuff with user.
}

转向:

using(IDisposableObject dispObj = Container.Resolve<IDisposableObject>())
{
    IUserRepository users = Container.Resolve<IUserRepository>();

    // Do stuff with user.
}

如何在使用IoC时抽象DisposableObject,使其成为using范围内唯一使用的实例?我正在试图弄清楚Autofac是如何做到的,但我并不完全确定。

编辑:当使用using范围实例化对象时,所有解析该类型的调用(在本例中为IDisposableObject)都应该返回范围变量,而不是一个新的实例。同样重要的是,在using语句和另一个Resolve<IDisposableObject>被调用之后,它将返回 new 实例。

2 个答案:

答案 0 :(得分:2)

以这种方式使用一次性对象时要记住的事情是,容器中的引用也将被处理,因此理想情况下,当您通过Resolve&lt;&gt;返回实例时它每次都需要返回一个新实例。

您需要做的是,在使用容器注册类型时,允许它指定行为,Shared(Singleton)或NonShared,例如:

Container.RegisterType<IDisposableObject>(CreationPolicy.NonShared);

using (var disposable = Container.Resolve<IDisposableObject>()) {

}

以上内容适用于NonShared实例,因为每次都会创建一个新实例,因此我们可以安全地处理它。如果您使用CreationPolicy = Shared尝试了上述操作,则会丢弃Singleton,因此将来访问可能会导致ObjectDisposedException。

通过构建此行为,您可以通过传递CreationPolicy = Shared来创建Singleton实例,例如:

Container.RegisterType<IUserRepository>(CreationPolicy.Shared);

using (var disposable = Container.Resolve<IDisposableObject>()) {
    var userRepository = Container.Resolve<IUserRepository>();
    // only one instance of user repository is created and persisted by the container.
}

希望有帮助吗?

如果您以前使用过MEF,这个术语可能会很熟悉。

编辑:所以,我的理解是你要做的事情如下:

using (var repository = Container.Resolve<IUserRepository>())
{
  var other = Container.Resolve<IUserRepository>();
  // should resolve to the same instance.
}

您需要找到某种方法来监控容器中的一次性物体。也许引入一个额外的创建策略,SharedScope,例如:

Container.Register<IUserRepository, UserRepository>(CreationPolicy.SharedScope);

现在,当您使用容器解析类型时,您需要找出该项的CreationPolicy。如果该项目是SharedScope,并且尚未创建,则创建它的实例并返回。

如果解析了实例并且已经创建了该实例,则返回现有实例。

当在该项目上调用Dispose时,您需要某种方法来回调容器以删除该实例。

编辑两次

嗯,没有一种简单的方法可以解决这个问题。我能想到的唯一方法就是引入另一个界面:

public interface IMonitoredDisposable : IDisposable
{
  bool IsDisposed { get; set; }
}

处理对象时,请确保它设置IsDisposed属性。你可以从容器中监控该属性吗?

答案 1 :(得分:2)

您可以返回Owned<Foo>类型的对象,而不是Foo

Owned<T>可能如下所示:

public class Owned<T> : IDisposable
{
   private readonly Container container;
   private readonly T value;

   public Owned(Container container, T value)
   {
      this.container = container;
      this.value = value;
   }

   public T Value { get { return value; } }

   public void Dispose()
   {
      this.container.ReleaseOwned(this);
   }

}

现在,即使Owned<Foo>本身不是一次性的,拉出Foo的客户端也可以通过处理它来通知容器完成该对象。这样,容器知道何时清理对象及其依赖关系(也可能是一次性的,但对客户端是不可见的),并且您可以实现您所追求的行为。

Autofac做的事非常相似。请参阅Nicholas Blumhardt撰写的博客文章The Relationship Zoo,他将介绍这一概念。