在 Ninject 中,在单例范围内声明绑定意味着每次都会返回相同的对象。永远只能有一个对象。
我想要的是一次返回一个对象。换句话说:
- 第一次调用 Get() 实例化一个新对象并返回它。
- 对 Get() 的后续调用返回相同的实例。
- 该对象已被处置。
- 对象被释放后对 Get() 的第一次调用会实例化一个新的/第二个对象并返回该对象。
- 对 Get() 的后续调用返回在步骤 4 中创建的对象。
编辑: 使用 providers 并让相关对象在处置时引发事件。我很好奇是否有办法使用 Ninject 中的范围来做到这一点,并将这个问题留在这里,因为 Steven 的回答非常好。
In Ninject, declaring a binding in singleton scope means that the same object will be returned every time. There can only be one object, ever.
What I would like is to return one object at a time. In other words:
- The first call to Get() instantiates a new object and returns it.
- Subsequent calls to Get() return the same instance.
- The object is disposed.
- The first call to Get() after the object was disposed instantiates a new/second object and returns that.
- Subsequent calls to Get() return the object created in step 4.
EDIT: This problem is actually rather simple to solve using using providers and having the object in question raise an event when disposed. I was curious if there was a way to do this using scopes in Ninject, and will leave this question here because Steven's answer is excellent.
发布评论
评论(2)
由于您想在多线程应用程序中使用此构造,并希望跨线程重用同一实例(正如您在评论中暗示的那样),因此您将无法通过配置 DI 容器来解决此问题。
由于竞争条件,您根本无法将对象配置为在处置后更新。想象一下以下场景:
Dispose
。问题是应用程序将获得对可以释放的实例的引用。
如果可以的话,尝试通过重新设计您的应用程序来防止这种情况发生。公开实现 IDisposable 的服务类型是一种不好的做法,因为 IDisposable 是一个有漏洞的抽象。我个人的偏好甚至是阻止这些服务的任何实现来实现 IDisposable。在大多数情况下,重新设计可以让您不必这样做。
如果您需要使用
IDisposable
对象,通常的方法是创建并注入创建这些IDisposable
对象的工厂。这样,消费者就可以安全地处置此类物品,没有任何问题。这里的普遍问题是很难创建实现 IDisposable 的对象,而这些对象实际上是线程安全的。
如果你真的想要这个,你可以尝试创建一个执行引用计数的装饰器。例如,看看下面的装饰器。它包装了
IService
并实现了IService
。IService
实现IDisposable
。装饰器采用Func> 。允许创建实例的委托。对象的创建和处置受到
lock
语句的保护,并且装饰器会计算调用者对其的引用。在最后一个消费者处置装饰器之后,它将处置该对象并创建一个新对象。请注意,由于以下原因,此实现仍然存在缺陷:
Dispose
会破坏装饰器。SomeOperation
(或者IService
有多个方法)时,实现将会中断。创建一个按预期工作的装饰器是相当困难的。执行此操作的一种简单方法是序列化对对象的访问,但是当您执行此操作时,您可能希望每个线程使用单个实例。那会容易得多。
我希望这有帮助。
Since you want to use this construct in a multi-threaded application and want to reuse the same instance across threads (as you imply in your comment), you will not be able to solve this problem by configuring your DI container.
You simply can't configure the object to be renewed after disposal, because of race conditions. Imagine the following scenario:
Dispose
.The problem is that the application will get a reference to an instance that can be disposed.
Try to prevent doing this by redesigning your application if you can. It's a bad practice to expose service types that implement
IDisposable
, becauseIDisposable
is a leaky abstraction. My personal preference is even to prevent any implementations of these services to implementIDisposable
. In most scenarios a redesign can prevent you from having to do this.If you need to use
IDisposable
objects, the usual way to do this is to create and inject factories that create theseIDisposable
objects. This way the consumer can safely dispose such an object, without any problem.The general problem here is that it is hard to create objects that implement
IDisposable
, that are actually thread-safe.If you really want this, you can try creating a decorator that does reference counting. Look for instance at the decorator below. It wraps an
IService
and implementsIService
.IService
implementsIDisposable
. The decorator takes aFunc<IService
> delegate that allows creation of instances. Creation and disposal of objects is protected by alock
statement and the and the decorator counts the references to it by callers. It will dispose the object and create a new one, after the last consumer disposed the decorator.Please note that this implementation is still flawed, because of the following reasons:
Dispose
multiple times breaks the decorator.SomeOperation
multiple times (or theIService
has multiple methods) the implementation will break.It is pretty hard to create a decorator that functions as expected. One simple way of doing this is by serializing access to the object, but when you do this, you probably want to use a single instance per thread. That would be much easier.
I hope this helps.
我知道这个问题已经解决了,但是...@Steven 的回答并没有指出 Ninject 中有一个 InScope 机制可以解决您正在寻找的内容的各个方面。
看看 Nate Kohari 的 缓存和收集文章介绍如何在 Ninject 2 中完成范围界定。
接下来,请看一下在 ninject 源代码中查看 InRequestScope 是如何实现的(包括如何挂钩拆解)。 2.3-4 计划进行一些工作来概括其工作原理,以允许其用于某些复杂的托管场景。
当您查看这两个参考资料后,请在 ninject 邮件列表上提出问题,您一定会找到解决方案。
I know this is solved but... @Steven's answer doesnt point out that there's a
InScope
mechanism in Ninject that addresses aspects of what you're looking for.Have a look at Nate Kohari's Cache and Collect article re how scoping can be done in Ninject 2.
Next, go look at the ninject source and see how InRequestScope is implemented (including how the teardown is hooked in). There's some work planned for 2.3-4 to generalise how that works to permit it to be used for some complex hosting scenarios.
When you've looked at these two references, go ask a question on the ninject mailing list and you'll definitely have a solution.