如何创建一个Ninject自定义范围,该范围返回相同的对象,直到该对象被处置? [英] How to create a Ninject custom scope that returns the same object until that object is disposed?

查看:58
本文介绍了如何创建一个Ninject自定义范围,该范围返回相同的对象,直到该对象被处置?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Ninject中,在单例作用域中声明绑定意味着每次都将返回相同的对象.永远只有一个物体.

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:

  1. 第一次调用Get()会实例化一个新对象并返回它.
  2. 随后对Get()的调用将返回相同的实例.
  3. 对象已处置.
  4. 对象处置后对Get()的第一次调用实例化了一个新对象/第二个对象,并返回该对象.
  5. 随后对Get()的调用将返回在步骤4中创建的对象.

使用

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.

推荐答案

由于您要在多线程应用程序中使用此构造,并且希望在线程中重用同一实例(如您在注释中所暗示),因此您将无法通过配置DI容器来解决此问题.

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:

  1. 线程1向容器请求实例.
  2. 这是第一个请求,容器将创建一个新实例.
  3. 线程2向容器请求实例
  4. 容器返回在步骤2中创建的实例.
  5. 线程1由实例完成,并调用Dispose.
  6. 线程2开始使用实例,但是实例被处置,并引发异常.
  1. Thread 1 requests an instance from the container.
  2. This is the first request and the container will create a fresh instance.
  3. Thread 2 requests an instance from the container
  4. The container returns the instance created in step 2.
  5. Thread 1 is done with the instance and calls Dispose.
  6. Thread 2 starts using the instance, but the instance is disposed, and throws an exception.

问题在于应用程序将获得对可以处置的实例的引用.

The problem is that the application will get a reference to an instance that can be disposed.

如果可以的话,请尝试通过重新设计应用程序来防止这样做.公开实现IDisposable的服务类型是一种不好的做法,因为IDisposable是泄漏抽象.我个人的喜好甚至是阻止对这些服务的任何实现以实现IDisposable.在大多数情况下,重新设计可以阻止您执行此操作.

Try to prevent doing this by redesigning your application if you can. It's a bad practice to expose service types that implement IDisposable, because IDisposable is a leaky abstraction. My personal preference is even to prevent any implementations of these services to implement IDisposable. In most scenarios a redesign can prevent you from having to do this.

如果需要使用IDisposable对象,通常的方法是创建并注入创建这些IDisposable对象的工厂.这样,消费者可以安全地处置此类物体,而不会出现任何问题.

If you need to use IDisposable objects, the usual way to do this is to create and inject factories that create these IDisposable objects. This way the consumer can safely dispose such an object, without any problem.

这里的一般问题是很难创建实现IDisposable的对象,而这些对象实际上是线程安全的.

The general problem here is that it is hard to create objects that implement IDisposable, that are actually thread-safe.

如果您确实想要这样做,则可以尝试创建一个进行引用计数的装饰器.例如看下面的装饰器.它包装一个IService并实现IService. IService实现IDisposable.装饰器采用允许创建实例的>委托.对象的创建和处理受到lock语句的保护,并且装饰器对调用者对其的引用进行计数.在最后一个消费者放置装饰器之后,它将放置该对象并创建一个新对象.

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 implements IService. IService implements IDisposable. The decorator takes a Func<IService> delegate that allows creation of instances. Creation and disposal of objects is protected by a lock 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.

public class ScopedServiceDecorator : IService
{
    private readonly object locker = new object();
    private Func<IService> factory;
    private IService currentInstance;
    private int referenceCount;

    public ScopedServiceDecorator(Func<IService> factory)
    {
        this.factory = factory;
    }
    public void SomeOperation()
    {
        IService instance;
        lock (this.locker)
        {
            instance = this.GetInstance();
            this.referenceCount++;
        }

        instance.SomeOperation();
    }

    public void Dispose()
    {
        IService instance = null;

        lock (this.locker)
        {
            this.referenceCount--;

            if (this.referenceCount == 0)
            {
                instance = this.wrappedService;
                this.wrappedService = null;
            }
        }

        // Dispose the object outside the lock for performance.
        if (instance != null)
        {
            instance.Dispose();
        }
    }

    private IService GetInstance()
    {
        if (this.wrappedService == null)
        {
            this.wrappedService = this.factory();
        }

        return this.wrappedService;
    }
}

请注意,由于以下原因,该实现仍存在缺陷:

Please note that this implementation is still flawed, because of the following reasons:

  1. 多次调用Dispose会中断装饰器.
  2. 当使用者多次调用SomeOperation(或IService具有多个方法)时,实现将中断.
  1. Calling Dispose multiple times breaks the decorator.
  2. When consumers call the SomeOperation multiple times (or the IService 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.

我希望这会有所帮助.

这篇关于如何创建一个Ninject自定义范围,该范围返回相同的对象,直到该对象被处置?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆