Friday, April 30, 2010

Agatha and a Unified Request Handler Theory

At my last job we were using the Agatha library to simplify requests and responses through our WCF service. All in all it was pretty cool. But there was one thing I didn't like. Every request/response pair needed its own handler class.

Now, normally this makes sense. However, we were dealing with a growing enterprise domain. We're talking about potentially hundreds upon hundreds of handlers. From a code organizational perspective, it made more sense to group our domain methods into categories. Thus were created processor classes. Each subset of the overall domain logic (product ordering, reporting, security, etc.) would have a processor class (or namespace, with further divided classes) with all the methods in there.

This left us with a growing namespace of handler classes, each of which was nearly identical. We had our base handler which inherited from Agatha's handler and did our common stuff (error trapping, unit of work encapsulation, etc.) that every service call needs. It included an abstract method that its Handle method would call. Then, for every request/response pair in the domain, a handler was written which implemented this abstract method and passed control along to the proper processor method.

This means that we were going to have hundreds upon hundreds (maybe more) of identical handlers, all doing precisely the same thing. That's a lot of repetition in the code, and it's an easy point for a new junior dev to get hung up on when he's not used to the system. He shouldn't be expected to "just know" that he has to implement this intuitively useless class. All he should have to do is create his request/response types and his domain logic method. The base handler should take care of directing traffic.

It was a dream born of a desire to simplify and to make the programming as plug-and-play as possible for new junior devs on the team. And this dream has now produced a prototype. Keep in mind that this is not finished. Also, it's SLOW. But it works. Hopefully I can make enhancements to it (feel free to suggest any) or even just re-implement the idea in an entirely different and better way. This is just a proof of concept:

public class MyBaseRequestHandler : IRequestHandler
  where TREQUEST : MyBaseRequest
{
  public Response Handle(TREQUEST request)
  {
    try
    {
      // TODO: Error-check the request

      // Get the assembly for the domain code
      // At my last job they had an empty "marker" class.  Made sense.
      var assembly = Assembly.GetAssembly(typeof(DomainAssemblyMarker));

      // Create a response based on the request type
      // I use a custom attribute called "ResponseTypeAttribute" to
      // store the response type for any given request type at the
      // class level.
      var response = Activator.CreateInstance(
        ((ResponseTypeAttribute)
          request.GetType().GetCustomAttributes(
                              typeof(ResponseTypeAttribute),
                              false)[0]
        ).TypeName
      );

      try
      {
        // Do the process for this request
        // The convention is that there should be exactly ONE method
        // in the processors which takes ONE argument (the request)
        // and returns the response.
        // TODO: Implement a true "unit of work" here
        foreach (var type in assembly.GetTypes())
          if (type.IsClass)
            foreach (var method in type.GetMethods())
              if (method.GetParameters().Length == 1)
                if (method.GetParameters()[0].ParameterType == typeof(TREQUEST))
                {
                  // Get an instance from the IoC and invoke the method
                  var obj = ObjectFactory.GetInstance(type);
                  return (Response)method.Invoke(obj, new object[] { request });
                }

        // Couldn't find the right method, return a failed response
        // The .Failed extension (and .Success) was something they
        // implemented at my old job.  Kind of got me hooked on
        // extensions.  So expect more in the future.
        return ((MyBaseResponse)response).Failed(
          new Message
          {
            DisplayMessage = "Service Error",
            ErrorMessage = "Unable to find processor method for "
                     + request.GetType().ToString(),
            MessageType = MessageType.Error
          }
        );
      }
      catch (Exception ex)
      {
        // Caught an exception trying to find and/or invoke the handler method
        return ((MyBaseResponse)response).Failed(ex);
      }
    }
    catch (Exception ex)
    {
      // Caught an exception trying to create a properly typed reponse based on
      // the request type.  Just return a base response with the failed message,
      // even though the client probably won't like the type.
      return new MyBaseResponse().Failed(ex);
    }
  }

  public Response Handle(Request request)
  {
    return Handle((TREQUEST)request);
  }

  public void Dispose()
  {
    // TODO: Handle disposal
  }

  public Response CreateDefaultResponse()
  {
    return new MyBaseResponse();
  }
}

Ya, you can see the slow part. It's kind of obvious. So the main improvement to be made here is in finding the method to invoke. Hey, at least it's properly asking for the object from the IoC container (StructureMap), right? I'm thinking of doing more with custom attributes (an area that's somewhat new to me) to assist in that method discovery process, similar to what I did with the request/response discovery (which I'd also like to improve a bit if possible).

Also, before I forget, I had to tell the StructureMap configuration that all IRequestHandlers should use MyBaseRequestHandler:

For(typeof(IRequestHandler<>)).Use(typeof(MyBaseRequestHandler<>));

You'll also notice that the last catch block in the Handle method returns a not-properly-typed response. I can confirm that the client does not like this. When I try to Get<>() a specific response from the dispatcher it blows up saying the sequence contains no elements. Ya, that response isn't in there. MyBaseResponse is. So hopefully I can find a way to clean this up as well.

I'd love some input on this, since as I said it's just an early prototype.

Thursday, April 29, 2010

Introduction and something cool

Well since I have been added as a co-author for this blog, I suppose introductions, perhaps rant, and a cool link are in order.

Let us get the introductions out of the way for anyone who might care or what to look at my first post ever....meh that's bullshit. I doubt people care and you can always look at my profile anyway.

Time to begin a rant, well more of a story. I hate WebForms. Having to keep track of an event lifecycle to wrap up a bad abstraction for the web sucks. I completely understand where it came from, why it exists (silly event driven desktop apps), and that there are a lot of controls out there for reuse (paid and free). Recently this hate of it came up again as I had a chance to deal with some code at work.

I got a chance to do a major refactoring of part of our product at work. This part displays dynamic forms. It's all data driven and it pieced together the form with programmatically created controls. The problem was the original system seemed to have been voodoo coded until it work which left any programmer staring at the code wishing they were a psychopath with the address of the author.

I think the big problem here was that the original author didn't really understand the whole lifecycle thing (hell I barely got down...nah I doubt I do). Server side validation was broken because of how the controls were loaded (by the way make sure you create all those dynamic controls again before Page_Load and give them the same Ids), and they ended up fishing through all the post data manually to save stuff.

This misunderstanding bleed through it seems to a lot of areas involving this part of the system. When I got a chance to rework the whole thing everyone felt it came out much better from the code point of view and we now get consistent results along with proper validation. But we might be tracking down some bugs for a little while due to how they originally handled everything.

Really the solution to all this is, just slow down. Everything can't be on fire. You need time to get going, to understand, and you can't have blinders on when you're sprinting. Sometimes you need to stop and think if you're facing problems due to problem or problems due to your solution. I suppose time crunch is usually to blame but (although perhaps a bit extreme) I'll have to quote Lincoln:

"If you give me 6 hours to chop down a tree, I'll spend the first 4 sharpening my axe."

Ok, so I suppose now it's time for something cool.

Vim is awesome and check out this code kata in Python: http://vimeo.com/8569257

I hope that came out mostly comprehensible because my eyes were actually losing focusing during the writing of this post.

Excel Interop - Not New, But New To Me

If you're like me, you've managed to avoid using COM Interop libraries for a long time. When business users want rich text from a web app, you give them HTML. When they want to open something in Excel, you give them a CSV. They just copy/paste stuff, they'll never know the difference. Right?

Well, eventually this catches up with you. Eventually you're going to have to click on that COM tab in the Add Reference dialog and see all it has to offer. Some of it may even look interesting or exciting. Sure, it's always exciting at first. But then you find yourself waking up in a dumpster in Mexico with no useful intellisense on a function that takes 12 parameters, all of type object. You don't know what happened last night, all you have are some Google searches and vague memories of DLLs that had to be underage.

But when the business users specifically want multiple reports on multiple sheets in the same Excel file that they load into some other software, CSV just isn't going to cut it anymore. You're going to have to add a reference to Microsoft Excel 12.0 Object Library and you're going to have to get dirty.

Luckily, if you can convince the business to keep it simple, it won't be so bad. Here's some code I slapped together today to perform the simple task of writing to some worksheets (the very word makes me shudder) in an Excel file:

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using Microsoft.Office.Core;
using Excel = Microsoft.Office.Interop.Excel;

namespace ExcelOutputting
{
  public class ExcelOutputter
  {
    public void WriteAFile(List report)
    {
      // Instantiate the workbook
      var xlApp = new Excel.ApplicationClass();
      // This will be important later
      xlApp.DisplayAlerts = false;
      // From reflection, you'll use it a lot when you have to pass
      // something but don't have anything to pass.
      // I guess COM is like that.
      var mis = Missing.Value;
      var xlWorkbook = xlApp.Workbooks.Add(mis);

      // Create an extra worksheet after the last one.  Remember, there
      // are always 3 already in there by default.  And it's 1-indexed.
      xlWorkbook.Worksheets.Add(
        mis,
        (Excel.Worksheet)xlWorkbook.Worksheets.get_Item(3),
        mis,
        mis
      );

      // Name the worksheets
      ((Excel.Worksheet)xlWorkbook.Worksheets.get_Item(1)).Name = "Jan 2010 Report";
      ((Excel.Worksheet)xlWorkbook.Worksheets.get_Item(2)).Name = "Feb 2010 Report";
      ((Excel.Worksheet)xlWorkbook.Worksheets.get_Item(3)).Name = "Mar 2010 Report";
      ((Excel.Worksheet)xlWorkbook.Worksheets.get_Item(4)).Name = "Apr 2010 Report";

      // Edit worksheet for January
      var xlWorksheet =
      (Excel.Worksheet)xlWorkbook.Worksheets.get_Item(1);
      for (var i = 0; i < report.Records.Count, i++)
      {
        // Remember, 1-indexed
        xlWorksheet.Cells[i+1, 1] = report.Records[i].SomeValue;
        xlWorksheet.Cells[i+1, 2] = report.Records[i].SomeOtherValue;
        //... and so on...
      }
      //... and so on...

      // Save the file
      xlWorkbook.SaveAs(
        report.DestinationFile,
        Excel.XlFileFormat.xlWorkbookNormal,
        mis,
        mis,
        false,
        false,
        Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlNoChange,
        Excel.XlSaveConflictResolution.xlLocalSessionChanges,
        false,
        mis,
        mis,
        mis
      );
      // I told you you'd use that value a lot.  Now, there are many
      // ways you can go about saving.  This one, in conjunction with
      // that xlApp.DisplayAlerts = false setting from earlier will
      // save to the file name (fully qualified, it doesn't always
      // default to the current directory) in the first argument,
      // overwriting without prompt.
      // This is especially important in a web environment where the
      // user won't see the prompt on the server.  Yes, the application
      // will show a File Already Exists dialog asking you to overwrite
      // or cancel on the console of the machine running this.  You'll
      // want to handle your file saving conflicts yourself.

      xlWorkbook.Close(true, mis, mis);
      xlApp.Quit();

      releaseObject(xlWorksheet);
      releaseObject(xlWorkbook);
      releaseObject(xlApp);
    }

    // Found this online, might be a good idea to make sure
    // when using COM
    private static void releaseObject(object obj)
    {
      try
      {
        Marshal.ReleaseComObject(obj);
        obj = null;
      }
      catch (Exception ex)
      {
        obj = null;
        Console.WriteLine("Exception Occured while releasing object: " + ex.ToString());
      }
      finally
      {
        GC.Collect();
      }

    }
  }
}


You get the idea. There's actually a lot of formatting you can do hidden within the bowels of this library, but of course it'll never be as robust as the business users think it will be when they use the _actual_ Excel interface. You'll have to convince them that the pretty colors and effects aren't necessary. You can do some of it, sure, but not all of it.

Sure, this isn't a big deal. Excel interop has been around and used by many since the beginning of time in .NET. But, like I said, you may have managed to avoid it for as long as I have. And maybe all you're looking for is a quick example of how to get the damn thing working, rather than a search result where somebody else asks the question and the only answers they've received are "just use COM" or some other nonsense that, had they known what that meant, they wouldn't have asked the question in the first damn place.

Frst Post!!1!

So this is what it's like in the blogosphere, whatever that is. I guess I was expecting more crushing, or perhaps the clamps. But as it turns out, trying to host a blog is just a natural step in the career of a software developer. You've seen some of the popular ones, and they're successful because they traditionally offer rich morsels to be consumed and digested, possibly after being stored in your RSS bile sac. And now you find yourself here.

Why? What can I possibly offer? Well, I do fancy myself a software developer of sorts. And in my spare time between support tickets and change requests to legacy software I actually get to work on interesting things. Some of these problems may have been solved before, some of them may have better solutions available, etc. That's all part of the learning process.

I've been lucky enough to work with some talented folks during my career, and hopefully they'll contribute to this blog and share their intellectual warez. But as for me... I'm not a rock star or an industry pioneer, I'm a real life developer. And this is the stuff that I do.