Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Resolve Generic Dependency #90

Unanswered
c0nstexpr asked this question in Q&A
Mar 21, 2025 · 1 comments · 9 replies
Discussion options

Consider the following code, the types A and B are like model types. Each of the model provide a specific implementation for generic interface IDependency.
The generic type Service consume the dependencies with a generic parameter.

public class A;
public class B;
public interface IDependency<T>
{
}
public class ADependency : IDependency<A>;
public class BDependency : IDependency<B>;
public interface IService<T>;
public class Service<T>(IDependency<T> _) : IService<T>;
public partial class Composition : ServiceProviderFactory<Composition>
{
 [Conditional(nameof(DI))]
 static void Setup() => DI.Setup()
 .DependsOn(Base)
 .Bind<IDependency<A>>().To<ADependency>()
 .Bind<IDependency<B>>().To<BDependency>()
 // what if i have much more of model types?
 .RootBind<IService<A>>().To<Service<A>>()
 .RootBind<IService<B>>().To<Service<B>>();
}

But the DI decleration for services in composition is really cumbersome if we have more model types.

Is there any way to simplify the decleration into one line?

.RootBind<IService<?>>().To<Service<?>>();
You must be logged in to vote

Replies: 1 comment 9 replies

Comment options

There's no way to do that at the moment. This is due to the fact that we will have to analyze too many variants for the roots of the composition (hypothetically and generally). But you can simplify your setup like this:

public partial class Composition : ServiceProviderFactory<Composition>
{
 [Conditional(nameof(DI))]
 static void Setup() => DI.Setup()
 .DependsOn(Base)
 .Bind().To<ADependency>()
 .Bind().To<BDependency>()
 .Bind().To<Service<TT>>()
 .Root<Service<A>>()
 .Root<Service<B>>();
}
You must be logged in to vote
9 replies
Comment options

I can overwite the new root method to record all injected types?

Comment options

Unfortunately this only works for declared roots.

Comment options

I think the optimal solution is this:

public partial class Composition : ServiceProviderFactory<Composition>
{
 [Conditional(nameof(DI))]
 static void Setup() => DI.Setup()
 .DependsOn(Base)
 .Bind().To<ADependency>()
 .Bind().To<BDependency>()
 .Bind().To<Service<TT>>()
 .Root<Service<A>>()
 .Root<Service<B>>();
}

Wouldn't you like to define a composition root for each type of Service<T>?

Comment options

I think the optimal solution is this:

public partial class Composition : ServiceProviderFactory<Composition>
{
 [Conditional(nameof(DI))]
 static void Setup() => DI.Setup()
 .DependsOn(Base)
 .Bind().To<ADependency>()
 .Bind().To<BDependency>()
 .Bind().To<Service<TT>>()
 .Root<Service<A>>()
 .Root<Service<B>>();
}

Wouldn't you like to define a composition root for each type of Service<T>?

this a simple demo extracting from my production code. in production code:

  1. no default implementation for idependency or service, only interface and separated implementation for each model
  2. there're 30 or maybe more model types, really cumbersome to write those declarations and easy to miss some of them
  3. finally i really don't think duplicated code is good, it just make the code hard to maimaintain
    i wonder whether the technique used in consumer-types example would help here, like build the service type for each model type
Comment options

The difference with classic DI containers is that classic containers resolve dependencies dynamically at runtime. That is, they can potentially work with any set of types and allow late binding. There are disadvantages - runtime errors and performance degradation (reflection for dependency analysis).

Pure.DI - works differently. Already at compile time it should know the entire set of compositions it should be able to build for each specific root of the composition. It has its disadvantages - no late binding, no possibility to generate code to create all variants of object compositions - it is impossible. But it has its advantages - the code is as efficient as the code written manually in the pure DI paradigm, there are no runtime errors because all the analysis and construction of the composition code takes place at compile time.

In the pure DI paradigm, it is better if the composition has a single composition root and instead of the Resolve<T>() method uses method(s) (or property) to concrete compositions of objects (without any tricks).

ASP.NET Core and MS DI is done on late binding and universally uses GetService() to get object compositions. So for ASP.NET I would recommend using MS DI.

To use Pure.DI, you need to shift your focus a bit from using GetService() to using the roots of the composition. And then many of your questions will resolve themselves. For example, consider your example like this:

using System.Diagnostics;
using System.Runtime.CompilerServices;
using Pure.DI;
using Pure.DI.MS;
var composition = new Composition();
var app = composition.App;
public class A;
public class B;
public interface IDependency<T>;
public class ADependency : IDependency<A>;
public class BDependency : IDependency<B>;
public interface IService<T>;
public class Service<T>(IDependency<T> _) : IService<T>;
public class MyApp(IService<A> serviceA, IService<B> serviceB);
public partial class Composition
{
 private static void Setup() => DI.Setup()
 .Bind().To<ADependency>()
 .Bind().To<BDependency>()
 .Bind().To<Service<TT>>()
 .Root<MyApp>("App");
}

composition.App is the composition root here:

public MyApp App => new MyApp(new Service<A>(new ADependency()), new Service<B>(new BDependency()));

You have efficient code and simple setup.

In my solutions, I try to limit the number of roots to a minimum, like here.

If you want to add a later creation, you can do it like this:

public class MyApp(IService<A> serviceA, Func<IService<B>> serviceBFactory);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
Q&A
Labels
None yet

AltStyle によって変換されたページ (->オリジナル) /