Skip to content

Monkey-patching to support 2.x and 3.x #133

@NightOwl888

Description

@NightOwl888

I have a NuGet package that depends on SimpleInjector.

I am just letting you know that your restrictive use of the ObsoleteAttribute (that is, setting the error argument to true) is causing a problem with supporting your library. As you might already know, the default NuGet behavior will _silently_ upgrade a package to the lowest common version number. So, if another library decides to support only 3.x and that is installed into someone's target project along with mine (which supports 2.x +), NuGet will install 3.x into the project.

This dilemma for those who depend on SimpleInjector has left us with 3 options:

  1. Don't support SimpleInjector 3.x and set the NuGet maximum version < 3.x (which by the way is totally ignored when doing a Package-Update).
  2. Create a separate NuGet package for SimpleInjector 2.x and SimpleInjector 3.x.
  3. Use .NET reflection to work around the ObsoleteAttribute restriction (yuck!)

I went with the 3rd option. But this means that patching the code to support either version has gone from this:

// Extension methods for cross-version support of Simple Injector 2.x and 3.x.

// This will succeed on 2.x and will be bypassed on 3.x
public static void RegisterCollection(this Container container, Type serviceType,
    IEnumerable<Type> serviceTypes)
{
    container.RegisterAll(serviceType, serviceTypes);
}

// This will succeed on 2.x and will be bypassed on 3.x
public static void RegisterCollection<TService>(this Container container, 
    IEnumerable<TService> containerUncontrolledCollection) where TService : class
{
    container.RegisterAll(containerUncontrolledCollection);
}

To this:

// Extension methods for cross-version support of Simple Injector 2.x and 3.x.

// This will succeed on 2.x and will be bypassed on 3.x
public static void RegisterCollection(this Container container, Type serviceType, 
    IEnumerable<Type> serviceTypes)
{
    // container.RegisterAll(serviceType, serviceTypes);
    var method = container.GetType().GetMethod(
        "RegisterAll",
        BindingFlags.Instance | BindingFlags.Public,
        null,
        new Type[] { typeof(Type), typeof(IEnumerable<Type>) },
        null);

    if (method != null)
    {
        method.Invoke(container, new object[] { serviceType, serviceTypes });
    }
}

// This will succeed on 2.x and will be bypassed on 3.x
public static void RegisterCollection<TService>(this Container container, 
    IEnumerable<TService> containerUncontrolledCollection) where TService : class
{
    // container.RegisterAll(containerUncontrolledCollection);
    var method = container.GetType().GetMethods()
        .Where(mi => mi.Name == "RegisterAll")
        .Select(mi => new { M = mi, P = mi.GetParameters(), A = mi.GetGenericArguments() })
        .Where(x => x.A.Length == 1
            && x.P.Length == 1
            && x.P[0].Name == "collection")
        .Select(x => x.M)
        .FirstOrDefault();

    if (method != null)
    {
        var genericMethod = method.MakeGenericMethod(new Type[] { typeof(TService) });
        genericMethod.Invoke(container, new object[] { containerUncontrolledCollection });
    }
}

If you would have just marked the methods obsolete without raising a compile error I wouldn't have to resort to such measures, because I don't have any control over whether the end user will have 2.x or 3.x in their project - that is up to NuGet.

Adding an extension method to mimic the 3.x behavior when 2.x is installed would have been great. Since an instance method takes precedence over an extension method, the fact that 3.x has them would normally make the first code silently ignored. Except when I upgraded to 3.x, the backward-compatibility code failed because the ObsoleteAttribute caused it not to build.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions