0

I have a web service where I find myself putting the exact same try/catch block in every method and I'm trying to eliminate that code reuse.

Here is a sample method (some code removed) where you can see #1/#2/#3 are the core calls and are very similar.

    [HttpGet]
    [Route("GetKeys")]
    [Produces(MediaTypeNames.Application.Json)] // "application/json"
    public async Task<EntityKeyPage> GetKeys([FromQuery] string company = "", [FromQuery] DocumentPaging documentPaging = null)
    {
        [...]

        try
        {
            // Sometimes it's this #1
            VendTableServiceGetKeysResponse getKeysResponse = await aifClient.getKeysAsync(getKeysRequest);

            // Sometimes it's this #2
            // VendTableServiceReadResponse readResponse = await aifClient.readAsync(readRequest);

            // Sometimes it's this #3
            // VendTableServiceFindKeysResponse findKeysResponse = await aifClient.findKeysAsync(findKeysRequest);


        }
        catch (System.ServiceModel.FaultException<AifFault> ex)
        {
            var message = this.GetAXMessage(ex);

            aifClient.Abort();

            throw new Exception(message);
        }
        catch (Exception e)
        {
            aifClient.Abort();

            string errorMessage = e.Message != null ? e.Message : "";

            errorMessage = e.InnerException != null ? errorMessage + "\n" + e.InnerException : errorMessage;

            throw new Exception(errorMessage);
        }
        finally
        {
            aifClient.Close();
        }

        return getKeysResponse.EntityKeyPage;
    }

Here is my attempt at a generic method I can use.

    private async Task<Out> MakeAIFCall<In, Out>(Func<In, Out> action, In @in)
    {
        Out @out;

        try
        {
            @out = action(@in);
        }
        catch (System.ServiceModel.FaultException<AifFault> ex)
        {
            var message = this.GetAXMessage(ex);

            aifClient.Abort();

            throw new Exception(message);
        }
        catch (Exception e)
        {
            aifClient.Abort();

            string errorMessage = e.Message != null ? e.Message : "";

            errorMessage = e.InnerException != null ? errorMessage + "\n" + e.InnerException : errorMessage;

            throw new Exception(errorMessage);
        }
        finally
        {
            aifClient.Close();
        }

        return @out;
    }

I wrote that method based on my reading, but I can't figure out how to call it? It appears I normally would pass a method name, but in this case it would be an object+method (aifClient.getKeysAsync(...)/aifClient.readAsync(...)/aifClient.findKeysAsync(...))?

How do I call my method and or am I doing it wrong?

This is my first attempt at calling it, but it's wrong:

var response = await MakeAIFCall<VendTableServiceGetChangedKeysRequest, VendTableServiceGetChangedKeysResponse>(aifClient.getKeysAsync, getKeysRequest);

The variables @out and @in were suggested by intellisense...I have no idea why the @ symbol is there or what it does.

William YK
  • 1,025
  • 12
  • 26

1 Answers1

1

Since your methods are awaited we can almost safely assume that they return tasks (however there are other possibilities), so first of all you will need to change your method signature to something like this:

private async Task<Out> MakeAIFCall<In, Out>(Func<In, Task<Out>> action, In @in)
{
    Out @out;

    try
    {
        @out = await action(@in);
    }
    ....

Second of all you can leverage existence of closures in C# so you will not need to provide a parameter:

private async Task<Out> MakeAIFCall<Out>(Func<Task<Out>> call)
{
    Out @out;

    try
    {
        @out = await call();
    }
    ....
}

And use it like that:

await MakeAIFCall(() => aifClient.getKeysAsync(getKeysRequest));
Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • This is brilliant. I'm going to have to stare at this for a while to figure out how it works, but thanks! Any idea why the variable it suggested is `@out` instead of just `out`? I can probably hunt that out on Google though. – William YK Feb 02 '21 at 17:56
  • @WilliamYK was glad to help! As for `@out` - `in` and `out` are [reserved identifiers](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/) in C# and `@` is used to create a [verbatim identifier](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/verbatim) so you can call variable/parameter with those words. – Guru Stron Feb 02 '21 at 18:21
  • Ah, so I just happened to choose a keyword (`Out`) so the `@` prevents the compiler from erroring because I just happened to choose the keyword? – William YK Feb 02 '21 at 18:30
  • @WilliamYK yep, exactly. – Guru Stron Feb 02 '21 at 18:43