2009-02-17 49 views
7

我有一个问题,我打算标记这个主观因为这是我认为它演变成更多的讨论。我希望有一些好的想法或者一些思想敏捷的人。我对这个冗长的问题表示歉意,但你需要知道上下文。ServiceContainer,IoC和一次性物品

的问题基本上是:

  • 如何在关系处理的具体类型IoC容器?具体而言,谁负责处理它们,如果需要处置,以及这些知识如何传播到调用代码?

您是否要求他们是IDisposable?如果不是,那么这个代码是未来的证明,还是你不能使用一次性对象的规则?如果您对接口和具体类型强制实施IDisposable-requirements以确保未来不会受到影响,其责任是作为构造函数调用的一部分注入的对象?


编辑:我接受了答案通过@Chris Ballard因为它是最接近的一个,我们结束了与方法。

基本上,我们总是返回一个类型,看起来像这样:

public interface IService<T> : IDisposable 
    where T: class 
{ 
    T Instance { get; } 
    Boolean Success { get; } 
    String FailureMessage { get; } // in case Success=false 
} 

然后,我们从两个.Resolve和.TryResolve返回一个对象实现了这个接口回来,让我们在调用代码得到的是总是相同的类型。

现在,实现此接口的对象IService<T>是IDisposable,并且应该总是被丢弃。这不取决于程序员决定是否应该处置对象IService<T>

但是,这是至关重要的部分,服务实例是否应该被处置,知识是否被烘焙到实现IService<T>的对象中,所以如果它是一个工厂范围的服务(即每次调用Resolve都会结束使用新的服务实例),那么服务实例将在处理对象时丢弃。

这也使得它可以支持其他特殊的范围,如池。现在我们可以说我们需要最少2个服务实例,最多15个,通常是5个,这意味着每次调用.Resolve都将从可用对象池中检索服务实例,或者构建一个新实例。然后,当处理存储池服务的对象IService<T>被丢弃时,服务实例被释放回其池中。

当然,这让所有的代码是这样的:

using (var service = ServiceContainer.Global.Resolve<ISomeService>()) 
{ 
    service.Instance.DoSomething(); 
} 

但它是一个干净的方式,它具有相同的语法,无论服务还是在使用具体对象的类型,所以我们选择了为可接受的解决方案


原题如下,供后人


啰嗦问题来了这里:

我们有我们使用IoC容器,最近我们发现数额是多少到一个问题。

在非IoC的代码,当我们想用,比如说,一个文件,我们使用这样的类:

using (Stream stream = new FileStream(...)) 
{ 
    ... 
} 

有作为这一类是否是一些持有有限毫无疑问资源与否,因为我们知道文件必须关闭,并且类本身实现了IDisposable。规则就是我们构造一个实现IDisposable的对象的每个类都必须被丢弃。无话可问。这不是由该类的用户决定是否调用Dispose是可选的。

好吧,继续走向IoC容器的第一步。假设我们不希望代码直接与文件交谈,而是要经过一层间接寻址。在这个例子中,我们称这个类为BinaryDataProvider。在内部,类使用的流,这仍然是一个一次性对象,所以上面的代码将被更改为:

using (BinaryDataProvider provider = new BinaryDataProvider(...)) 
{ 
    ... 
} 

这并没有太大变化。类实现IDisposable的知识仍然存在,没有问题,我们需要调用Dispose。

但是,让我们假设我们有类提供目前不使用任何有限资源的数据。

上面的代码然后可以写为:

BinaryDataProvider provider = new BinaryDataProvider(); 
... 

OK,到目前为止好,但问题来了的肉。假设我们想使用IoC容器来注入这个提供者,而不是依赖于特定的具体类型。

的代码将被:

IBinaryDataProvider provider = 
    ServiceContainer.Global.Resolve<IBinaryDataProvider>(); 
... 

请注意,我假设有可用的,我们可以通过访问该对象的独立接口。

随着上述变化,如果我们稍后想要使用真正应该处置的对象呢?没有解决该接口的现有代码被写入来处理该对象,那么现在呢?

我们看到它的方式,我们必须选择一个解决方案:

  • 实现运行时检查来检查,如果正在注册一个具体类型实现IDisposable,要求该接口是通过还器具暴露IDisposable接口。这不是一个很好的解决方案被用于
  • enfore表示对接口的限制,他们必须自IDisposable继承,为了成为面向未来的
  • 强制运行时,它没有具体类型可以是IDisposable的,因为这是专门不由代码使用IoC容器处理
  • 只需让程序员检查对象是否实现了IDisposable和“做正确的事情”?
  • 有其他吗?

另外,如何在构造函数中注入对象?我们的容器和我们研究过的其他一些容器能够将新的对象注入到具体类型的构造函数的参数中。例如,如果我们的BinaryDataProvider需要一个实现接口的对象,那么如果我们对这些对象执行IDispose-“ability”,这些对象的职责是处理日志记录对象?

您认为如何?我想要好的和坏的意见。

回答

2

(声明:我是基于java的东西回答这个虽然我编写C#我还没有代理在C#什么,但我知道这是可能有关Java术语对不起。)

你可以让国际奥委会框架检查正在构建的对象,以查看它是否支持ID为一次性的 。否则,您可以使用动态代理来将IoC框架提供的实际对象包装到客户端代码中。这个动态代理可以实现IDisposable,这样你就可以随时向客户端提供一个IDisposable。只要你使用的接口应该相当简单?

然后,您只需要向开发人员传达问题时对象是IDisposable。我不确定如何以良好的方式完成这件事。

3

一个选择是去同一个工厂模式,所以直接由IoC容器永远不需要创建的对象被布置自身,例如

IBinaryDataProviderFactory factory = 
    ServiceContainer.Global.Resolve<IBinaryDataProviderFactory>(); 
using(IBinaryDataProvider provider = factory.CreateProvider()) 
{ 
    ... 
} 

缺点是增加了复杂性,但它确实意味着容器永远不会创建开发人员应该处理的任何东西 - 它始终是这样做的明确代码。

如果你真的想使它明显,工厂方法可以被命名为CreateDisposableProvider()。

+0

IoC容器的要点是我稍后通过配置可以返回不同的对象。在你的例子中,每个这样的对象都必须实现IDisposable。我的问题是:这是否是这些对象的标准?我可以在某种程度上指定这个标准,还是每个界面?面向未来的? – 2009-02-17 13:27:29

+0

在这个例子中,我可以返回工厂的不同实现,但每个实现都必须创建一个实现IDisposable的提供者。这并不理想,但至少可以为您提供关于语义的线索。也许这不是你问的问题? – 2009-02-17 13:53:48

1

你实际上想出了一个非常肮脏的解决方案:你的IService合同违反了SRP,这是一个很大的禁忌。

我推荐的是将所谓的“单件”服务与所谓的“原型”服务区分开来。 “singleton”的生命周期由容器管理,容器可以在运行时查询特定实例是否实现IDisposable,如果是,则在关闭时调用Dispose()

另一方面,管理原型完全是调用代码的责任。