Let's say I have a service interface that looks like this:
public interface IFooService
{
FooResponse Foo(FooRequest request);
}
I would like to fulfill some cross-cutting concerns when calling methods on services like these; for example, I want unified request logging, performance logging, and error handling. My approach is to have a common base "Repository' class with an Invoke method that takes care of invoking the method and doing other things around it. My base class looks something like this:
public class RepositoryBase<TService>
{
private Func<TService> serviceFactory;
public RepositoryBase(Func<TService> serviceFactory)
{
this.serviceFactory = serviceFactory;
}
public TResponse Invoke<TRequest, TResponse>(
Func<TService, Func<TRequest, TResponse>> methodExpr,
TRequest request)
{
// Do cross-cutting code
var service = this.serviceFactory();
var method = methodExpr(service);
return method(request);
}
}
This works fine. However, my whole goal of making the code cleaner is thwarted by the fact that type inference isn't working as expected. For example, if I write a method like this:
public class FooRepository : BaseRepository<IFooService>
{
// ...
public BarResponse CallFoo(...)
{
FooRequest request = ...;
var response = this.Invoke(svc => svc.Foo, request);
return response;
}
}
I get this compilation error:
The type arguments for method ... cannot be inferred from the usage. Try specifying the type arguments explicitly.
Obviously, I can fix it by changing my call to:
var response = this.Invoke<FooRequest, FooResponse>(svc => svc.Foo, request);
But I'd like to avoid this. Is there a way to rework the code so that I can take advantage of type inference?
Edit:
I should also mention that an earlier approach was to use an extension method; type inference for this worked:
public static class ServiceExtensions
{
public static TResponse Invoke<TRequest, TResponse>(
this IService service,
Func<TRequest, TResponse> method,
TRequest request)
{
// Do other stuff
return method(request);
}
}
public class Foo
{
public void SomeMethod()
{
IService svc = ...;
FooRequest request = ...;
svc.Invoke(svc.Foo, request);
}
}