Skip to content

Register service type + implementation type in Jab modules #158

@profix898

Description

@profix898

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:

  1. Change Jab modules to be classes (instead of interfaces), this is discussed and suggested here: Support class modules and Import(Type, string Factory) #97
  2. 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:

  1. a parameter in the registration attribute to (additionally) register the concrete implementation
  2. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions