【问题标题】:Making Custom IoC - How to Implement DI That Has Scope?制作自定义 IoC - 如何实现具有范围的 DI?
【发布时间】:2010-07-01 09:01:50
【问题描述】:

我正在为自己的学习/成长编写一个 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&lt;IDisposableObject&gt; 被调用之后,它会返回一个 new 实例。

【问题讨论】:

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


    【解决方案1】:

    以这种方式使用一次性对象时要记住的是,容器中的引用也会被释放,所以理想情况下,当您通过 Resolve 返回一个实例时,每次都需要返回一个新实例。

    您需要做的是,在向容器注册类型时,允许它指定行为,共享(单例)或非共享,例如:

    Container.RegisterType<IDisposableObject>(CreationPolicy.NonShared);
    
    using (var disposable = Container.Resolve<IDisposableObject>()) {
    
    }
    

    上述方法适用于非共享实例,因为每次都会创建一个新实例,因此我们可以安全地处理它。如果您尝试使用 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 属性。然后您可以从您的容器中监控该属性吗?

    【讨论】:

    • 那种答案是我正在寻找的。我知道我需要传递某种“范围”。我只想知道如何从我的字典中删除单例,如果它已被处理?我想到的一种方法是为每个“范围”using(var localContainer = IoC.Create()) 创建一个新容器,然后调用localContainer.Resolve&lt;T&gt;() 进行本地解析...
    • 好吧,你不会处理单身人士,否则你将无法再次归还它们。如果你的 Container 有一个内部的 Singleton 哈希表,请将它们存储在你可以轻松再次获取它们的地方。如果要解析的类型未声明为 Singleton,则只需通过 Resolve 返回它而不存储它。使用具有 Disposable 模式问题的容器并不是一个好办法,因为您每次都必须设置和创建容器......
    • 所以如果你不处理单例,它们怎么能特定于using 范围?哈希表中的键总是会命中相同的单例(已处理或未处理),因为它没有被删除。我认为你错过了一个问题,如果我在 using 语句中调用 Container.Resolve() inside,它应该引用从 using 范围创建的那个。
    • Singletons 被设计为单个实例。如果您丢弃它们,它们将无法再次使用。这是因为您不会将它们与 using 块一起使用。
    • 这正是我仍然想知道的“对处置的监控”。我怎么能去监控一些东西,直到它被处理掉......
    【解决方案2】:

    您可以返回 Owned&lt;Foo&gt; 类型的对象,而不是 Foo

    Owned&lt;T&gt; 可能如下所示:

    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&lt;Foo&gt; 的客户端可以通过释放对象来通知容器,即使Foo 本身不是一次性的。这样,容器就知道何时清理对象及其依赖项(也可能是一次性的,但对客户端不可见),并且您可以实现您所追求的行为。

    Autofac 做了非常相似的事情。请参阅 The Relationship Zoo,Nicholas Blumhardt 的博客文章,他在其中介绍了这一概念。

    【讨论】:

    • 昨晚很晚才看到你的帖子,我最终找到了相同的链接。唯一的问题是不是 Resolve() 然后返回 Owned 而不是 T?那么,我的代码是不是总是必须依赖 using 语句中的 Owned 和 own.Value 来获得实际的 T?
    • 调用代码知道它是否想要所有权,因此它可以根据需要调用Resolve&lt;Owned&lt;Foo&gt;&gt;()Resolve&lt;Foo&gt;。或者,如果您不喜欢根据请求类型更改语义,您可以使用单独的 Resolve&lt;T&gt;ResolveOwned&lt;T&gt; 方法。
    • 好吧,如果我真的需要一些无法通知它已被处置的东西,那这很有意义。
    猜你喜欢
    • 2015-07-07
    • 1970-01-01
    • 1970-01-01
    • 2015-10-09
    • 2011-12-05
    • 1970-01-01
    • 1970-01-01
    • 2019-04-04
    • 1970-01-01
    相关资源
    最近更新 更多