Monday, December 22, 2008

Using Dependency Injection with CSLA.NET

In this post, I'm going to talk about one way to implement Dependency Injection with CSLA.NET
NOTE: My discussions and code samples are using CSLA.NET 3.5.

Background

CSLA.NET.  I'm a big fan.  I have been even back before it went .NET in the VB5 and VB6 days.
I don't know actual statistics, but I would wager that CSLA.NET is probably the most popular framework for creating object-oriented business objects in .NET.  If you're building even a moderately complex line-of-business application where you want your business logic to live in one place (instead of spread all over the place, the worst being in your UI), you want a middle tier that takes advantage of object-oriented design (not just a set of classes that mimic the structure of the database), and optionally have everything just work in a distributed environment, then you should definitely take a close look at CSLA.NET.
I'm also a big fan of automated tests around my business layer.  And when I say tests, I'm referring to unit tests, where I want to validate just the behavior of my business object and nothing else.  In other words, I'm not interested in testing the other layers that my business object may interact with (like the database or a logging component).  Those would be integration or regression tests.  Those are good too, by the way, but unit tests should come first.
Since business objects almost always have to communicate with other layers, your unit tests need the ability to fake those layers out in order to test your business logic in isolation.  There are variety of ways to do this, but a popular one is to abstract calls to these layers using Dependency Injection and then use a mocking framework to inject a fake version of that layer.  To really do Dependency Injection, you need to use a framework of some kind (typically called an IOC Container) to handle your dependency instances and automatically perform injections on your objects.

Problem

So, like I said, I really love the CSLA.NET framework.  But with all of its awesome excellence, I've always been challenged writing true unit tests against business objects that use it.  This is because CSLA.NET makes it difficult to implement Dependency Injection.  Why?  In short: CSLA.NET creates your business object instances for you.  This happens in the servers-side portion of the DataPortal.  Dependency Injection works best if it can have some kind of access to the object creation process so an object's dependencies can be injected.  Unfortunately, the DataPortal does not provide any sort of hook where you can create your object.  The DataPortal does this all behind the scenes as a black box and then gives you your object instance after creation, letting you then execute your data-access code in the DataPortal_* methods.
To be fair, the latest version of CSLA.NET, 3.6 which just released, does give you access into the creation of your business objects via the new object factory feature.  However, there are two challenges with going down this road:
  1. You end up having to write a lot more than just object creation into your object factory classes since the object factory is also responsible for performing all data access including the state population of your business objects. 
  2. You have to write a factory class for every business object that requires dependency injection.  Couple this with the first point, and that adds up to a lot more coding just so you can have control over the creation of your business objects.
There is also another challenge, and that is that the creation of objects on the server-side of the DataPortal isn't the only place business objects in CSLA.NET are being created!  One of the great features of CSLA.NET is this idea of mobile objects: your business objects have the ability to be transported from one physical tier to another.  However, every time an object goes mobile it needs to be serialized.  When it gets to the destination tier, it needs to be deserialized and deserialization involves recreating the object instance except this time its done within the bowels of the .NET Framework.  Yuck!  Even more difficult to hook into.  And by the way, as of CSLA.NET 3.5, for consistency this serialization/deserialization process even occurs when everything happens in-process on one physical tier.

Solution

The solution I finally stumbled upon essentially gave up on the idea of trying to hijack the object creation process either in the DataPortal or worse yet in the .NET Framework itself during deserialization. 
The good news is that most IOC Container frameworks out there allow you to perform dependency inject with an existing object instance.  In my case, I'm using Unity, the IOC Container from Microsoft, which is a member of their Application Blocks family.  Unity's container object has a method called BuildUp that let's you do exactly this.
And the other piece of good news is that CSLA.NET allows very easy access to an object instance just after it is created either by the server-side DataPortal or by deserialization.  All you have to do is override a couple virtual methods and you're done!

Implementation

My implementation of this was to create a set of base classes that all inherit from main CSLA.NET base classes that do the necessary overrides and perform the dependency injection.  Let's build the base class for BusinessBase<T>, probably the most used CSLA.NET base class.
First, let's get the basics out of the way.  We'll declare an abstract class called InjectableBusinessBase<T> that inherits from BusinessBase<T> and contains a protected constructor, which all abstract classes should have:
[Serializable]
public abstract class InjectableBusinessBase<T>
    : BusinessBase<T>
    where T : BusinessBase<T>
{

    protected InjectableBusinessBase() { }

}
Now, let's add the necessary overrides to hook into post-object creation so we can kick off our dependency injection:
    protected override void DataPortal_OnDataPortalInvoke(DataPortalEventArgs e)
    {
        Inject();
        base.DataPortal_OnDataPortalInvoke(e);
    }

    protected override void Child_OnDataPortalInvoke(DataPortalEventArgs e)
    {
        Inject();
        base.Child_OnDataPortalInvoke(e);
    }

    protected override void OnDeserialized(StreamingContext context)
    {
        Inject();
        base.OnDeserialized(context);
    }
First we overrode DataPortal_OnDataPortalInvoke.  This gets called by the server-side DataPortal when a root level business object is created.  Then we overrode Child_OnDataPortalInvoke which gets called by the server-side DataPortal whenever a child object is created by the DataPortal (a new feature as of CSLA.NET 3.5 that I highly recommend).  Finally, we overrode OnDeserialized, which, through the help of BusinessBase<T>, gets called by the deserialization process of .NET.
In all three cases, we're calling an Inject method, which is a private method declared in our base class.  It looks like this:
    private void Inject()
    {
        var type = this.GetType();
        Ioc.Container.BuildUp(type, this);
    }
Nothing too fancy here.  We're essentially calling the Unity BuildUp method which requires the type of our business object and the business object instance itself.
What is a little exotic, if you're familiar with Unity, is the Ioc.Container code.  Our base class needs some way to access the Unity container instance against which to call BuildUp.  I've created a utility class called Ioc that has a static Container property that returns this.  I'll include the code for this class as well as all the code for the various base classes at the end of this post.  For now, just know that the code Ioc.Container returns our IOC container.
That pretty much is all that's required to make dependency injection  work with CLSA.NET and Unity!  However, I do have one more method I want to include in my base class:
    protected TDependency EnsureDependency<TDependency>(TDependency dependency) 
        where TDependency : class
    {
        if (dependency == null)
            throw new MissingDependencyInjectionException(this.GetType(), typeof(TDependency));
        return dependency;
    }
The purpose of this method will become a little more clear when we take a look at an example concrete business object class that inherits from InjectableBusinessBase<T>.  Essentially all this method does is return the same object that gets passed in.  However, before it returns it, the method checks to make sure the object is null; if so, it throws a custom exception that basically says a required dependency instance is missing that it's of a certain type and is contained within the object of this type.  I'll include the source code for this exception class at the end of the post as well.

Sample Concrete Class

To make all this gel, let's code up a concrete business object class that inherits from our base class.  I'm going to code up a very simple Customer class that has two properties, ID and Name, and a single Fetch factory method.  Here's the code so far:
[Serializable]
public sealed class Customer
    : InjectableBusinessBase<Customer>
{

    private static PropertyInfo<int> IdProperty = 
        RegisterProperty<int>(new PropertyInfo<int>("Id"));
    public int Id
    {
        get { return GetProperty<int>(IdProperty); }
    }

    private static PropertyInfo<string> NameProperty = 
        RegisterProperty<string>(new PropertyInfo<string>("Name"));
    public string Name
    {
        get { return GetProperty<string>(NameProperty); }
        set { SetProperty<string>(NameProperty, value); }
    }

    private Customer() { }

    public static Customer Fetch(int id)
    {
        var criteria = new SingleCriteria<Customer, int>(id);
        return DataPortal.Fetch<Customer>(criteria);
    }
    
}
Nothing new here other than the class inherits from InjectableBusinessBase<Customer>.  We have two properties declared using the new CSLA.NET 3.5 managed property syntax.  Then we have the standard private constructor and the static factory method which uses the DataPortal to create and fetch our business object from the database.
Now I want to introduce a dependency into my class that performs the actual data access necessary to fetch the customer, which will be performed in a standard DataPortal_Fetch method.  This data access object will implement a custom interface called ICustomerDataAccess and will have a method called FetchData that takes a customer ID and returns a DTO that contains my customer data.  Here's what my ICustomerDataAccess interface might look like:
public interface ICustomerDataAccess
{
    ICustomerDto FetchData(int id);
}
By the way, a DTO (data transfer object) is just an object that contains nothing but data.  For mocking purposes, my DTO is an interface (called ICustomerDto). 
In an upcoming post, I'll show a more elegant way to abstract away the data access layer in CSLA.NET using dependency injection and the Repository pattern.
Let's add a field to my class that will contain this dependency:
    [NonSerialized]
    [NotUndoable]
    private ICustomerDataAccess _dataAccess;
Notice that I've added two attributes to this field.  The first, NonSerializedAttribute, is important because we don't want this dependency (or probably any dependency) to be serialized if the business object were to become mobile and transferred from one physical tier to another.  In the case of data access, it really only needs to occur on the tier that performs data access (i.e. an application server).
The second attribute, NotUndoableAttribute, comes from the CSLA.NET framework.  It tells CSLA.NET that this field is not to participate in the undo functionality provided by the BusinessBase<T> class.  This is because the dependency does not contain any business object state that would need to be rolled back if the users were to perform an Undo.
Having both of these attributes in place essentially relieves us of the need to make our dependency serializable.  The actual implementation class of ICustomerDataAccess can be just a plain old object.
Now we need a way to inject this dependency into our business object.  Unity gives you three ways: constructor, set-able property, or method.  We can't use the Customer constructor since we're performing the dependency injection after the instance has been created by the DataPortal or deserialization.  We could use the set-able property.  However, I personally like the method approach since, if you have multiple dependencies, you can inject all of them in a single call. 
We just have one in our example, so here's the code:
    [InjectionMethod]
    public void Inject(ICustomerDataAccess dataAccess)
    {
        if (dataAccess == null)
            throw new ArgumentNullException("dataAccess");
        _dataAccess = dataAccess;
    }
My method can be named anything, but it needs to be decorated with the special Unity InjectionMethodAttribute and it needs a parameter for each of my dependencies so I can set the fields.  Notice I also throw an ArgumentNullException if whatever is doing the injection passes in a null.  We want to fail if this ever happens.
Finally, we need to add the DataPortal_Fetch method that will use our data access dependency:
    private void DataPortal_Fetch(SingleCriteria<Customer, int> criteria)
    {
        var customerData = EnsureDependency(_dataAccess).FetchData(criteria.Value);
        LoadProperty<int>(IdProperty, customerData.Id);
        LoadProperty<string>(NameProperty, customerData.Name);
    }
The first thing we do is call the FetchData method of my ICustomerDataAccess object to obtain the customer DTO.  Here is where you can see the EnsureDependency method in action that we coded earlier in the base class.  If we wrap the _dataAccess dependency in a call to EnsureDependency, we know we're not going to get a NullReferenceException, which is non-descriptive and hard to debug.  Rather we'll get a more descriptive exception that explains exactly what dependency is missing from what object.
Once we have the DTO, we can simply populate our business object in the standard CSLA.NET 3.5 way.

Conclusion

There you have it!  Dependency Injection and CSLA.NET living peacefully side-by-side. 
If you'd like to take a look at the complete set of source code for this, which includes all the Injectable* base classes, the MissingDependencyInjectionException class, the Ioc helper class, and the sample Customer business object code, you can download it hereBefore the sample code will compile, you will also need to download CSLA.NET 3.5 and Unity 1.2 and update the assembly references accordingly.

UPDATE: The download link is actually now to the larger sample code set that demonstrates the Repository pattern (see these two blog posts for more details).  To see the DI-specific code discussed in the above blog post, check out the CslaRepositoryTest.Core namespace.

Now I'm not pretending that this is the only way to make this work.  If there are other people out there who've made Dependency Injection and CSLA.NET work better, perhaps with a different IOC Container like StructureMap or Castle Windsor, write a comment and let me know!

Monday, December 15, 2008

Trying out PreCode and SyntaxHighlighter

Per Scott Hanselman's recent blog post on the best code syntax highlighter for your blog, I'm giving his suggestion a try, which is to use the PreCode plug-in for Live Writer (a free blog writing GUI from Microsoft), which renders your code snippets so they use the SyntaxHighlighter JavaScript library. 

I host my blog on Google's Blogger and so I don't have an easy way to host the necessary JavaScript files to make SyntaxHighlighter work.  But I do have a Google Pages account, so I was able to upload them there and modify my blog's HTML Template in Blogger to include the necessary <link> and <script> tags to make the magic happen.  I used tips from this blog post to get it working.

I wanted to get this set up since I'm about to post three (yes three!) blog entries on CSLA.NET which will probably contain a fair amount of code.

Anyway, without further adieu, here's my first SyntaxHighlighter code snippet, generated by the PreCode plug-in:

public interface IFoo
{
void Bar();
}

public class MyFoo
: IFoo
{
public Bar()
{
//do something
}
}

NOTE: If you're using a feed reader like Google Reader, this code won't look like anything special.  You'll have to open the blog page directly to see the full effect.

Monday, November 17, 2008

Where's Pete?

Wow, it's been ages since I've posted anything here. Pretty lame. I have a laundry list of stuff I actually want to write about, but I just haven't found the time. Maybe it's cuz we had another baby back in September? Still, no excuse.

Hopefully this will post will kick me in the pants. Feel free to comment and do the same.

Tuesday, June 10, 2008

More than one way to not drive to work

009

Saw this on my ride into work today on the Gateway Trail.  I think I've seen a maybe two Segways ever in my life.  This guy rocks!

Sunday, June 08, 2008

2008 MS150 - Sunday

Today we finished the second half of the MS150.  Hit the road at about 7:30am and crossed the finish line in Blaine at about 12:20pm. 

Today's pace was a little faster than yesterdays, probably due to the slight tail wind instead of a head wind.  We averaged over 20mph for the day and we had one 22 mile run in the morning (where we skipped a rest stop) where we averaged over 22mph!  That was a lot of fun!

008I took a lot of really cool pictures (with my phone), including one over my shoulder while my group was in formation, but somehow (and I'm still perplexed as to how this happened) I lost the memory card in my phone.  Fortunately, I have this one, which my wife Kris took just after the finish with my kids.  This was the highlight of my weekend anyway!  It was great seeing my family cheering me on when I crossed the finish!

 

Well, it was a great event this year.  I'm so glad I decided to join a team and even more glad that I joined the Backdrafters.  What an awesome group (115 riders!) led by a great team captain, Doug.

I've already signed up for next year (got my complimentary MS150 socks).  I'm definitely going to keep riding with the Backdrafters. 

Thanks for everyone who supported me this year.  I increased my donations from last year and brought in over $600!  Next year I want to raise over $1000, which puts you into the Golden Gears Club.  Also, anyone reading this who would want to experience the MS150 for themselves is more then welcomed to do so!  Just let me know.  You can contact me via email which you can get by viewing the homepage of my blog here.

Saturday, June 07, 2008

2008 MS150 - Saturday Evening

Beer pool!That is a kiddie pool full of beer (with a mini-keg of Heineken in the middle). This was the first thing I saw when we entered the Backdrafters tent. Man, that first beer tasted good!

The snack table Hydrating after the long ride

Cap'n Doug enjoying a cold one There was all the beer, water, and snacks you could consume! What a refreshing end to the day. Even Cap'n Doug took a few minutes and enjoyed a cold one.

At 5pm they served a catered diner and we ate like kings and queens!

Bike farm

Now we and the bikes rest until tomorrow where we do the second half. The weather report looks great: No rain and 10-15 mph NW tail wind!

2008 MS150 - Saturday Ride

Leaving Black Bear Casino

We left Carlton this morning at about 7:30 am. I was very excited to ride, but nervous to see if I'd be able to keep up with my partners. Landon (far right) is a biking freak and claims to have drafted many a semi truck doing 50+ mph!

First pedals

So we hit the road and in about 36 minutes and 10.17 miles later we hit our first rest stop (which was actually rest stop #2 since we started in Carlton instead of Proctor). Our average speed was 17 mph. Not too bad for the warm-up ride. Cory (right, below) decided it was wise to put some air in his tires at this point.

Rest Stop #2

After the first rest stop we picked up the pace and averaged 18.6 to rest stop #3, biking 11.34 miles. Also I did some pulling (led our group) and was actually able to maintain a decent pace!

At 8:50 we reached rest stop #3 and took a quick breather. The next stop would be lunch!

With our 10.05 mile run to lunch our average speed increased again to 18.7 mph! The Subways were great!

Rest Stop #4 (Lunch) Yum!  Subway!

And a bit filling. Our 9.13 mile run from lunch to rest stop #5 was a little slower (17.4 mph). It was definitely getting warmer. Landon (right, below) was tired of my camera phone.

Rest Stop #5 It's Pete

Our last run was the going to be the longest (15.6 miles). Amazingly we all hit a major second wind! At one point we were consistently holding 24 mph, all of taking our turn pulling, and still doing that with a head wind!

And finally, the finish:

Hinckley finish line

Overall we averaged about 19 mph and arrived in Hinckley at about 11:45am (I think I arrived at Hinckley last year about 3pm)! And I kept pace the entire day! I owe most of that to the awesome bike that my boss, Kerry, borrowed me for the event: A 2005 (I think) Bianchi Cross Concept.

Now it was time to check into the hotel, relax a bit, and check out the infamous Backdrafters tent...

2008 MS150 - Friday

007 008

Loaded the bikes in our own rental truck, hopped on the bus, and made it up to Carlton, MN last night with the Backdrafters.

013

Spent the night in the Black Bear Casino Hotel, and now I sit in the hotel lobby with my laptop mentally gearing up for the first day of riding with a cup of coffee.

Our team has a bit of a unique situation in that they're not starting in Proctor like the rest of the 3500 MS150 bikers. Doug, the team captain, wasn't able to book enough hotel rooms in Proctor for the team and so switches us over to Carlton, which is about 15 miles down the trail, making today's ride a little shorter than 75 miles (this is one reason why I chose to bike from home to National Sports Center yesterday, which was about 16 miles).

009

I've already met a lot of great people on the team. I met Doug (above) right away; he's a great guy.

012There was also someone practicing their accordion in the parking lot yesterday afternoon before we left Blaine (I'm assuming that's going to be used tonight in Hinckley for the festivities).

I also met my roommates: Landin (our room captain), Cory, John, and Dillan. The cool thing is that it sounds like all of us want to try to ride together. Ladin is a pretty aggressive rider; he's done both the Minnesota Iron Man and the MS TRAM (Trek Across Minnesota).

It should be a fun day of biking!

Friday, June 06, 2008

Starting the 2008 MS150

015

Well, it's that time of year again!  Last year was so much fun I had to do it again.  This will be my second Minnesota MS150 bike ride.  I took this photo this morning before I left the house just before I biked to the National Sports Center in Blaine where everything starts. 

I've been fortunate enough to join a team this year (Jeff wasn't able to attend due to his new kid!) called the Backdrafters.   Instead of tenting I'm going to be doing the hotel thing Friday (tonight) and Saturday, which means I was able to pack a lot less stuff.  I got it all in that gray backpack.

Well, I hope to do some actual blogging of the event over the weekend with some pictures from my phone.  Right now I'm sitting at a Caribou right next to the National Sports Center.  In about an hour I head over to get registered, meet up with my team (none of whom I've physically met yet), and hop on the bus for Duluth.  And then it's an early rise to start turnin' pedals (as Doug, the team leader, sez) on Saturday.

And a huge thanks to everyone who's contributed to this event!

Monday, May 12, 2008

Upgraded to Vista

This may come as a surprise to many people, but I've just now upgraded my main laptop from Windows XP to Windows Vista.  Why the wait?  Two things: First, I wanted to wait until SP1.  Second, I wanted to wait until I had more RAM.

Regarding that second point, my laptop had 2GB RAM, which seems like plenty.  However, I was maxing it out pretty easily with XP when running a 1GB Virtual PC (which is how I do all of my software development).  So I upgraded to 3GB, which seems to give me just enough breathing room, even with Vista.  I would have gone to 4 (which my ThinkPad T60 supports), but discovered that the extra 1GB was pretty much a waste with 32-bit Vista given issues with hardware memory mapping.  64-bit Vista apparently doesn't suffer as bad from this issue, but, alas I do not have a 64-bit processor, yet.

The good news is that on the same machine I was running XP Pro, Vista seems to perform just fine.  In fact, it seems noticeably faster than XP was, which is a bit counter to popular opinion.  Of course a clean install of any OS always seems to run fast.  We'll see how things go.

Monday, April 14, 2008

Code Camp Presentation Posted - Tapping the Power of LINQ

Well, I had the opportunity to present again at the Twin Cities Code Camp, this time #4, which was for Spring 2008. I did a general talk on LINQ including LINQ to Objects, LINQ to XML, and LINQ to SQL.

Anyway, thanks again to Jason Bock of Magenic Technologies for organization this event. From what I heard, we had a record turn out!

If you want to download the presentation and/or check out the sample code, you can do so here.

Thursday, April 10, 2008

Managing Unhandled Exceptions in ASP.NET Web Services

We've been working on building a web services at my current project at work for quite some time now and we're finally wrapping things up. One of the items to unfortunately get addressed toward the end of the project was error handling and logging (doesn't it always). And to my dismay the error handling scenario for ASP.NET Web Services (even in .NET 3.5) is not as pretty as it is with a standard ASP.NET Web Forms website.

Specifically, you can't just add a few lines of code to the Global.aspx.cs file in the Application_Error event handler method to log unhandled exceptions because the ASP.NET Web Services pipeline bypasses this event so it can return all the exceptions back to the caller. It does this by wrapping the unhandled exception in a SoapException, which ultimately gets deserialized as a SOAP fault. Out of the box, there is very little you can do with these unhandled exceptions before they are sent to the client. For example, there is no quick and easy way to log them like you can with .aspx pages.

The SoapExtension Solution

The most popular approach to solving this problem (that I've found so far) is to code up a custom SoapExtension subclass, which privodes you low-level access to the SoapMessage at different stages of the web service request and response. When the unhandled exception occurs, the SoapException can be captured in the response SoapMessage (via the Exception property). The SoapException's InnerException property then contains the actual unhandled exception. At this point you can at log the exception on its way out the door so when you get that email from your tech support department, you've got something to go on.

But if you've ever developed a real public-facing web service (like ours is), you'll quickly agree that simply logging the exception isn't enough. Often times you want a little more control over the contents of the SoapException itself that gets sent back to the caller. The SoapExtension subclass approach gives you the ability to modify the SoapException, but at a very low level. Specifically, it's already been deserialized into its representative SOAP fault XML and you have to do some fancy stream manipulation to insert any custom content into that fault (ex: add a detail element). In my opinion, this is a hack and not a very ellegant solution. Instead we should have more control of the SoapException before it gets thrown. Ideally if we could create and throw the SoapException ourselves, we could have much more control over the contents of the resuling SOAP fault (like the fault code and detail). Then we wouldn't have to bother with intercepting and manipulating the raw SOAP messages themselves.

Client-Caused vs. Server-Caused Errors

A great example of this is when you want to return a client-caused SOAP fault vs. a server-caused SOAP fault. A client-caused SOAP fault is one caused because the client sent invalid data in their request. For example, they're not authenticated or they sent a null for a value of a required web method parameter or data transfer object (DTO) property. The SOAP fault infrastructure allows you to classify this kind of error by throwing a SOAP fault with a Code = Client (or Sender in SOAP 1.2). It's also helpful to send information in the detail section of the SOAP fault about what is invalid and how to fix it. It's up to the client to then fix their request before sending it again; otherwise, they'll get another client-caused SOAP fault.

A server-caused SOAP fault is one where something actually bad happenned on the server. In this case the client is really helpless to fix the issue. Operations or an engineer on the development team needs to determine the issue and fix it on the server side. When this kind of error occurs, you want very little detail to be sent to the client, certainly not a full stack trace! Rather you want a simple error message like "An unexpected error has occurred on the server." Optionally you could also send some kind of error identifier, which the client could use when communicating with customer support so that the error can easily be referened in the server logs. Like the client-caused SOAP fault, the server-caused SOAP fault has a special fault Code = Server (or Receiver in SOAP1.2).

The Try/Catch Solution

The best way to implement catching all exceptions and throwing your own SoapException is with a try/catch around the code that can cause the unhandled exception, which essentially is every web method in your web service. Unfortunately, since there's no single place in code you can do this, you're faced with putting try/catch statements around the logic of every single web method. The code for one particular web method might look like this:


[WebMethod]
public string WebMethod1(int arg1, int arg2)
{
try
{
//web method logic
}
catch (ExceptionType1 ex)
{
//handle ExceptionType1
}
catch (ExceptionType2 ex)
{
//handle ExceptionType2
}
catch (Exception ex)
{
//handle all other exceptions
}
}

While this works, it's a ton of duplicated code, especially all the logic in the catch statements. Wouldn't it be nice if there was a way to add a little bit of code to each web method but put the bulk of the error handling logic in a single method somewhere else?

Well, there is!

The Try/Catch Using Anonymous Methods Solution

The trick is to put all of your even handling logic (including the try/catch) into a separate utility method and then invoke that method by passing it a delegate to your web method logic. This utility method might look something like this:


private T Execute<T>(Func<T> body)
{
//wrap everything in common try/catch
try
{
return body();
}
catch (SoapException)
{
//rethrow any pre-generated SOAP faults
throw;
}
catch (ValidationException ex)
{
//validation error caused by client
ClientError innerError = new ClientError();
//TODO: populate client error as needed
//throw SOAP fault
throw this.GenerateSoapException(
"An error occurred while validating the client request.",
SoapException.ClientFaultCode,
innerError);
}
catch (Exception ex)
{
//everything else is treated as an error caused by server
ServerError innerError = new ServerError();
//TODO: populate server error as needed
//TODO: log error
//throw SOAP fault
throw this.GenerateSoapException(
"An unexpected error occurred on the server.",
SoapException.ServerFaultCode,
innerError);
}
}

A few things to note about the Execute method above:


  • The delegate type being passed in is the generic Func<TResult> delegate, which is new to .NET 3.5 (Visual Studio 2008/C# 3.0). It works with any method that has no arguments and returns a value of the generic type TResult. If you're coding this in .NET 2.0 you could simply declare your own delegate that has the same signature like this
private delegate TResult Func<TResult>();

  • The same generic type is also specific in the Execute method and is also the return type of the Execute method. And since we execute the delegate and return its result, everything is done in a strongly-typed fashion with no casting. It's even more elegant since the caller doesn't have to explicitly specify the type T since it can be derrived from the actual parameters of the method this is passed (which we'll see in a moment).
  • The error-handling logic expcitly catches and rethrows SoapExceptions. The assumption here is that if something lower down threw an actual SoapException, they probably want that exception to drive the resulting SOAP fault generated by ASP.NET instead of the one we're going to create (in the catch statements that follow).
  • The error-handling logic explicitly catches an exception type that represents all client-caused errors (in this case a made-up exception class called ValidationException). If there are multiple exception types that represent this, then you'd have multiple catches.
  • The final catch (Exception) is a catch-all for everything else, which is, by definition, an unexpected server error. It has a TODO placeholder for logging the error.
  • The last two catch statements employ another utility method that generates the SoapException itself (which I will show shortly). This method uses XML serialization to serialize a custom error object DTO and put it in the SoapException Detail.

So, what would a web method look like that's using this Execute method? Back to our first web method example:

[WebMethod]
public string WebMethod1(int agr1, arg2)
{
return this.Execute(delegate
{
string result;
//do something with arg1 and arg2 and generate a string result
return result;
});
}


Notice how by putting the web method logic within an anonymous method, the call to the Execute method is nothing more than a thin wrapper. Also notice that we don't have to call the Execute method with a string type parameter (i.e. Execute(...)) because it's inferred from the return type of the anonymous method.

The beauty is that now you've got a single place where you can do all of your plumbing-related coding. Error handling is one thing you can do (which was the original focus of this blog post), but you can also do logging, add instrumentation code, or whatever, all in one spot!

And here's the handy code for the GenerateSoapException utility method referenced before:


private SoapException GenerateSoapException(string message, XmlQualifiedName faultCode, object detailError)
{
using (Stream stream = new MemoryStream())
{
using (XmlWriter writer = new XmlTextWriter(stream, Encoding.Default))
{
//build detail XML by serializing detailError object
XmlSerializer serializer = new XmlSerializer(detailError.GetType(), "your-webservice-namespace-here");
serializer.Serialize(writer, detailError);
stream.Position = 0;
XmlDocument doc = new XmlDocument();
XmlElement detail = this.SoapVersion == SoapProtocolVersion.Soap12
? doc.CreateElement("soap", "Detail", "http://www.w3.org/2003/05/soap-envelope")
: doc.CreateElement("detail");
doc.Load(stream);
detail.AppendChild(doc.DocumentElement);
//build exception
return new SoapException(
message,
faultCode,
this.Context.Request.Url.AbsoluteUri,
detail);
}
}
}

Enjoy!

~pete