Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Method to return a generic type when passing in a generic delegate

I want to:

  1. return a generic type, can be for example ICollection OR string, needs to be flexible. this will be returned by the restapi client that consumes the api.(different endpoints have different return types)

  2. pass in some kind of delegate containing the method to be called from the actual rest client.

    MEDevel.com: Open-source for Healthcare and Education

    Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

    Visit Medevel

rest client is an Interface IClient that is resolved like this, it is just and instance of the api client for the application.

 public static IClient GetClient()
        {
            return App.Current.Handler.MauiContext.Services.GetService<IClient>();
        }

you can then access api like this (InjectedApiClient is a static class)

var client = InjectedApiClient.GetClient();
var languages = client.GetAllLanguagesAsync()) // get all languages from the api ICollection<Language> return type

In the past I remember using delegates like this:

  public async Task DoSomething(Func<Task> methodToExecute)
        {
        //some generic logic here that can be applied to everything
                await methodToExecute();
        //some generic logic here that can be applied once this is done or failed
        }

     public async Task FlagOrder(Guid orderId)
        {
            await DoSomething(async () => await SetOrderStatusToFlagged(orderId));
        }

I was trying to use the same approach when designing the following logic:

//this method should accept any api call that does not need retrying and be flexible to return any type
     public static async Task<T> ExecuteCall<T>(Func<Task<T>> methodToExecute)
        {
            try
            {
                return await methodToExecute();
            }
            catch (HttpRequestException)
            {
                return default(T);
                //log this to queue to do
            }

        }

//this method should accept any api call that needs retrying and be flexible to return any type
        public static async Task<T> ExecuteWithRetry<T>(Func<Task<T>> methodToExecute)
        {
            try
            {
                return await _retryPolicy.ExecuteAsync(async () =>
                {
                    return await methodToExecute();
                });
            }
            catch (HttpRequestException)
            {
                return default(T);
                //log this to queue to do
            }
        }

the following code works but I dont like the fact I need to call GetClient and pass it like this for every call in the app:

var client = InjectedApiClient.GetClient();
var machines = await InjectedApiClient.ExecuteWithRetry(async () => await 
client.GetAllMachinesAsync());
var languages = await InjectedApiClient.ExecuteWithRetry(async () => await 
client.GetAllLanguagesAsync());

Is there any way I could do the following:

 public static async Task<T> ExecuteCall<T>(Func<Task<T>> methodToExecute)
    {
        try
        {
    //I want to get the client here so dont need to instantiate that everywhere I am 
    //making api calls, also dont want to pass it as an argument 
    // but execute the method passed down as a methodToExecute here with this client

     var client = InjectedApiClient.GetClient();

            return await //hey client take the passed delegate(methodToExecute) and execute that and return whatever it returns.
        }
        catch (HttpRequestException)
        {
            return default(T);
            //log this to queue to do
        }
    }

When I pass the client as an argument this refactoring is loosing its purpose I dont want to
have execute method for each individual api call just one and reuse it for everything.
Also calling get client inside this method will only allow me to manually choose which method to call from the Iclient so again will need multiple methods so the generic would be useless. I want to have only 2 methods for any possible outcome.

>Solution :

It seems that you need to accept Func<IClient, Task<T>> (see the Func<T,TResult> delegate), i.e. something along these lines:

public static async Task<T> ExecuteCall<T>(Func<IClient, Task<T>> methodToExecute)
{
    try
    {
        var client = InjectedApiClient.GetClient();

        return await methodToExecute(client);
    }
    catch (HttpRequestException)
    {
        return default(T);
        //log this to queue to do
    }
}

And usage:

var machines = await InjectedApiClient.ExecuteCall(c => c.GetAllMachinesAsync());

You also can go full generic with:


public async Task<TResult> ExecuteCall<T, TResult>(Func<T, Task<TResult>> method)
{
    T t = ...;
    return await method(t);
}

With a bit more convoluted usage (another option – specify both generic parameters):

await InjectedApiClient.ExecuteCall((IClient c) => c.GetAllMachinesAsync());
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading