There is an external generic method, which I call with reflection. But there is a trick: this method has a following signature – Foo<TResult>(Func<Bar, TResult> factory)
. The problem is that in my method, I get TResult
not as generic type, but dynamically, during run-time. Actually, there is a whole purpose of my method – being dynamic.
I tried following code as parameter for my reflected Foo
:
Func<Bar, dynamic> factory = (bar) =>
{
return tResultInstance;
};
Yes, I actually don't need Bar
, it is not a mistake. :) Problem is that when I invoking my reflected Foo
it fails with casting exception, because it cannot cast System.Object
to TResult
.
So, is that possible to dynamically (during run-time) create such Func to pass it to dynamical invocation?
UPD 0.
Static.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<Baz>(_ => new Baz("string"));
Dynamic.
private const string AddScopedMethodName = "AddScoped";
private const string AddScopedMethodSignature = "Microsoft.Extensions.DependencyInjection.IServiceCollection AddScoped[TService](Microsoft.Extensions.DependencyInjection.IServiceCollection, System.Func`2[System.IServiceProvider,TService])";
<...>
var bazConstructorInfo = bazType.GetConstructor(new Type[1] // bazType is Baz.
{
typeof(String)
});
var bazConstructorParameters = new object[1]
{
inputString // inputString is "string".
};
var bazInstance = bazConstructorInfo.Invoke(bazConstructorParameters);
var addScopedMethodInfo = typeof(ServiceCollectionServiceExtensions)
.GetMethods()
.Where(method =>
method.IsPublic &&
method.IsGenericMethod &&
(method.Name == AddScopedMethodName) &&
(method.ToString() == AddScopedMethodSignature))
.Single();
var addScopedMethod = addScopedMethodInfo.MakeGenericMethod(bazType);
Func<IServiceProvider, dynamic> implementationFactory = (serviceProvider) =>
{
return bazInstance;
};
var addScopedMethodParameters = new object[2]
{
services, // services is IServiceCollection.
implementationFactory
};
addScopedMethod.Invoke(
services,
addScopedMethodParameters); // Fails with "Object of type 'System.Func`2[System.IServiceProvider,System.Object]' cannot be converted to type 'System.Func`2[System.IServiceProvider,Baz]'." exception. :(
UPD 1.
To @alexei-levenkov. It looks like Runtime creation of generic Func<T> is not answer to me. They are using following code:
static Foo CreateFoo() { return new Foo(); }
<...>
var method = typeof(Program)
.GetMethod(
"CreateFoo",
BindingFlags.NonPublic | BindingFlags.Static);
But in my case, I do not know Foo
before runtime. I just cannot write this static Foo CreateFoo() { return new Foo(); }
line.
CodePudding user response:
A quick fix would be adding helper class:
class Helper
{
public static Func<IServiceProvider, T> GetFactory<T>(T instance) => _ => instance;
}
And using it to get implementation factory:
var methodInfo = typeof(Helper).GetMethod(nameof(Helper.GetFactory));
var implementationFactory = methodInfo.MakeGenericMethod(bazType).Invoke(null, new[] { bazInstance });
But in general all of this seems a huge overkill when you can just use ServiceDescriptor
directly:
Func<IServiceProvider, object> implementationFactory = _ => bazInstance;
var serviceDescriptor = new ServiceDescriptor(bazType, implementationFactory, ServiceLifetime.Scoped);
services.Add(serviceDescriptor);
Also the instance creation reflection can be reduced to Activator.CreateInstance
call:
var services = new ServiceCollection();
Type bazType = typeof(Baz);
var bazConstructorParameters = new object[]
{
"string" // inputString is "string".
};
var bazInstance = Activator.CreateInstance(bazType, bazConstructorParameters);
Func<IServiceProvider, object> implementationFactory = _ => bazInstance;
var serviceDescriptor = new ServiceDescriptor(bazType, implementationFactory, ServiceLifetime.Scoped);
services.Add(serviceDescriptor);