-
Notifications
You must be signed in to change notification settings - Fork 43
Description
I'm having issues with modules in combination with registering both service type and implementation type. In #97 a very similar issue is raised (proposed solution is to change Jab module interfaces to be classes). In #133 it is suggested to change attributes to allow both service+implementation type registration.
But let me try to explain the scenario step by step
For some implementations I would like to register a service type (mostly an interface) and additionally the concrete (implementation) type with the container. This is currently not possible, the only option you have is to put multiple attributes (one for each type or interface to be registered) like this:
[Singleton(typeof(MyImpl))]
[Singleton(typeof(IMyService), typeof(MyImpl))]
public partial class Container { }
However, in that case the singleton does not apply anymore (same for scoped). Because both registrations are singletons, you end up with two instances in the end. This can be solved by using Instance=
or Factory=
parameters with some custom logic to ensure that only a single instance is ever created. This case is also discussed in #97.
This is not only tedious to write repeatedly, but the custom logic requires a class, because you either need a field to store the instance for Factory=
or access to GetService<>()
which is only available on the container class. You could (as one example) have something like this:
[Singleton(typeof(MyImpl))]
[Singleton(typeof(IMyService), Factory = nameof(MyImplFactory))]
public partial class Container {
IMyService MyImplFactory() => GetService<MyImpl>();
}
Here, MyImpl
is created by the container and IMyService
is "redirected" to point to the same instance.
However, you can't do the same in a Jab module, because Jab modules are interfaces (and you can't implement the required logic).
It seems that there are two potential solutions:
- Change Jab modules to be classes (instead of interfaces), this is discussed and suggested here: Support class modules and Import(Type, string Factory) #97
- Provide addition registration attributes to allow an implementation type to be registered along with one or more service type (it implements). A less intrusive solution IMO. This is discussed and suggested here: Register both as service and implementation #133
I think the registration attribute could be extended to provide:
- a parameter in the registration attribute to (additionally) register the concrete implementation
- the ability to specify multiple service types (interfaces) for the same implementation
This might look like this:
[Singleton(typeof(ITypeA), typeof(ITypeB), typeof(MyClass), registerInstance: true)]
// or in generic attribute syntax
[Singleton<ITypeA, ITypeB, MyClass>(registerInstance: true)]
Since all "elements" are registered together in that case, the source generator would "know" to produce a proper singleton for MyClass
which gets exposed (resolvable via the container) as ITypeA
and ITypeB
(and if registerInstance=true
also as MyClass
itself).
From my POV this would solve both issues quite elegantly. What do you think?
I'm open to suggestions on how to combine Jab modules and multiple registrations for a single implementation (even if it requires a little more code), because I don't think that Jab can be used for the outlined scenario above at the moment.