1

After updating SimpleInjector from version 2.8.3 to v3.0.1, it returns an error when I try to pass the connection string into a constructor. This was working fine in the past but after the update something is wrong. Error:

A first chance exception of type 'System.ArgumentException' occurred in SimpleInjector.dll

Additional information: The constructor of type MwmJobUpdateNotifier contains parameter 'connectionString' of type String which can not be used for constructor injection.

Here is where the SimpleInjector container is configured:

    public static Container Configure(Container container)
    {
        // Force assembly reference so Interfaces load correctly.
        if (typeof(IJobRepository) != null) 
            container.RegisterAllInterfacesForClassesInAssemblyContaining<JobRepository>();

        // Force assembly reference so Interfaces load correctly.
        if (typeof(IGmcService) != null)
            container.RegisterAllInterfacesForClassesInAssemblyContaining<GmcService>();

        container.Options.AllowOverridingRegistrations = true;
        container.Register<IMwmJobUpdateNotifier>(() => new MwmJobUpdateNotifier(container.GetInstance<IJobRepository>(),
            ConfigurationManager.ConnectionStrings["Coordinate_DatabaseEntities"].ConnectionString));
        container.Options.AllowOverridingRegistrations = false;

        MWM.Service.DAL.Config.AGI.AGIIocConfig.Configure(container);

        return container;
    }

It looks like doesn't like the way I pass the two parameters into the constructor.

Edited: The exception is triggered within my ContainerException class where all interfaces are registered:

public static Container RegisterAllInterfacesForClassesInAssemblyContaining<T>(this Container container, Lifestyle lifestyle = null) where T : class
    {
        var assembly = typeof(T).Assembly;

        var registrations = assembly.GetExportedTypes()
            .Where(type => type.IsClass && 
                type.GetInterfaces()
                    .Except(type.GetInterfaces().SelectMany(x => x.GetInterfaces()))
                    .Except(type.BaseType.GetInterfaces())
                    .Any())
            .Select(type => new 
            { 
                Services = type.GetInterfaces()
                    .Except(type.GetInterfaces().SelectMany(x => x.GetInterfaces()))
                    .Except(type.BaseType.GetInterfaces()), 
                Implementation = type
            });

        foreach (var registration in registrations)
        {
            foreach (var service in registration.Services)
            {
                if (registration.Implementation.IsGenericTypeDefinition)
                {
                    if (lifestyle == null)
                    {
                        container.Register(service.GetGenericTypeDefinition(), registration.Implementation.GetGenericTypeDefinition());
                    }
                    else
                    {
                        container.Register(service.GetGenericTypeDefinition(), registration.Implementation.GetGenericTypeDefinition(), lifestyle);
                    }
                }
                else
                {
                    if (lifestyle == null)
                        container.Register(service, registration.Implementation);
                    else
                        container.Register(service, registration.Implementation, lifestyle);
                }
            }
        }

        return container;
    }

The exception is captured in the penultimate container.Register(...) call.

Eris
  • 7,378
  • 1
  • 30
  • 45
Rober
  • 726
  • 8
  • 27
  • On which line do you get that exception? – Steven Aug 22 '15 at 11:34
  • 1
    This wasn't supported in v2 either. Could it be that for some reason the `MwmJobUpdateNotifier` is auto-wired, where it pteviously wasn't? – Steven Aug 22 '15 at 11:36
  • I get the exception in "conatiner.Register(service, registration.Implementation);". If I downgrade the nuget packages to v2.8.3 I don't get the exception anymore. I see in the breaking changes for v3, this SI version is more restrictive https://github.com/simpleinjector/SimpleInjector/releases – Rober Aug 22 '15 at 15:07
  • 1
    It's interesting that you can no longer replace the registration before the container spots the string constructor argument. I guess one solution is to pass a list of types to exclude to `RegisterAllInterfacesForClassesInAssemblyContaining`. – qujck Aug 22 '15 at 20:45
  • 1
    Please can you include the full stack trace? – qujck Aug 23 '15 at 06:25

1 Answers1

0

I have the same problem but from what I'm reading here: How do I pass a parameter to the constructor using Simple Injector?

it seems you/we have to change the design so that run-time values are not dependencies of the constructor.

In my particular situation, the object is a database service and needs connection strings parameters, which are stored in a config file, making them run-time values because of the different environments in the release pipeline.

My repositories project can't access the config file directly (tho I suppose it could but it felt wrong to me to try) so I introduced an ad-hoc value dependency resolver. This object is just a wrapper around a dictionary with some management methods to add, remove, update, merge and check for required values so each object that depends on it can check it provides the required items.

public interface IResolver 
{
    void Add<T>(string key, T keyValue);
    T Resolve<T>(string key);
    bool ResolvesAll(params string[] keys);
}

public sealed class Resolver : IResolver
{
    private Dictionary<string, object> directory;

    public void Add<T>(string key, T keyValue)
    {
        if (directory.ContainsKey(key))
          directory[key] = keyValue;
        else
          directory.Add(key, keyValue);
    }

    public T Resolve<T>(string key) 
    {
        return directory.ContainsKey(key) ? directory[key] as T : default(T);
    }
    public bool ResolvesAll(params string[] keys)
    {
        return keys.All(k => directory.ContainsKey(k));
    }

}

Then the container just registers the resolver:

container.RegisterSingleton<IResolver, Resolver>(new Resolver());

And the dependent classes receive the auto-wired object.

public class SomeClass : ISomeClass
{
    private readonly IResolver resolver;
    public SomeClass(IResolver resolver)
    {
        this.resolver = resolver;
    }

    public void SomeMethod(string whatever)
    {
        if (!resolver.ResolvesAll("fred", "barny", "wilma"))
           throw new Exception("missing dependencies");

        var fred = resolver.Resolve<string>("fred");
        SomeOtherMethod(fred, whatever);
    }
}
stardotstar
  • 318
  • 2
  • 18