Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a Singleton with Multiple Service Registrations Using DryIoc

Tags:

c#

dryioc

The Problem

I'm trying to register a singleton using DryIoc, but container is returning multiple instances of my singleton class. The singleton class is registered as the implementation type for multiple different service interfaces. When any of the aforementioned service interfaces is requested from DryIoc, I expect to get the same instance of my singleton class back, but that's not happening and I don't know why.

An Example

Here's a basic example of what I'm trying to do. In this example, I have a class, Foo that I would like to use as the singleton implementation for interfaces IFoo and IBar. In other words, when either IFoo or IBar are resolved from the container, I'd like for the same instance of Foo to be returned.

The Service Interfaces

interface IFoo
{   
}
interface IBar
{   
}

The Singleton (Implementation) Class

class Foo : IFoo, IBar
{
}

The Test

Container container = new Container();
container.Register<IFoo, Foo>(Reuse.Singleton);
container.Register<IBar, Foo>(Reuse.Singleton);

object foo = container.Resolve<IFoo>();
object bar = container.Resolve<IBar>();
Assert.AreSame(foo, bar); // Why does this fail?

Considered Solutions

I've considered using DryIoc's RegisterInstance method but that would require the class to be manually created and I'm trying to avoid that because, unlike the simplified example above, the real-world class has dependencies of its own.

like image 387
Brandon Baker Avatar asked Aug 29 '16 19:08

Brandon Baker


2 Answers

The Register method adds separate / independent registrations to the container. You need to specifically say to use the same registration for multiple services.

Option 1: RegisterMany

// registers with Foo interfaces and itself. Remove @nonPublicServiceTypes to restrict for public types
container.RegisterMany<Foo>(Reuse.Singleton, nonPublicServiceTypes: true);

Assert.AreSame(container.Resolve<IFoo>(), container.Resolve<IBar>());

Option 2: RegisterMapping

container.Register<IFoo, Foo>(Reuse.Singleton);
container.RegisterMapping<IBar, IFoo>(); // maps to the IBar registration

Assert.AreSame(container.Resolve<IFoo>(), container.Resolve<IBar>());

Other:

Manually delegate the resolution as in @Fyodor answer.

like image 71
dadhi Avatar answered Oct 31 '22 10:10

dadhi


DryIoc's concept of "reuse" applies to services, not implementations. Your registrations are for two different services (IFoo and IBar), so they get different reuses, and the fact that they share implementation (Foo) does not apply here.

To achieve your goal, you can register the services via made that redirects to the implementation:

container.Register<Foo>( Reuse.Singleton );
container.Register<IFoo, Foo>( made: Made.Of( () => ReturnThis( Arg.Of<Foo>() ) ), reuse: Reuse.Singleton );
container.Register<IBar, Foo>( made: Made.Of( () => ReturnThis( Arg.Of<Foo>() ) ), reuse: Reuse.Singleton );

// Where ReturnThis is define thusly:
static T ReturnThis<T>( T t ) => t;

(you need the ReturnThis call, because naked Made.Of( () => Arg.Of<Foo>() ) doesn't work; Made always expects a method call)

like image 24
Fyodor Soikin Avatar answered Oct 31 '22 11:10

Fyodor Soikin