How to create a custom Ninject scope that returns the same object until this object is deleted?

In Ninject, declaring a binding in a Singleton scope means that the same object will be returned every time. There can be only one object.

I would like to return one object at a time. In other words:

  • The first call to the Get () method creates a new object and returns it.
  • Subsequent calls to Get () return the same instance.
  • The facility is located.
  • The first call to Get () after placing the object creates an instance of the new / second object and returns it.
  • Subsequent calls to Get () will return the object created in step 4.

EDIT:. This problem is actually quite simple to solve using providers and having the object raise an event when it is located. I was curious if there is a way to do this using areas in Ninject, and leave this question here because Stephen's answer is excellent.

+6
source share
2 answers

Since you want to use this construct in a multi-threaded application and want to reuse the same instance in threads (as you mean in your comment), you cannot solve this problem by setting up a DI container.

You simply cannot configure the object that will be updated after deletion, due to race conditions. Imagine the following scenario:

  • In stream 1, an instance from the container is requested.
  • This is the first request, and the container will create a new instance.
  • Thread 2 requests an instance from the container
  • The container returns the instance created in step 2.
  • Topic 1 runs on the instance and calls Dispose .
  • Topic 2 is started using the instance, but the instance is deleted and throws an exception.

The problem is that the application will receive a link to an instance that can be deleted.

Try to prevent this by changing your application if you can. Bad practice is to expose the types of services that implement IDisposable , because IDisposable is a fuzzy abstraction. My personal preference is to even prevent the implementation of these services to implement IDisposable . In most scenarios, a redesign may prevent you from doing so.

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

A common problem is that it is difficult to create objects that implement IDisposable , which are actually thread safe.

If you really want this, you can try creating a decorator that does reference counting. Look for an example from the decorator below. It completes the IService and implements the IService . IService implements IDisposable . The decorator accepts a Func<IService > delegate, which allows you to instantiate. Creating and deleting objects is protected by the lock statement, and the decorator counts the references to it by callers. It will destroy the object and create a new one after the last consumer places 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; } } 

Note that this implementation is still erroneous for the following reasons:

  • Calling Dispose breaks the decorator several times.
  • When consumers call SomeOperation several times (or IService has several methods), the implementation will be broken.

It is difficult to create a decorator that functions as expected. One easy way to do this is to serialize access to the object, but when you do, you probably want to use a single instance for the stream. That would be a lot easier.

Hope this helps.

+6
source

I know this has been resolved, but ... @Steven's answer does not indicate that there is an InScope mechanism in Ninject that takes into account aspects of what you are looking for.

Take a look at Nate Kohari Cache and Collect to determine how you can define scope in Ninject 2.

Then go to the source of the ninja and see how the InRequestScope function is implemented (including how the stall is connected). There, some work is planned for 2.3-4, to generalize how it works, to allow it to be used for some complex hosting scenarios.

When you look at these two links, ask a question about the ninject mailing list, and you will definitely have a solution.

+1
source

Source: https://habr.com/ru/post/886670/


All Articles