tags:

views:

185

answers:

5

I've got a number of WebService methods that all include some very boilerplate code of wrapping the actual work in a try/catch/finally and performing the same tasks in the catch/finally. So as a way to encapsulate all of the shared catch/finally stuff I wrote a simple generic.

This works and really eliminates a bunch of repetitive code but feels klunky, and the syntax is very obtuse. Every time I come back to this my brain gets twisted trying to figure it out (a clear sign it is not a good design). I'm looking for feedback on whether this is a crazy thing to do, and if there is a better way to approach it.

Here is my template:

public delegate T2 RestfulServiceRequest<T1, T2>(T1 req);

static class RestfulService
{
 public static T2 ExceptionHandler<T1, T2>(RestfulServiceRequest<T1, T2> serviceCall, T1 req)
 {
  if (req == null)
     throw new BadRequestException(new ArgumentNullException("Invalid or missing request object"));

  try
  {
   return serviceCall(req);
  }
  catch (RestfulException e)
  {
   // log it and rethrow
   Logger.Write(e);
   throw;
  }
  catch (Exception e)
  {
   Logger.Error(e);

   // wrap in a consistent exception for propagation back to caller
   throw new InternalServerException(e);
  }
  finally
  {
   Logger.Debug("Complete");
  }
 }
}

}

And here is a usage of it:

public class Initialization : IInitialization
{
 // MyMethod thas uses the template
 public ApplianceInitResp CreateApplianceServer(ApplianceInitReq req)
 {
  return RestfulService.ExceptionHandler<ApplianceInitReq, ApplianceInitResp>(delegate(ApplianceInitReq x)
  {
   // do some work
   return new ApplianceInitResp();
  }, req);
 }
}

}

A: 

I would agree that this feels a bit clunky to accomplish, however, I am not right away seeing the "pefect" way to re-work this to minimize duplication.

THe only thing I'm thinking is that if you can somehow use interfaces or break it up a bit, I'm not sure exactly how I'd do it, but I can say that for on going changes I don't think I would leave this, at least not without really good documentation.

Mitchel Sellers
+1  A: 

One thing that will make it cleaner is to define interfaces that your request/response objects implement. Then you can get rid of the generics in favor of the interfaces. Note, too, the name change which I think is more descriptive of what you are really trying to do.

public interface IServiceResponse { ... }
public class ApplianceInitResp : IServiceResponse { ... }
public interface IServiceRequest { ... }
public class ApplianceInitReq : IServiceRequest { ... }

public delegate IServiceResponse RestfulServiceRequest( IServiceRequest req );

static class RestfulService
{
    public static IServiceResponse
        Invoke( RestfulServiceRequest serviceCall, IServiceRequest req)        
    {
        if (req == null)
            throw new BadRequestException( ...inner-exception... );
         try
         {
            return serviceCall(req);
         }
         catch (RestfulException e)
         {
            Logger.Write(e);
            throw;               
         }
         catch (Exception e)
         {
             Logger.Error(e);
             throw new InternalServerException(e);
         }
         finally
         {
             Logger.Debug("Complete");
         }
    }
}

public class Initialization : IInitialization
{
     // MyMethod thas uses the template 
     public ApplianceInitResp CreateApplianceServer(ApplianceInitReq req) 
     {
          return RestfulService.Invoke(
                    delegate(ApplianceInitReq x)
                    {
                        // do some work
                        return new ApplianceInitResp();
                    },
                    req );
     }
}
tvanfosson
I like this solution as it is much easier to understand. The only drawback I see is the requirement that all service operations use args that derive from the interface. Not a big deal unless I need to return native types.
BrettRobi
+1  A: 

I would suggest you to look for frameworks that offer AOP functionality (like Spring.NET, Unity). Those will help you to reduce your CreateApplianceServer() call to mere

public ApplianceInitResp CreateApplianceServer(ApplianceInitReq req)
{
    // Do some work
    return new ApplianceInitResp();
}

by taking care of entrance/exit and exception logging with aspects. Probably, if you have some common parameters, you could plug the the argument validation into the aspect as well.

Of course, there would be a learning curve tax, but, trust me, result worth it. You will dramatically reduce amount of boilerplate code in your methods.

A: 

I think what you're doing is fine. This kind of coding is common in scripting languages -- but then it's less verbose because they're dynamically typed. And it's also common in functional languages -- but then it's less verbose because they have such good type inference! In C# you're somewhat hampered by the current limitations of the type inference system, but that will get better in version 4.

The alternative is to use a proliferation of interfaces or classes to encapsulate your work. As you do this, you have fewer type names at the point of invocation, but more code overall.

Eric
+2  A: 

I would change

public static T2 ExceptionHandler<T1, T2>(RestfulServiceRequest<T1, T2> serviceCall, T1 req)

to

public static T2 Invoke<T1, T2>( this T1 req, RestfulServiceRequest<T1, T2> serviceCall)

This will change the call to

public class Initialization :IInitialization {
    public ApplianceInitResp CreateApplianceServer( ApplianceInitReq req ) {
        return req.Invoke( r => {
            // do some work
            return new ApplianceInitResp();
        });
    }
}
jyoung
I have to admit that I do not understand how 'this T1 req' works. How does the compiler figure out to use the correct generic?
BrettRobi