3

I'm working on a project that uses a Windows Mobile device to call a web service.

There's a requirement that states that if a service call fails then the user should be prompted to retry. Currently, there's a service agent which calls all the methods on the web service proxy, and if the call fails there's some code which prompts the user to retry, and then trys the call again. It looks something like this:

public void MyServiceCall(string stringArg, bool boolArg, int intArg)
{
    try
    {
        MyWebService.MyServiceCall(stringArg, boolArg, intArg);
    }
    catch(SoapException ex)
    {
        bool retry = //a load of horrid code to prompt the user to retry
        if (retry)
        {
            this.MyServiceCall(stringArg, boolArg, intArg);
        }
    }
}

The stuff in the catch looks a lot messier on the system than in that snippet, and the CTRL-C CTRL-V pattern has been used to replicate it in every service call. I'd like to refactor this duplicated code into a method but I'm unsure of the best way to retry the method call. I was thinking of having a delegate as an argument to my new method, but since I won't know the signature I'm unsure of how to do this in a generic way. Can anyone help? Thanks.

Charlie
  • 10,227
  • 10
  • 51
  • 92

2 Answers2

7

I think you just need two methods:

protected void Invoke(Action action) {
    try {
       action();
    } catch {...} // your long boilerplate code
}
protected T Invoke<T>(Func<T> func) {
    try {
       return func();
    } catch {...} // your long boilerplate code
}

Then you can use:

public void MyServiceCall(string stringArg, bool boolArg, int intArg)
{
    Invoke(() => MyWebService.MyServiceCall(stringArg, boolArg, intArg));
}

and likewise use the other version for methods with a return value. If you need to, you can also make the delegates take the service itself as an argument - which might be handy for IDisposable reasons:

protected void Invoke(Action<MyService> action) {
   using(MyService svc = new MyService()) {
     try {
       action(svc);
     } catch {...} // your long boilerplate code
   }
}
...
public void MyServiceCall(string stringArg, bool boolArg, int intArg)
{
    Invoke(svc => svc.MyServiceCall(stringArg, boolArg, intArg));
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
2

Just making up code at the top of my head... You could also implement something similar for Func to return values.

public static void ExecuteServiceCall(Action serviceCall) {
    while (true) {
        try {
            serviceCall();
            return;
        } catch (SoapException ex) {
            bool retry = // whaazzaaaaa!?
            if (!retry)
                // Dont retry, but fail miserably. Or return. Or something.
                throw;
        }
    }
}

public void MyServiceCall(string stringArg, bool boolArg, int intArg) {
    ExecuteServiceCall(() => MyWebService.MyServiceCall(stringArg, boolArg, intArg));
}
sisve
  • 19,501
  • 3
  • 53
  • 95