April 2007 - Posts

What's Wrong With This Code? (#15)

Mortimer is taking up TDD, and starts a new project by writing the following test.

<TestMethod()> _
Public Sub CanReverseString()

    Dim input As String = "OdeToCode"
    Dim result As String = Utility.ReverseString(input)
    Assert.AreEqual("edoCoTedO", result)

End Sub

Yes, it's another one of those big projects that reverses strings all the time.

With a test in place, Mortimer writes the following stub.

Public Class Utility
    Public Shared Function ReverseString( _
        ByVal input As String) As String
 
        Return Nothing

    End Function
End Class

Good news - the test fails! Now it's time to provide a real implementation...

Public Class Utility
    Public Shared Function ReverseString( _
        ByVal input As String) As String

        Dim chars As Char()
        chars = New Char(input.Length) {}

        For i As Integer = 0 To input.Length - 1
            chars(i) = input(input.Length - i - 1)
        Next

        Return New String(chars)

    End Function
End Class

Mortimer thinks he has everything straight - but the test still fails! What could be wrong with Mortimer's ReverseString?

posted by scott with 15 Comments

The .Net Days Of Old

My association with .NET started over 6 years ago. In those early days I used C++ and COM as a point of reference for everything I was learning in .NET and C#. Destructors versus finalizers, Assembly.Load versus LoadLibrary, Metadata versus COM+ attributes, and of course - memory leaks versus managed heaps.

Those were fun times.

I'm gearing up for a customized Pluralsight class that includes material from the Applied .NET and Applied ASP.NET 2.0 courses. I also need some perspective of a Visual Basic 6.0 developer in a .NET land.

This reading list helps to re-live the days of old (from a VB6 perspective):

posted by scott with 2 Comments

Looping 101

Here is a pop quiz for language aficionados.

Examine the following class:

public class Foo
{
   
public int GetUpperLimit()
    {
        Console.WriteLine(
"GetUpperLimit invoked");
       
return 5;
    }

   
public IEnumerable<int> GetCollection()
    {
        Console.WriteLine(
"GetCollection invoked");
       
return new int[] { 1, 2, 3, 4, 5 };
    }
}

Now examine the following console mode program:

class Program
{
   
static void Main(string[] args)
    {
       
Foo foo = new Foo();

       
for (int i = 1; i <= foo.GetUpperLimit(); i++)
        {
           
Console.WriteLine(i);
        }

       
foreach (int i in foo.GetCollection())
        {
           
Console.WriteLine(i);
        }
    }
}

 Without using a compiler, do you know:  

  • How many times "GetUpperLimit invoked" will appear in the output?
  • How many times "GetCollection invoked" will appear in the output?

Now, examine the same console program in VB:

Sub Main()
   
Dim foo As New Foo()

   
For i As Integer = 1 To foo.GetUpperLimit()
        Console.WriteLine(i)
   
Next

    For Each i As Integer In foo.GetCollection()
        Console.WriteLine(i)
   
Next
End
Sub

 Bonus third question:  

  • Will the output of the VB program be the same as the C# program?

I was suprised by the answer to the last question ...

posted by scott with 9 Comments

Writing Technical Books

The skill of writing is to create a context in which other people can think.

-- Edwin Schlossberg

Jeff Atwood presents a visual contrast of two WPF books in "How Not To Write A Technical Book". I haven't read either WPF book, but Jeff's post did provoke some thinking…

Color Me Code

It's amazingly difficult to read code in monochrome these days. Language keywords, program comments, and string literals all blur into lumps. There is now chance to scan the source code for the important bits. Unfortunately, many publishers are unwilling to accept the higher cost of full color printing (except for technical books in the higher-education market, where the consumer is a PHY 101 student and at the publisher's mercy). Most college textboxes are printed in full color, but full color computer text books are a rare find.

If we look at some currently shipping WPF books, we see they are all heavily discounted from their list price and fall into the same price range:

It seems that publishers are pricing their books based on the competition and not on the production costs. Shrinking margins don't bode well for features like color.

I Have a Tip for You

Another topic in Jeff's post was the use of sidebars and callouts. Some people love sidebars. I don't.

A good technical book makes me think. An even better technical book makes me get to the keyboard to try something. Books with colored sidebars on every page are telling me there is something more important to read and rarely make me think or code. Instead, I'm burning cycles on context switching.

Warning: In 5 billion years, the sun will run out of hydrogen and become a red giant. Make sure your offspring will be located at a safe distance.

I see sidebars used as a copout. The author gave up trying to work an important fact into the technology's storyline, and instead threw the content inside a cartoon box. To be fair, it's not the author's fault. All publishers encourage this behavior. I think the publishers want books to look more like the web…

Can I Get An Ad?

Books are competing with the web, to be certain. Magazines are, too. Many tech magazines are free because they are chocked full of ads. Perhaps book publishers will one day start including advertisements to offset the cost of color printing - I hope not.

posted by scott with 1 Comments

Fleeting Visions Of Doom

Your mind is racing and your fingers are struggling to keep pace. Clickety-clackety-clickety-clackety.

New code, test code, refactored code – it pours across the screen. Clickety-clackety-clickety-clackety.

There is no passing of time in this superb condition of lucidity. Clickety- clackety-clickety-T-T-TWACK.

Opps. What was that?

Something went wrong. A finger slipped! You hit the wrong combination of keys, for sure – but which keys? Was that a menu that flashed by? The intensity of that red disk light fills you with dread. Did you trigger some hidden doomsday command that is now writing 0s to the system drive? When was the last system backup? When was your last commit? Why isn't this computer responding? What on earth is this disk drive doing? Should you cut power now and salvage what you can?

Oh, it's back <exhale>.

It's just the Visual Studio "Add New Reference" dialog.

Cancel.

For a fleeting moment, you thought all was lost.

Now, about that backup….

posted by scott with 4 Comments

What's Wrong With This Code? (#14)

MSDN says the List.ForEach method will "perform the specified action on each element of the List".

The following program does print out a haiku to the console, but does not make the haiku all lowercase.

using System;
using System.Collections.Generic;

class WWWTC
{
    
static void Main()
    {
        
List<string> haiku = new List<string>();

        haiku.Add(
"All software changes");
        haiku.Add(
"Upon further reflection -");
        haiku.Add(
"A reference to String");

        
// make it lowercase
        haiku.ForEach(
                    
delegate(string s)
                    {
                        s = s.ToLower();
                    }
        );

        
// ... and now print it out
        haiku.ForEach(
                    
delegate(string s)
                    {
                        
Console.WriteLine(s);
                    }
        );
    }
}

 What's wrong?  

posted by scott with 12 Comments

Event Validation Errors and Network Congestion in ASP.NET

Joseph Rattz emailed me about posts I wrote last year covering ASP.NET event validation (see ASP.NET Event Validation and "Invalid Callback Or Postback Argument" Part I and Part II). In the posts I describe one scenario where an event validation exception can appear, and describe how to prevent the exception. In the scenario, client side script dynamically adds values to a list (select) control. When the user POSTs the form to the server, ASP.NET sees a new value and concludes something is wrong. The list comes back to the server with a value that wasn't present when the list left the server. ASP.NET tracks the legal values by serializing them into a hidden input control with an ID of __EVENTVALIDATION. This event validation feature helps to prevent all sorts of form spoofing attacks.

Joe's scenario was odd, not only because the invalid postback argument exception appeared sporadically, but because the source of the exception was a TextBox control!

ArgumentException: Invalid postback or callback argument.  ...
  System.Web.UI.ClientScriptManager.ValidateEvent(..) ...
  System.Web.UI.Control.ValidateEvent(..) ...
  System.Web.UI.WebControls.TextBox.LoadPostData(..) ...

 

What could ASP.NET possibly validate about a TextBox? A server can expect specific values from a DropDownList, but a plain TextBox allows free form text entry by the user. The answer (via Reflector), is that the TextBox control only validates that its UniqueID is present in the legal arguments described by the __EVENTVALIDATION data. Even after knowing this information, it's hard to see what could go possibly wrong with a TextBox.

Joe's theory, which I believe to be correct, is that the user might create a postback before their browser even receives the __EVENTVALIDATION form input. This could happen, for example, over a poor connection. The resulting POST won't contain the __EVENTVALIDATION input, and thus ASP.NET cannot validate the postback arguments. The klaxons wail. Glass breaks. The runtime throws an exception.

One way to validate this theory is to simulate network congestion with the following control. We flush out the output stream, sleep, and then continue rendering.

using System;
using System.Web.UI;
using System.Threading;

namespace OTC
{
    
public class FlushAndSleep : Control
    {
        
protected override void Render(HtmlTextWriter writer)
        {
            writer.Write (
"Begin Render<br>");

            
base.Render (writer);

            Page.Response.Flush();

            
Thread.Sleep(2000);
            writer.Write(
"End Render<br>");
        }
    }
}

 

Stick the control into the following page:

<%@ Page Language="C#" %>
<%
@ Register TagPrefix="otc" Assembly="App_Code" Namespace="OTC" %>

<form id="form1" runat="server">
    <div>
        <asp:textbox runat="server" id="TextBox1" />
        <asp:button runat="server" id="Button1" text="Submit" />
        <br />        
        
<otc:FlushAndSleep runat="server" ID="FlushAndSleep" />
    </div>
</
form>

 

Run the page and click the button as soon as it appears in the browser. Voila! Instant "invalid callback or postback argument" exception! If you ever see the exception occur sporadically, this might be the reason.

posted by scott with 5 Comments

DTS in SQL 2005 - Good News and Bad News

DTS has always worried me. Unfortunately, I have 331 reasons to worry:

PS > gci -r -i *.dts | group Extension
Count   Name
----- ----         . . .
  331   .dts 

Five years ago, ADO.NET didn't have a bulkload API, and DTS seemed like the best tool for moving and transforming millions of records. This was despite the fact that the binary DTS packages check into source control as opaque blobs, and the cumbersome UI makes even simple changes difficult. We thought we'd need to write and distribute ~50 packages total, but feature growth and support for additional 3rd party databases has ballooned the original estimate.

The application using these packages has to support SQL 2000 for the foreseeable future. Not every customer has budgeted for a new SQL 2005 license, unfortunately. The good news is that SQL 2005 will run the DTS packages, even though it cannot migrate but a handful of them to the new SSIS platform.

Another piece of good news is that you can edit DTS packages in the SQL 2005 Management Studio after downloading some designer components. The bad news is that the designer is still suboptimal. For instance, the Home, End, Backspace, PageUp, and PageDown keys will quit working in the designer. The good news is that this problem is fixed in SP2. The bad news is that the fix changed the designer into a modal dialog. Modal dialogs should be banned from all software in the universe.

If you haven't started your DTS migration yet, here are a few resources I've collected.

DTS Services for SQL 2005 FAQ

SQL Server 2005 Integration Services Backward Compatibility

Known Package Migration Issues

A tool to improve DTS to SSIS migration

Any other good ones out there?

posted by scott with 5 Comments

The Jesse Liberty Review

My publisher pinged recently to show me Jesse's review of my WF book on Amazon.com:

"Every once in a while an author is able to take a subject that others struggle with but fail to make understandable, and succeeds in making so crystal clear that it seems obvious. Allen brilliantly illuminates Windows Workflow, and his book is required reading."

I've never met Jesse, but I've enjoyed his writing over the years. Coming from an expert writer like Jesse, this comment is high praise.

posted by scott with 3 Comments

What ASP.NET Developers Should Know About JavaScript

"What ASP.NET Developers Should Know About JavaScript" is the title of a presentation I'll be giving at VSLive! next month.

I don't plan for this to be a talk that features ASP.NET AJAX, or JavaScript data types. Instead, I want to focus on two key concepts I had to discover after I decided that I couldn't ignore client-scripting any longer:

  1. Every JavaScript function is an object.
  2. Every JavaScript function has an execution context.

I think these concepts are keys to understanding modern JavaScript libraries. I hope that driving home these two concepts will let a developer understand what the following code is doing, and why it works. The example is contrived, but if you can understand the following code you can follow many of the JavaScript patterns in use today.

<script src="msajax/MicrosoftAjax.debug.js"></script>
<
input type="button" value="Print!" id="button" />
<
script>

    Type.registerNamespace("Geometry");

    Geometry.Point =
function(x,y)
    {
        
this.x = x;
        
this.y = y;
                
        $addHandler($get(
"button"), 'click',
                   Function.createDelegate(
this, this.print));
    }
    
    Geometry.Point.prototype =
    {
        print:
function()
        {
            alert(
this.x + ' ' + this.y);
        }
    }
          
    Geometry.Point.registerClass(
'Geometry.Point');        
        
    
var p = new Geometry.Point(2,5);
    
</script>

There are lots of ancillary topics in the above code: object notation, prototypical inheritance, and the evils of the global object, just to name a few. However, given just 60 minutes I think that coming to grips with #1 and #2 lays a solid foundation for moving forward in the AJAX jungle.

posted by scott with 7 Comments

Solutions With The VirtualPathProvider

The VirtualPathProvider is one of the ASP.NET pieces I looked at when 2.0 was new. I suspected then that the VPP would need a little time, but would eventually be the centerpiece of some clever solutions.

Adam Kahtava has documented a number of issues he has with ASP.NET 2.0 Themes. Adam has worked around these issues by using a VirtualPathProvider. See the article descriptively entitled: "A Resolution to The Problems with Themes, Skins, and Cascading Style Sheets (CSS) - Putting the Cascades back into ASP.NET 2.0 Themes (taking control over CSS Cascades / Load Order, Media Types, and Overrides)"

Piyush Shah has devised an approach for sharing a master page across web applications by embedding the shared master page as a resource in an assembly. At run time, the VirtualPathProvider can serve up the master page from the assembly's manifest resource.

The VirtualPathProvider also works magic behind MSDN

posted by scott with 4 Comments

Be Wary of Page Event Handlers for Events Originating Outside The Page

A familiar question in the ASP.NET world is: "How do I show the progress of a long-running background operation?"

ASP.NET AJAX has given us new tools to answer this question. It's easy to drop an UpdatePanel and a Timer inside a web form, and partially update the page when the Timer control posts back. There are still some fundamentals to remember when taking this approach, however. For example:

protected void Button1_Click(object sender, EventArgs e)
{
    
HeavyDutyWorker worker = new HeavyDutyWorker ();
    worker.ProgressNotification +=
            
new ProgressEventHandler (worker_ProgressNotification);
    worker.DoWork();
}

void worker_ProgressNotification(object sender, ProgressEventArgs args)
{
    Session [ProgressKey] = args.PercentComplete;        
}

protected void Timer1_Tick(object sender, EventArgs e)
{
    Label1.Text =
String.Format("Percent Complete = {0}", Session[ProgressKey]);
}

 

The button click event handler creates an object whose DoWork method is going to work asynchronously on some task. Let's pretend the object fires an event each time it completes 10% of the work. The Page subscribes to this event, and the event handler (worker_ProgressNotification) updates a session variable with the percentage of work complete.

To update the page with the completion percentage, the AJAX Timer fires a Tick event on the server. The Tick event handler (Timer1_Tick) tries to read the completion percentage out of the session variable and update a Label control.

This approach will fail.

The problem lies in the worker_ProgressNotification event handler. The Page class uses HttpContext.Current to retrieve the current Session object, but the asynch worker will raise this event on a background thread that is not associated with an HttpContext. The line of code in the event handler will throw a System.Web.HttpException. Even if the event fired on the right thread, the event will fire long after the Page has rendered and the original request that started the asynch work is complete. All bets on having a valid HttpContext would be off at that point.

Interestingly, this approach does work if you use the Cache object (with a unique key for the user / work item, of course). A Page always uses HttpContent.Current to fetch the Session object, but always carries a direct reference to the Cache object. This is, of course, an implementation detail that could change in the future.

A good rule of thumb is to never let a Page subscribe to an event that doesn't originate from inside the Page (where user controls and master pages are considered inside the Page). Anything else is asking for trouble.

posted by scott with 3 Comments