Functional .NET – Lose the Magic Strings

In this current series that I’m running as a follow-up to my presentation last week on Applied Functional Programming, we’re talking about ways we can improve our code with functional techniques.  The ideas presented here are things that are being used in applications today, and learning from them can help you write more concise and flexible code. 

Let’s get caught up to where we are in the series:

The War on Magic Strings

Many in the .NET world were introduced to the power of mocking through the NMock and NMock2 library.  The latter was based upon the Java implementation of jMock.  In order to mock an object, we might have to do something like the following:

[Fact]
public void TransferFunds_Should_Convert_Currencies()
{
    var mocks = new Mockery();
    var currencyService = mocks.NewMock<ICurrencyService>();
    var accountService = new AccountService(currencyService); 

    var account1 = new Account("12345", "USD");
    var account2 = new Account("54321", "EUR");
    account1.Deposit(100);
    
    Expect.Once.On(currencyService).
        Method("GetConversionRate").
        With("USA", "EUR").
        Will(Return.Value(0.74));

    accountService.TransferFunds(account1, account2, 100);

    Assert.Equal(0, account1.Balance);
    Assert.Equal(74, Account2.Balance);
    mocks.VerifyAllExpectationsHaveBeenMet();
}

The problem with the above code is that given our mock object, in order to set expectations, we had to set a string as the method name and then pass the parameters as an extra step.  This of course was clumsy and could cause all sorts of problems due to lack of static verification through compilation.  These strings which set the method name are what are considered to be magic strings, which is to say something to represent something more concrete, so, very weakly typed.

Along came Rhino Mocks and Moq which were able to solve this problem by introducing Action<T> delegates into the syntax and recording the behaviors.  This allows you to grab the object inside the lambda to set the projection for which it will record.  With Rhino Mocks, we were able to take that code and rewrite to use lambdas to capture our expectations and stubbed values to something like this:

[Fact] 
public void TransferFunds_Should_Convert_Currencies() 

    var currencyService = MockRepository.GenerateMock<ICurrencyService>(); 
    var accountService = new AccountService(currencyService); 
    currencyService.Expect(x => x.GetConversionRate("USA", "EUR"))
        .Return(0.74);

    var account1 = new Account("12345", "USD"); 
    var account2 = new Account("54321", "EUR"); 
    account1.Deposit(100); 
    
    accountService.TransferFunds(account1, account2, 100); 

    Assert.Equal(0, account1.Balance); 
    Assert.Equal(74, Account2.Balance); 
    currencyService.VerifyAllExpectations();
}

To understand how it works requires a bit of understanding on how it records the values and plays them back to you.  The point is that through the use of lambdas, we are able to analyze our code and react properly instead of the wild guesses that were required with the magic string laden NMock2 API.

Fast forward to ASP.NET MVC.  In the 1.0 release that just happened this week, we have the ability to describe controls that bind to our view models pretty easily through the use of extension methods.  For example, to create a bound text box, we can use the following extension method to the HtmlHelper class:

public static string TextBox(
    this HtmlHelper htmlHelper, 
    string name, 
    object value)

This allows us to specify the name and the value of our new text box.  Then we can use this API to create a text box rather easily in our view:

<%= Html.TextBox("FirstName", Model.FirstName) %>

Pretty easy enough, but once again, we’re dealing with a magic string issue.  If we already have a strongly typed view, which I tend to do, then why can’t we utilize this to determine both the name and the value of our given input?  The answer is, we can! 

With the release of MVC 1.0 also came the release of the ASP.NET MVC Futures.  In this library, we are indeed able to specify this exact behavior with the help of the ExpressionInputExtensions class.  This gives us the ability to add hidden fields, text boxes, text areas, drop down lists, and validation messages.  Now we can write controls such as the following:

// Drop down list using our AddressViewModel
<%= Html.DropDownListFor(x => x.Address.State, Model.States) %>

// Text box using our PersonViewModel
<%= Html.TextBoxFor(x => x.Person.FirstName) %>
 

With this code, we are now able to not only grab the name of the item, but also the value?  How?  Through the power of Expressions that were introduced as part of .NET 3.5 of course.  Let’s look at the text box version first.  In order to call the normal TextBox method as we have above, we just need to determine both the name of the property and the value such as the following code:

public static string TextBoxFor<TModel, TProperty>(
    this HtmlHelper<TModel> htmlHelper, 
    Expression<Func<TModel, TProperty>> expression, 
    IDictionary<string, object> htmlAttributes) 
        where TModel : class 
{
    var inputName = ExpressionHelper.GetInputName(expression);
    var value = GetValue(htmlHelper, expression);
    return htmlHelper.TextBox(inputName, value, htmlAttributes);
}

But how do we actually get that name?  Let’s dig now into the ExpressionHelper.GetInputName to find the answer there.  The answer is actually simple.  First, if the expression is a call type, then we want to dig deeper into the body to extract the name minus the parameter name we’re using (most cases we tend to use x for whatever reason).

public static string GetInputName<TModel, TProperty>(
    Expression<Func<TModel, TProperty>> expression) 
{
    if (expression.Body.NodeType == ExpressionType.Call) 
    {
        var mce = (MethodCallExpression)expression.Body;
        var name = GetInputName(mce);
        return name.Substring(expression.Parameters[0]
            .Name.Length + 1);
    }

    return expression.Body.ToString()
        .Substring(expression.Parameters[0].Name.Length + 1);
}

private static string GetInputName(
    MethodCallExpression expression) 
{
    var mce = expression.Object as MethodCallExpression;
    if (mce != null
        return GetInputName(mce);

    return expression.Object.ToString();
}

With some simple operations using expressions, we’re able to analyze the code quickly to get the name.  But, what about the value?  That’s also pretty simple.  First, we check for null values, and if so, we return the default for our property type.  Else, we compile the expression and return the value.  The GetValue looks something like this:

internal static TProperty GetValue<TModel, TProperty>(
    HtmlHelper<TModel> htmlHelper, 
    Expression<Func<TModel, TProperty>> expression) 
        where TModel : class 
{
    var model = htmlHelper.ViewData.Model;
    if (model == null)
        return default(TProperty);

    var func = expression.Compile();
    return func(model);
}

What that does is allow us to bind nicely to our model without the use of magic strings.  For example, we could bind to my Person.FirstName in my PersonModel and it would render something like this.

<input id="Person_FirstName" name="Person.FirstName" 
type="text" value="Matthew" />

But is there room for extension?

One Change Of Many

One slight change I’d like to see here is to allow for custom formatting.  For types such as dates, numbers, etc, formatting can be important.  To allow for this, we need to make a slight modification to our TextBoxFor extension method to allow for that format.  Then, we could rewrite it as the following:

public static string TextBoxFor<TModel, TProperty>( 
    this HtmlHelper<TModel> htmlHelper,  
    Expression<Func<TModel, TProperty>> expression,
    string format,  
    IDictionary<string, object> htmlAttributes)  
        where TModel : class  

    var inputName = ExpressionHelper.GetInputName(expression); 
    var value = GetValue(htmlHelper, expression);
    var formatted = string.Format(CultureInfo.CurrentCulture, 
        format, value);
    return htmlHelper.TextBox(inputName, formatted, htmlAttributes); 
}
 

Then we can use this new extension method to write such things as the following:

// Formatting in month/day/year
<%= Html.TextBoxFor(x => x.Person.DateOfBirth, "{0:MM/dd/yyyy}") %>

// Expected results
<input id="Person_DateOfBirth" name="Person.DateOfBirth" 
type="text" value="01/01/1980" />
 

Once we get going, it’s easy to find these extension points to further improve our code.  Where else are there opportunities?  Well, plenty, but that’s for another time.

Conclusion

So, as you can see, with the proper application of closures, much as I covered before, we have new and pretty interesting ways of extending our applications.  If Func<TModel, TProperty> is the secret sauce then most definitely Expression<Func<TModel, TProperty>> is love.  I hope you enjoyed the little foray into how through the use of functional programming features such as closures and lazy evaluation that we can enrich the behaviors of our applications.  Once we understand these fundamentals, then finding more opportunities for extension will come.  As I stated earlier, I don’t think necessarily that FP versus OOP is a either or battle where OOP is co-opting many functional features as the languages mature as we understand the power and elegance of their solutions.

6 Comments

  • In your first example you use "USD", "EUR", "USA"... magic strings. :-)

  • Yes, * sigh * I know, but I took this example with the intention of only fixing the mocking magic strings from NMock2's example. There are ways of fixing those others, but that's another task...

    Matt

  • I downloaded the MVCFutures dll, and I see Html.TextBoxFor in intellisense, but not Html.DropDownListFor...weird. Any tips?

  • Cool technique and brings a bit of meta-programming to C#.

    One issue I have is with the body of the lambda expression. As far as I can tell, the developer could put anything they want in there, as long as it returns the right type. For example, this would be legal:

    "FooBar") %>


    Is there a way at compile time to enforce the form of the body of the lambda? Don't get me wrong, this is better than magic strings, but it's still not all the way there.

  • @Justin

    Actually, that wouldn't be legal in terms of actually getting it to work. Why? Because the not only would the id be FooBar but, value of your text box will now be FooBar and I don't think you actually want that. So, your example really doesn't fly at all.

    Matt

  • @Rod,

    It should be there...

    Matt

Comments have been disabled for this content.