Search code examples
dryioc

Container has different rules for .net 5 web app and test project


I have a small .net 5 WebApi app, using DryIoc. I am now trying to set up an xUnit test suite for this app, but the integration tests fail immediately because one of the registrations has multiple constructors, even though I am using the same rules for both containers and running the same registrations in the same order as the app.

The registration works fine for the app because the container has ConstructorWithResolvableArguments set for the factory method, but it's not being set from anywhere in our code. I know I can easily just add that rule to the container for the tests, but I don't understand why a container set up in the exact same way appears to have different rules, and I am concerned there may be other differences that could affect the tests.

In the app, the container is set up like so:

Program.cs

public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .UseServiceProviderFactory(new DryIocServiceProviderFactory(Startup.CreateContainer()))
            .ConfigureWebHostDefaults(...);

StartUp.cs

...
public static IContainer CreateContainer() => new Container(DiConfiguration.SetRules);

public void ConfigureContainer(IContainer container)
{
    container.RegisterCoreDependencies();

    // Site specific registrations
    container.ConfigureDomain();
}

DiConfiguration.cs

public static class DiConfiguration
{
    public static Rules SetRules(Rules rules)
    {
        rules = rules.WithAutoConcreteTypeResolution();
        rules = rules.WithVariantGenericTypesInResolve();

        return rules;
    }

    public static void RegisterCoreDependencies(this IContainer container)
    {
        // dependency registrations, not much to see here, nothing clever 
        // just basic registrations e.g.

        container.Register<IFoo, Foo>();
    }

In the test suite I am setting up the container like so:

public class Test
{
    private object _sut;

    public Test()
    {
        var container = new Container(DiConfiguration.SetRules); // <--- Same rules as the app
        container.RegisterCoreDependencies(); // <--- fails here
        container.ConfigureDomain();

        _sut = container.Resolve<Bar>();
    }

    [Fact]
    public void Some_Test_Here()
    {
        ...
    }
}

As you can see, all the container registration code is abstracted out into a shared library (the DiConfiguration class). The test is failing calling RegisterCoreDependencies. I don't understand why the rules are different between the two scenarios, is it perhaps something introduced by the DryIocServiceProviderFactory call in the app setting some extra defaults? (DryIocServiceProviderFactory is part of the DryIoc.Microsoft.DependencyInjection package)


Solution

  • The DryIocServiceProviderFactory will override the existing container rules to conform to MS.DI container. Here is the code: https://github.com/dadhi/DryIoc/blob/5e3f1f7edfe237f69ba33c9166d17e284ca4781a/src/DryIoc.Microsoft.DependencyInjection/DryIocAdapter.cs#L97

    Here how the rules are overriden in detail:

    private static Rules WithMicrosoftDependencyInjectionRules(Rules rules) 
    { 
        rules = rules.Clone(cloneMade: true);
    
        rules._settings |= Settings.TrackingDisposableTransients;
        rules._settings &= ~Settings.ThrowOnRegisteringDisposableTransient;
        rules._settings &= ~Settings.VariantGenericTypesInResolvedCollection;
        rules._factorySelector = SelectLastRegisteredFactory;
        rules._made._factoryMethod = DryIoc.FactoryMethod.ConstructorWithResolvableArguments;
     
        return rules; 
    }