Search code examples
genericsdryioc

DryIoC pass param to constructor of open generic service based on generic type parameter


Let say that I have this service and two strategies for it:

public class SomeService<TEntity> : ISomeService<TEntity>
{
    public SomeService(ICurrentDbContext context, IStrategy<TEntity>? delete = null)
         : base(context, delete ?? new DefaultStrategy<TEntity>())
        {}
}

public class DefaultStrategy<TEntity> : IStrategy<TEntity> {}

public class CustomStrategy<TEntity> : IStrategy<TEntity> {}

My current registration of service looks like this:

container.Register(typeof(ISomeService<>), typeof(SomeService<>), Reuse.Transient);

What I want to achieve here is to pass CustomStrategy on resolution attempt of ISomeService if generic type parameter T implements some interface, let say for example IFoo. Otherwise, keep it as a parameter with the default value null.

Ofc the first param should be automatically resolved in both cases as a registered dependency.

Don't have any idea for now how to do that, should I use Interceptors for it?


Solution

  • You may do it like this (but it is not clear from your question why you have two strategies DefaultStrategy<TEntity> and CustomStrategy<TEntity>).

    using System.Linq;
    using NUnit.Framework;
    
    namespace DryIoc.IssuesTests
    {
        [TestFixture]
        public class SO_DryIoC_pass_param_to_constructor_of_open_generic_service_based_on_generic_type_parameter
        {
            [Test]
            public void Test()
            {
                var container = new Container();
    
                container.Register(typeof(ISomeService<>), typeof(SomeService<>), Reuse.Transient,
                    made: Parameters.Of.Details((req, p) => 
                        p.ParameterType.GetGenericDefinitionOrNull() == typeof(IStrategy<>) && 
                        p.ParameterType.GetGenericParamsAndArgs().Any(x => x.IsAssignableTo<IFoo>())
                        ? null                              // the default injection behavior 
                        : ServiceDetails.Of(value: null))   // otherwise return the `null` value
                    );
    
                container.Register<ICurrentDbContext, MyDbContext>();
                container.Register(typeof(IStrategy<>), typeof(DefaultStrategy<>));
    
                var s1 = container.Resolve<ISomeService<OtherEntity>>();
                Assert.IsNull(((SomeService<OtherEntity>)s1).Delete);
    
                var s2 = container.Resolve<ISomeService<FooEntity>>();
                Assert.IsNotNull(((SomeService<FooEntity>)s2).Delete);
            }
    
            public interface ISomeService<TEntity> {}
            public class SomeService<TEntity> : ISomeService<TEntity>
            {
                public readonly IStrategy<TEntity> Delete;
                public SomeService(ICurrentDbContext context, IStrategy<TEntity> delete = null) => Delete = delete;
            }
    
            public interface IFoo {}
            public class FooEntity : IFoo {}
    
            public class OtherEntity {}
    
            public interface IStrategy<TEntity> {}
            public class DefaultStrategy<TEntity> : IStrategy<TEntity> { }
            public class CustomStrategy<TEntity> : IStrategy<TEntity> { }
    
            public interface ICurrentDbContext {}
            public class MyDbContext : ICurrentDbContext {}
        }
    }