mercredi 23 juin 2021

How to invoke a method via reflection that takes in a Func

I'm using SpecFlow, and I want to shove proxy in the service registrations from IServiceCollection so that ScenarioContext.ScenarioContainer can resolve them.

The strategy I'm trying to use is simply to register a factory for each of the types defined in the service collection so that the ScenarioContainer can resolve them. Unfortunately for me, there is no non-generic alternative to RegisterFactoryAs, so I'm trying to create a generic method and invoke it via reflection.

Where I'm hitting a wall is the arguments to MethodBase.Invoke: the first argument to RegisterFactoryAs is a Func<IObjectContainer, T>, and I'm encountering two problems with it:

  1. I cannot define it as Func<IObjectContainer, T> factory = _ => ... since I don't have an outer-scoped T.
  2. When attempting to use it as Func<IObjectContainer, object>, I the run-time exception "System.InvalidOperationException : Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true."
  3. When attempting to use the local function FactoryFunction, I see a compile error stating that I can't convert a method group into an object.

My best bet seems to be the local function, if I can somehow convert it into the required Func.

private void AddToObjectContainer2(IServiceCollection services)
{
    var sp = services.BuildServiceProvider();
    foreach (var sd in services)
    {
        var containerType = _scenarioContext.ScenarioContainer.GetType();
        var delegateType = typeof(Func<,>).MakeGenericType(typeof(IObjectContainer), sd.ServiceType);
        var methodInfo = containerType
            .GetMethod(nameof(IObjectContainer.RegisterFactoryAs),
                new[]
                {
                    delegateType,
                    typeof(string)
                });
        var registerFactoryAs = methodInfo
            !.MakeGenericMethod(new[]
            {
                sd.ServiceType
            });

        Func<IObjectContainer, T> factoryWithCompileError = _ =>
        {
            return sp.GetService(sd.ServiceType);
        };
        
        Func<IObjectContainer, object> factory = _ =>
        {
            return sp.GetService(sd.ServiceType);
        };

        T FactoryFunction<T>(IObjectContainer c)
        {
            return (T)sp.GetService(sd.ServiceType); 
        }
        
        // Throws System.InvalidOperationException : Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true.
        registerFactoryAs.Invoke(_scenarioContext.ScenarioContainer,
            new object[]
            {
                factory , (string)null
            });
        
        // Compiler error: Cannot convert expression of type 'method group' to type 'object'
        registerFactoryAs.Invoke(_scenarioContext.ScenarioContainer,
            new object[]
            {
                FactoryFunction , (string)null
            });
    }
}

I tried to use https://github.com/solidtoken/SpecFlow.DependencyInjection, but it can't seem to handle one of the auto-registered types we're using that has an internal implementation in the Specflow library.

I tried https://github.com/AdCodicem/SpecFlowMicrosoftDependencyInjection, but apparently the signature of a method of one of its dependencies has been removed, and it's throwing a MethodNotFoundException at runtime.





Aucun commentaire:

Enregistrer un commentaire