As I start prepping up Rhinestone to add domain logic, one of the things that I always end up adding in my entity properties and methods is parameter and state validation code that throws exceptions when the expected parameter(s) is not passed or the action taken is not valid for the current state of the component. Like so:
23 public void FTPUpload (string filePath)
25 if (string.IsNullOrEmpty(filePath))
26 throw new ArgumentNullException("filePath");
27 if (File.Exists(filePath))
28 throw new ArgumentException("The file path specified does not point to a valid file.");
30 if (_status != ConnectionStatus.Connected)
31 throw new InvalidOperationException(
32 "Cannot upload the specified file to the FTP server. The FTP Connection has not beein initailzed");
While reading a blog post, I forgot which one, I came across a component that used a re-usable class called Guard. All this utility class had was static methods that checked input arguments for exceptions.
I liked it, the name of the class is very intuitive and having a utility that localized the logic of throwing appropriate exceptions meant that all I needed to provide it was the predicate that would be analyzed if an exception should be thrown or not.
For Rhinestone, I decided to create a similar utility class but use Generics and lambdas to make it flexible enough to handle any exception type and accept complex predicates. So the first change I wanted to do was to allow the consumer of Guard to provide the type of exception to throw if the predicate was true:
11 /// <summary>
12 /// Throws an exception of type <typeparamref name="TException"/> with the specified message
13 /// when the assertion statement is true.
14 /// </summary>
15 /// <typeparam name="TException">The type of exception to throw.</typeparam>
16 /// <param name="assertion">The assertion to evaulate. If true then the <typeparamref name="TException"/> exception is thrown.</param>
17 /// <param name="message">string. The exception message to throw.</param>
18 public static void Against<TException>(bool assertion, string message) where TException : Exception
20 if (assertion)
21 throw (TException)Activator.CreateInstance(typeof(TException), message);
What the Against<TException> does is, if the predicate provided is true then the assertion is valid and it throws an instance of the exception type. So using the Guard class now, I can refactor the above scenario to this:
24 public void FTPUpload (string filePath)
27 "A non-null string pointing to a valid file was expected.");
29 "The file path specified does not point to a valid file.");
30 Guard.Against<InvalidOperationException>(_status != ConnectionStatus.Connected,
31 "Cannot upload the specified file to the FTP server. The FTP connection has not been initialized.");
Now the above scenario looks a lot cleaner and is more intuitive to read. A developer reading that code will intuitively know what the code is intending to do and reads in the form of “Guard against a ArgumentNullExcepton if the file path is null or empty”.
Another improvement I wanted to add was to allow the consumer of Guard to provide complex predicates. Sometimes you need more than just a equality or null check in your code, take the following code:
21 public void NotifyConsumer (IConsumer consumer)
23 string emailAddress = GetConsumerEmailAddress(consumer.Identifier);
24 string smsAddress = GetConsumerSMSAddress(consumer.Identifier);
26 if (string.IsNullOrEmpty(emailAddress) && string.IsNullOrEmpty(smsAddress))
27 throw new ApplicationException(
28 "Both email and SMS notfication address for the consumer has not been specified. The notification cannot be sent");
What the above scenario does is tries to get the email and SMS address of a consumer, and if both are empty then throws an ApplicationException. In this case I could use the existing Guard.Against<TException>(bool predicate, string message) function and pass the if condition in the above block but there wont be any semantic indication that the GetConsumerEmailAddress and GetConsumerSMSAddress method call are guarded and could provide results that are invalid. So I have added an additional overloaded Against method;
24 /// <summary>
25 /// Throws an exception of type <typeparamref name="TException"/> with the specified message
26 /// when the assertion
27 /// </summary>
28 /// <typeparam name="TException"></typeparam>
29 /// <param name="assertion"></param>
30 /// <param name="message"></param>
31 public static void Against<TException>(Func<bool> assertion, string message) where TException : Exception
33 //Execute the lambda and if it evaluates to true then throw the exception.
34 if (assertion())
35 throw (TException)Activator.CreateInstance(typeof(TException), message);
What the above overload does is accept a lambda that returns a boolean as a predicate. If after executing the lambda the result is true, it throws the exception specified by the TException type argument. With the above overload I can now refactor the above scenario to this:
21 public void NotifyConsumer (IConsumer consumer)
23 string emailAddress = string.Empty;
24 string smsAddress = string.Empty;
27 (() =>
29 emailAddress = GetConsumerEmailAddress(consumer.Identifier);
30 smsAddress = GetConsumerSMSAddress(consumer.Identifier);
31 return string.IsNullOrEmpty(emailAddress) && string.IsNullOrEmpty(smsAddress);
33 "Both email and SMS notification address for the consumer has not been specified. The notification cannot be sent."
The above now semantically reads “Guard against an ApplicationExcepton when getting the consumers email address and getting the consumers SMS address both return a null or empty string”.
In the spirit of keeping things simple, for now the above two overloads will should cover 90% of all scenarios where I need exception checking. I have added a Rhinestone.Shared project Rhinestone’s source which contains the Guard.cs class. I also added some additional methods, InheritsForm and Implements that I would need sometimes to do type checking.
As always, you can get the source for Rhinestone here… www.codeplex.com/rhinestone