I am attempting to create a proxy wrapper around services that are defined by an interface to "hide" the client specific calling code from the developer user of the service.
I have already been able to generate most of the wrapper class itself but am now struggling on generating the Func argument to the pre-existing client call.
An example TService has an interface:
public interface ITestService
{
public Task ServiceMethodAsync();
public Task<TestDTO> ServiceMethodWithResultAsync();
}
And the signature for the client implementation is:
public Task<TResult> CallAsync<TResult>(Func<TService, Task<TResult>> operation);
public Task CallAsync(Func<TService, Task> operation);
When calling directly a developer currently writes something like:
var result = await client.CallAsync(service => service.ServiceMethodWithResultAsync(requestDTO));
While the preferred option would be that they could just call:
var result = await service.ServiceMethodWithResultAsync(requestDTO);
as this allows the either a local service or my remote wrapper to be injected by DI using the IService interface and whether it is a remote service or local service does not need considered by the developer.
I currently have my typebuilder generating the required code to create the wrapper class inheriting from the client baseType
and defining all interface methods against it, but currently passing a null as the Func<TService, Task<TResult>> operation
.
private static void DefineInterface(TypeBuilder typeBuilder, Type baseType, Type serviceInterfaceType)
{
foreach (MethodInfo serviceMethod in serviceInterfaceType.GetMethods())
{
ParameterInfo[] parameters = serviceMethod.GetParameters();
MethodBuilder methodBuilder = typeBuilder.DefineMethod(
serviceMethod.Name,
serviceMethod.Attributes ^ MethodAttributes.Abstract,
serviceMethod.CallingConvention,
serviceMethod.ReturnType,
serviceMethod.ReturnParameter.GetRequiredCustomModifiers(),
serviceMethod.ReturnParameter.GetOptionalCustomModifiers(),
parameters.Select(p => p.ParameterType).ToArray(),
parameters.Select(p => p.GetRequiredCustomModifiers()).ToArray(),
parameters.Select(p => p.GetOptionalCustomModifiers()).ToArray());
ILGenerator ilGenerator = methodBuilder.GetILGenerator();
MethodInfo callAsyncMethod = getCallAsyncMethod(baseType, serviceMethod);
ilGenerator.Emit(OpCodes.Ldarg_0); // Push "this"
ilGenerator.Emit(OpCodes.Ldnull); // Push Func<> argument
ilGenerator.Emit(OpCodes.Call, callAsyncMethod); // Call CallAsync on the base client
ilGenerator.Emit(OpCodes.Ret);
}
}
From here I am a little stuck on exactly how to go about defining the Func<> service => service.ServiceMethodWithResultAsync(requestDTO)
including the requestDTO
parameter(s) that I expect/assume will be in Ldarg_1
, Ldarg_2
etc. ?
Any and all help greatly appreciated.