Mark Gilbert's Blog

Science and technology, served light and fluffy.

OrElse what? Boolean logic and data bound expressions

I honestly don’t know what these things are called in ASP.NET markup:

<% %>

But I remember hearing someone on a podcast describe them as "expression holes".  I haven’t found a better handle for these yet, so that’s what I’m going with.

The other day one of my colleagues, Doug, pinged me on IM.  I walked over and he started out "can you tell me why this doesn’t work?"  Some of the most interesting problems in my job start with that phrase, and this one was no different.

The issue was that he was trying to put a bit of logic into a repeater expression hole.

        <asp:Repeater ID="rptTextIngredients" runat="server">
            <ItemTemplate>
                <li>
                    <%#IsMicroDataIngredient = Container.ItemIndex = 0 OrElse Not rptTextIngredients.DataSource(Container.ItemIndex - 1).OR_Label%>

                    <span <%# IIf(IsMicroDataIngredient, "itemprop='ingredient'", String.Empty) %>>
                        ...
                    </span>
                </li>
            </ItemTemplate>
        </asp:Repeater>

He was using a Protected member of the form called IsMicroDataIngredient to store value that he would then later check in the IIf() statement in the <span> tag.  The problem was that the OrElse wasn’t short circuiting correctly so on the first item – where Container.ItemIndex is 0.  The logic was still trying to evaluate rptTextIngredients.DataSource(Container.ItemIndex – 1), which threw an "out of bounds" exception.

Let’s divert for a moment to talk about operators.  We’ll start with VB’s younger sibling, C#.

***

The "||", or "or" operator, in C# will evaluate the first operand (the one on the left of the operator) first, and if it’s True it won’t even bother with the second operand.  That allows for statements such as the following to be executed safely:

int x = -1;
bool IsValidNumber = (x < 0) || (Math.Sqrt(x) > 1);

This rather arbitrary example works fine, even though x is negative.  The square root function never gets a chance to be evaluated because the first operand, "x < 0" is True.

Now, if we were to port this to VB, our first attempt might look something like this:

Dim x As Integer = -1
Dim IsValidNumber As Boolean = (x < 0) Or (Math.Sqrt(x) > 1)

This will fail because both "(x < 0)" and "(Math.Sqrt(x) > 1)" get evaluated here, and the latter will throw an exception. The “Or” operator in VB does not short circuit.  However, VB has a newer operator called "OrElse" that is truly equivalent to the "||" in C#:

Dim x As Integer = -1
Dim IsValidNumber As Boolean = (x < 0) OrElse (Math.Sqrt(x) > 1)

In this version, the square root portion never gets executed for x=-1 because the first part of the expression is True.  The “OrElse” operator short circuits the expression here.

***

The expression in the repeater expression hole should not have been executing the second half on the first pass.  Doug then tried moving the expression into the IIf:

        <asp:Repeater ID="rptTextIngredients" runat="server">
            <ItemTemplate>
                <li>
                    <span <%# IIf(Container.ItemIndex = 0 OrElse Not rptTextIngredients.DataSource(Container.ItemIndex - 1).OR_Label, "itemprop='ingredient'", String.Empty) %>>
                        ...
                    </span>
                </li>
            </ItemTemplate>
        </asp:Repeater>

Lo and behold, this worked.

I will spare you the details, but after much tinkering with expression holes, both within and without a repeater, and after a couple of discussions with Doug, we believe we’ve figured out why the first version failed and the second worked.  As it turns out, the OrElse operator was doing exactly what it should have been.

The overwhelming majority of the time (in fact, I might go so far as to say ALL of the time) that I use an expression hole, it’s to write something out to the markup that begins life in the code-behind.  For instance, I might detect if the user has logged in, and if so, print their name on the page:

<%=IIf(Me.IsUserLoggedIn, "Hi, Mark", "Greetings!")%>

Or I might put the check in a property, and just do the following in markup:

<%=Me.NavbarGreeting%>

Notice the "=" sign?  That’s shorthand for "Response.Write", that classic ASP standby.  I could easily rewrite the above to:

<%Response.Write(Me.NavbarGreeting)%>

Now, notice that if I use the "=" sign the expression hole will just print out the results of the expression to the page.  If I don’t use the "=" at all, then it executes the line as if it were in code-behind (you can embed server-side logic into the markup in this way, sort of like what we used to do with classic ASP).

When you’re working with a data-bound control like a repeater, you use the "#" sign instead of the "=" (see above).  I’m not clear on all of the differences, but you can’t reference something like "Container" without using "#".  Additionally, using "#" functions like the "=" does – the expression within the hole will be evaluated and then written out as a string.

So, the expression

IIf(Container.ItemIndex = 0 OrElse Not rptTextIngredients.DataSource(Container.ItemIndex - 1).OR_Label, "itemprop='ingredient'", String.Empty)

would be evaluated, and either "itemprop=’ingredient’" or String.Empty gets returned.  The expression hole then prints that out.

Realizing this last part allowed us to finally puzzle out what was happening with this expression:

<%#IsMicroDataIngredient = Container.ItemIndex = 0 OrElse Not rptTextIngredients.DataSource(Container.ItemIndex - 1).OR_Label%>

If this were in the code-behind, this line would be treated as a boolean expression whose value was being assigned to IsMicroDataIngredient.  In the expression hole, however, the entire thing was being treated as an expression that would generate something that could be printed.  So, reading from left to right we have:

IsMicroDataIngredient = Container.ItemIndex = 0 OrElse Not rptTextIngredients.DataSource(Container.ItemIndex - 1).OR_Label

“IsMicroDataIngredient = Container.ItemIndex” evaluates to True.  IsMicroDataIngredient starts out as False and Container.ItemIndex as 0.  The implicit cast would translate that to "False = False", which is True.  Now, if we replace that in the original expression and move on we get:

True = 0 OrElse Not rptTextIngredients.DataSource(Container.ItemIndex - 1).OR_Label

"True = 0" evaluates to "False" after the implicit cast.  Substituting that:

False OrElse Not rptTextIngredients.DataSource(Container.ItemIndex - 1).OR_Label

Since the first operand is False, the engine has to proceed to the second operand, and that causes our exception. Basically, the line was being treated as one long boolean expression.

Like so many scenarios in software development, the computer is only doing exactly what I tell it to do.  So why am I continually surprised when it obeys my ".ActBizarre()" method?

Advertisements

July 5, 2010 - Posted by | Visual Studio/.NET

Sorry, the comment form is closed at this time.

%d bloggers like this: