Losing your (type) religion#

As I stated in my last post, the var keyword is an acknowledgement that I do not care what the type of the variable is - I only care what it can do.

Into the IUknown

Most developer start on the path to this revelation when they discover interface-based programming.

Consider: we need to write a method that returns a collection of strings. Callers of the method will only need to loop over the items and perform some action. The number of elements to return is not known at compile time, so we'll declare a generic List<string>, populate it within a loop, and then return it to the caller.

The junior developer will declare the return type of the method as List<string>:

List<string> GetNames(){}

Acknowledging that callers should not be bothered with having to know an implementation detail (the fact that a List was used instead of a fixed-sized array), and knowing that they only need to loop over the returned items, a more experienced developer might declare the method:

IEnumerable<string> GetNames() {}

Which means any variable the receives the return value will also be typed as IEnumerable<string>:

IEnumerable<string> names = GetNames();

Here's the kicker - in both scenarios, the GetNames method is really returning an object of type List<string>! When the supposedly more experienced developer chooses to return a more abstract type like IEnumerable<string>, type information is lost. Yikes! The names local variable above holds a List<string> instance, but you (the client developer) don't know it. As scary as that might sound, you probably realize that you don't care. As long as you can do the things you need to do with names (loop over it), you are happy.

Walking like a duck

Now let's take that train of thought and consider duck typing in a dynamic language like Python. I can declare the following method:

def Recycle(service):
    service.Stop()
    service.Start()

It takes a single parameter named service. What is the type of the parameter that is expected? I don't know. And after the initial anxiety settles, there is the liberating realization that I don't have to care! All I know, or care, is that any object instance that is passed to this method must implement a Start() and a Stop() method. The same method can be used with instances of a Lawnmower, AuditLogger, or SongPlayer class (assuming they all have Start and Stop methods).

Getting closer to the goal

Let me be clear - I am not suggesting that implicit typing in C# 3.0 is the same as duck typing. What I am suggesting is that the 2 examples above are points along the "progression of understanding" of the same concept: it is the behavior of an object that really matters, as opposed to the type name that is attached to it. Interface-based programming is near the start. Implicit typing is further along the progression. Duck typing is closer to the end.

I think we will find that programmers who have been exposed to and understand languages which support duck typing will be more likely to be comfortable with and embrace the var keyword. They have already made the mental leap that type information serves the computer more than it serves the human. If you haven't yet, do yourself a favor and learn a dynamic programming language, even if you know you will continue to spend your days with static typing.

Wednesday, June 25, 2008 2:05:02 AM (Central Daylight Time, UTC-05:00) #    Comments [0]  | 

 

To var#

Dare Obsanjo asked the question To var or not to var? He asserted "case closed" at the end of his post, and closed it to further comments, but I would like to continue the conversation.

He pulls out the common straw man argument that the anti-var (C# 3.0's implicity type keyword) crowd likes to attack. They assume that fans of implicit typing just want to save a few keystrokes. It isn't about less keystrokes. For me, its about thinking a little bit less like a computer and a little bit more like a human. I prefer to think less about Types and more about behaviors. The var keyword is an acknowledgement that I do not care what the type of the variable is - I only care what it can do.

I'll address a few specific quotes from his post (emphasis is mine).

[Referring to ReSharper] So it seems there are two suggestion modes. The first suggests using the var keyword when the name of the type is obvious from the right hand side expression being evaluated such as casts or new object creation. The second mode suggests replacing type declarations with the var keyword anywhere the compiler can infer the type... The first suggestion mode makes sense to me since the code doesn't lose any clarity and it makes for shorter code. The second mode is the one I find problematic it takes information out of the equation to save a couple of characters per line of code.

In both situations, it is about removing compiler noise. I have already given the compiler enough information for it to do its job. I don't care about a couple characters.

For example, the claim that it leads to "better naming for local variables" really means it compels developers to use LONGER HUNGARIAN STYLE VARIABLE NAMES.

Longer, more descriptive variable names? Yes, please. Hungarian style? That assumes I care about the type (which I don't see my follow-up post).

...these long variable names add more noise to the code overall since they show up everywhere the variable is used compared to a single type name showing up when the variable is declared.

This is completely different understanding of the term "noise" as it relates to code. To me, "noise" in a computer language is all of the stuff that makes code look like something that needs to be decoded. It is all of the stuff that normal humans would not use when communicating with each other. Your source code is a communication medium - it communicates intent to the computer, and to the humans that need to maintain it. Anything that is added purely for the computer's benefit is noise to the human. It commonly takes the form of angle brackets, parentheses, semi-colons, etc. Sometimes it takes the form of redundant or extraneous type information. But I would never consider a descriptive variable name "noise", just because it takes up a little more screen real-estate.

It is interesting that Dare seems to reject the argument that var helps promote use of better variable names, and yet the example which spurred his post illustrates it perfectly:

I found this change to be somewhat puzzling since while it may have shortened the code by a couple of characters [There he goes counting characters again] on each line but at the cost of making the code less readable. For example, I can't tell what the type of the variable named lvi is just by looking at the code.

The var keyword did not make the code less readable, it just made the poor choice of variable names more apparent. Might listItem or feedItem be a little more readable than lvi?

It seems his primary argument against var is that always knowing the type of a variable is of utmost importance. Why? When you are writing code, doesn't your IDE let you know what behaviors are available to your object instances? When you are reading code, can you not assume that the compiler has already checked it for invalid code?

Wednesday, June 25, 2008 1:11:04 AM (Central Daylight Time, UTC-05:00) #    Comments [0]  | 

 

Building Domain Specific Languages in Boo on the .NET Platform#

Ayende Rahien will be giving a course on buliding DSLs with Boo in Austin on May 19-20. I'm a closet fan of Boo, having lurked on the mailing list for about 6 months, and really looking forward to getting a stronger grasp of the language, and the benefits of building your own domain specific languages. This is a great opportunity to learn from the preeminent creator of Boo DSLs.

Scott Bellware sent a good writeup to the AgileATX mailing list:

This class takes you deeper into the reasoning and power of dynamic
programming on the CLR.  It will show you how to build and take
advantage of DSL's for your own CLR applications, and why DSL's and
language-oriented programming are such a valuable practice at this
point on the programming timeline. 
You'll leave this class having been jump-started on a new, powerful
language and paradigm, and be connected to other motivated .NET folks
who are working toward bringing some of successes of dynamic
programming communities into the .NET community.

The class will also give you the opportunity to check out a town that
folks are starting to recognize as a progressive and leading .NET
competency in North America, and to have some fun with Austin's
developer community folks in lush green and sunshine of Central Texas.

There are still some spots open, if you are interested you can register here.

.NET | Boo
Saturday, May 10, 2008 10:45:43 PM (Central Daylight Time, UTC-05:00) #    Comments [0]  | 

 

ALT.NET heart Microsoft#

So a high profile ALT.NETter, who is not in attendance at this weekend's altnetconf, has decided he no longer wants to be associated with the group because of its perceived negativity. It is a shame he wasn't here in person to experience the bizarro-world like ending to Day 2. The closing remarks, which is a chance for anyone in attendance to stand and share their thoughts on the day, were composed entirely of expressions of excitement around the just demonstrated System.Web.MVC framework, and thanks and appreciation to the Microsoft ASP.NET team that created it. This is not an easy crowd to please, but mostly because we all want to be able to assert our preference of tools and practices individually. NOT because we want to use "anything but Microsoft", but because we want to be able to choose "some things that are not from Microsoft". When Microsoft does provide a great tool (as they did with the .NET Framework, and potentially the MVC framework) members of the crowd are willing embrace it. Now, we are not a hive mind (a fact which seems to be frustrating some people that want us to come to some kind of consensus... on anything), so I won't say everyone is excited about it. But unlike the earlier opening remarks, at the end nobody stood up to air their grievances with Microsoft. It was all smiles and love for Scott Guthrie (who was there demonstrating the framework and representing the ASP.NET team). Yes, even from Scott Bellware.

(Of course, it may all be a side effect of ScottGu's super-hero power of being able to foster man-crush's from entire rooms of ornery, cynical developers.)

Technorati Tags:
Saturday, October 06, 2007 9:13:13 PM (Central Daylight Time, UTC-05:00) #    Comments [0]  | 

 

Machine specific tasks with MSBuild#

Jeremy D. Miller recently wrote about how to make a NAnt script branch based on the machine it's runnig on. Since I've been working almost exclusively with MSBuild, I figured it might be educational to post the equivalent script.

First, in Jeremy's example, he is really only changing the values of a property based on the machine name. If that is all you need to do, the MSBuild solution is fairly straightforward. I'll include the entire file contents so that you can easily copy/paste and test, and verify their are no hidden tricks.

<?xml version="1.0" encoding="utf-8"?>

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <PropertyGroup>

  <Database>invoice_dev</Database>

  <Database Condition="'$(Computername)'=='TEST-SERVER'">invoice_test</Database>

  <Database Condition="'$(Computername)'=='PRODUCTION-SERVER'">invoice_prod</Database>

  </PropertyGroup>

 

  <Target Name="Build" >

  <Message Text="Connect to database $(Database)" />

  </Target>

</Project>

This takes advantage of the fact that all environment variables are immediately available as properties in an MSBuild script; and that all Windows machines (that I've worked on recently) have the COMPUTERNAME environment variable set.

However, if you actually needed to run different tasks (as opposed to just setting properties), things would look a little different. One way I might approach it would be:

<?xml version="1.0" encoding="utf-8"?>

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

 

  <Target Name="Build" DependsOnTargets="MachineSpecificTasks">

  <Message Text="Start Build" />

  </Target>

 

  <Target Name="MachineSpecificTasks"

  DependsOnTargets="TestServers;

                      ProductionServers" />

 

  <Target Name="TestServers" Condition="'$(Computername)'=='TEST-SERVER1'">

  <Message Text="Building on test server." />

  </Target>

 

  <Target Name="ProductionServers" Condition="'$(Computername)'=='PRODUCTION-SERVER1'

                                    OR '$(Computername)'=='PRODUCTION-SERVER2'">

  <Message Text="Building on production server." />

  </Target>

</Project>

I created a separate dummy target called MachineSpecificTasks. It is just used to define the different targets that you make available for each environment. I make all of the machine specific targets as dependencies on this target, so that the script will attempt to execute each of them. I could have just as easily skipped the MachineSpecificTasks target and put the dependencies on my Build target, but I felt that list might get large and get confusing. Better to hide it away. I then set a condition on each target so that it is only run if the computername environment variable matches the given computer name. I went a little further and showed how you could associate multiple computer names (PRODUCTION-SERVER1 and PRODUCTION-SERVER2) with the same set of tasks (collectively called ProductionServers).

There is probably a way to use the CallTarget task to more closely match the look of the NAnt solution, but I'm not sure if it would buy you anything.

Hope that helps.

Tuesday, September 12, 2006 10:24:33 PM (Central Daylight Time, UTC-05:00) #    Comments [0]  | 

 

Formerly Known As Atlas, for real#

It's official, the technology formerly known as Atlas will be called...  Microsoft AJAX Library, ASP.NET 2.0 AJAX Extensions, and ASP.NET AJAX Control Toolkit!

Wow, those names are actually intuitive and descriptive. This is such a relief, considering what COULD have been... I had my bets on the dry, but .NET 3.0 compliant "Windows Dynamic Web Foundation"? Or the completely absurd "ASP.NET 7.0" ;-)

Yeah, about that... I completely made it up. It wasn't a hoax; no, I wasn't trying to fool anyone or make anyone believe it so that I could secretly laugh at them. It was a satire, so that I could publicly laugh at the madness of Microsoft naming schemes. I wanted to try and draw some attention to the issue, and hopefully motivate some people to go sign the Reverse WinFX Petition. Not because I thought Microsoft would change their mind, but because I wanted them to get the message that we don't want marketing decisions obfuscating and hijacking the terminology that we developers use to communicate. Its a version number - it has a very specific meaning, it is not a brand. Of all the things to lift from the Java camp, why did we want the version number confusion?

If you believed my post regarding Atlas => ASP.NET 7.0, I'm sorry, it wasn't my intention to deceive. But the fact that a number of people did believe it just demonstrates the levels of absurdity we've come to expect and accept from Microsoft when it comes to branding. I tried to make it absurd. I tried to make it so ridiculous, and so obvious that it was fake (the quotes were almost word for word cut from one of the linked articles about the .NET 3.0 naming), but people still believed it. Apparently I need to work on my comedic writing and satirical wit.

Thanks to the Microsoft Web Platform team for the sane naming and their ability to continue delivering technology without waiting for a large release vehicle (and sorry for any confusion the last post may have caused).

Monday, September 11, 2006 9:29:10 PM (Central Daylight Time, UTC-05:00) #    Comments [0]  | 

 

Formerly known as Atlas#

You've probably read the news a hundred times around the blogosphere already, but that won't stop me from joining the chorus and adding my 2 cents.

The Microsoft AJAX (formerly known as DHTML) technology, codename Atlas, will be officially named... ASP.NET 7.0!

I was very skeptical at first... this is the explanation:

"ASP.NET 7.0 aptly identifies the technology for exactly what it is – the next version of our web developer framework."

"...The change is in name only and will not affect the technologies being delivered as part of the product. ASP.NET 7.0 is still comprised of the existing ASP.NET 2.0 components, including DataSource controls, the Provider model, and Master Pages & Themes, as well as new web 2.0 developer-focused innovative technologies like UpdatePanels and JSON serialization."

"...The ASP.NET platform has always been at the core of Atlas, but the Atlas brand didn’t convey this"

"...The name conveys the maturity of the platform by emphasizing that it has evolved from proven technology in production for over a decade, while also aligning it with the rest of the Web Tools Platform (IIS 7 + IE 7)."

Over a decade you ask? Follow along - it started with Active Server Pages 1, 2, & 3.0, changed the brand slightly to ASP.NET (4.0), ASP.NET 1.1 (5.0), and then most recently ASP.NET 2.0 (6.0). The next logical name of the development platform, ASP.NET 7.0, more accurately reflects this heritage.

(I can't help but suspect a little marketeering "two-upmanship" over the PHP 5 competition).

"In the specific example of ASP.NET 7.0, there is a lot of new compelling improvements to the technology that warrant a major version number for the redist"

"...We are confident that this change will go a long way towards reducing confusion people may have about our web developer platform and the technologies in which they should invest."

Reducing confusion? Ok, never mind what I said earlier about coming around.. this is ridiculous. If any of this bugs you like it bugs me, go sign the Reverse WinFX Petition.

This post was written using Windows Live Writer Beta. My source tells me that when it comes out of Beta, it will be renamed Word 13.

Update: You should probably read the follow up post regarding the naming of the Atlas framework.

Thursday, August 24, 2006 9:27:48 PM (Central Daylight Time, UTC-05:00) #    Comments [6]  | 

 

HttpModule to allow a custom error page for 401.2 Access Denied in ASP.NET#
As you know, the customErrors section of web.config allows you to define your own pages to display to the user when an error occurs. It allows for a default page to display when any unhandled error occurs, and it also allows you to specify different pages to display depending on the HTTP status code.

I like to take advantage of ASP.NET's declarative security model by defining the users and roles that are authorized to execute different parts of my website. For example, I can limit access to a page named Protected.aspx by declaring that all users must be a meber of the VerySpecialUsers group. This can be accomplished by adding the following section to my web.config:

  <location path="Protected.aspx">

    <system.web>

      <authorization>

        <allow roles="VerySpecialUsers"/>

        <deny users="*"/>

      </authorization>

    </system.web>

  </location>


I like this model as it allows me to easily modify the access restrictions for a page, and since it uses ASP.NET's Role Provider framework, it is not tied to any implementation about how role membership is determined. By default in a Windows Authentication/NTLM scenario, the roles map to the group memberships of your Windows login account (Administrators, Power Users, Users, etc). But you can very easily create your own Role Provider that retrieves group membership information from your own data store.

When a user does not meet the authorization requirements to visit a certain page, ASP.NET will return a 401.2 Access Denied error. The page looks like the standard yellow screen of death (YSOD) that ASP.NET returns for all errors.The customErrors section of web.config is supposed to let you provide a friendlier response to your users so they never have to see the YSOD. You would think that all you need to do is define a page for the 401 status code in customErrors and all would be good. You would be wrong. You can even try changing the custom error page defined in your IIS settings, and it still won't work.

The newsgroups have countless unanswered posts about this issue. Fortunately, my most recent attempt to solve this problem finally found some answers. The first one I found, and the one I use for my solution, was posted by John "iSpeakGeek" on ASPFree forums. I have since discovered a CodeProject article by George Mamaladze, and even a (somewhat related) Microsoft KB article. Hopefully this post will help improve the search results for other people struggling with this issue.

The key to the solution is to intercept the EndRequest event of the page lifecycle, check for a 401 status code, and then execute your custom page. The Microsoft article suggests intercepting the Error event and checking for an UnauthroizedAccessException, but that didn't work for my scenario. It may only apply when using impersonation, as the article describes. I need to do more testing and will possibly modify my solution to cover more scenarios.

Since I need this functionality on pretty much every website I make, I created an HttpModule to encapsulate the logic. The nice thing about my module approach is that you simply register the module in your web.config, and it will automatically respect the settings you define in your customErrors section. There is no hardcoded dependence on a specific error page or need for a custom configuration section.

<httpModules>

  <add name="CustomAccessDenied" type="FlimFlan.CustomAccessDenied, FlimFlan.CustomAccessDenied"/>

</httpModules>

<customErrors mode="On" defaultRedirect="Error.htm">

  <error statusCode="401" redirect="AccessDenied.aspx"/>

</customErrors>


I've packaged up the full source and a sample website. Use it and change it as you wish. If you don't care about the source, just grab the DLL from the DemoWeb\Bin folder and put it in your own website's Bin folder.

I can't claim it is a general purpose solution yet, as I have not tested it with all possible authentication/authorization settings. If you find a scenario that doesn't work, please let me know and I'll see if I can address it.

Download FlimFlan.CustomAccessDenied
Saturday, August 05, 2006 12:02:52 PM (Central Daylight Time, UTC-05:00) #    Comments [9]  | 

 

A simple color ConsoleTraceListener#
Considering how many pre-release Whidbey demos I saw showing off the new color support in Console applications, I'm surprised this wasn't include in the box. I use a System.Diagnostics.TraceSource coupled with the ConsoleTraceListener for output in console apps. Of course, when you have verbose logging enabled, you see a LOT of gray text scroll across the screen, and nothing really stands out. Reminds me of trying to decode NAnt output after living with MSBuild for a while. I did a quick search for "color consoletracelistener" and came across this offering from Mauricio Rojas. I (probably unfairly) dimissed it because it looked much more complicated than I would expect (ok, and because it was in VB).
So I spent 10 minutes throwing this together. It works for every scenario that I need. In other words, I make no claims that it is general purpose solution for you - it may not add color to every type of trace message (depending on which methods you use to write messages). If this doesn't work for you, Mauricio's may be a better bet.

using System;

using System.Diagnostics;

using System.Collections.Generic;

 

namespace FlimFlan.Diagnostics

{

    public class ColorConsoleTraceListener : ConsoleTraceListener

    {

        Dictionary<TraceEventType, ConsoleColor> eventColor = new Dictionary<TraceEventType, ConsoleColor>();

 

        public ColorConsoleTraceListener()

        {

            eventColor.Add(TraceEventType.Verbose, ConsoleColor.DarkGray);

            eventColor.Add(TraceEventType.Information, ConsoleColor.Gray);

            eventColor.Add(TraceEventType.Warning, ConsoleColor.Yellow);

            eventColor.Add(TraceEventType.Error, ConsoleColor.DarkRed);

            eventColor.Add(TraceEventType.Critical, ConsoleColor.Red);

            eventColor.Add(TraceEventType.Start, ConsoleColor.DarkCyan);

            eventColor.Add(TraceEventType.Stop, ConsoleColor.DarkCyan);

        }

 

        public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string message)

        {

            TraceEvent(eventCache, source, eventType, id, "{0}", message);

        }

 

        public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string format, params object[] args)

        {

            ConsoleColor originalColor = Console.ForegroundColor;

            Console.ForegroundColor = getEventColor(eventType, originalColor);

            base.TraceEvent(eventCache, source, eventType, id, format, args);

            Console.ForegroundColor = originalColor;

        }

 

        private ConsoleColor getEventColor(TraceEventType eventType, ConsoleColor defaultColor)

        {

            if (!eventColor.ContainsKey(eventType))

            {

                return defaultColor;

            }

            return eventColor[eventType];

        }

    }

}


You can see it in action using the following app:

using System.Diagnostics;

namespace ConsoleApplication1

{

    class Program

    {

        static void Main(string[] args)

        {

            TraceSource trace = new TraceSource("log");

            trace.TraceEvent(TraceEventType.Start, 0);

            trace.TraceInformation("Hello World");

            trace.TraceEvent(TraceEventType.Error, 0, "Something failed.");

            trace.TraceEvent(TraceEventType.Verbose, 0, "I like ice cream.");

            trace.TraceEvent(TraceEventType.Critical, 0, "Something went horribly wrong!");

            trace.TraceEvent(TraceEventType.Verbose, 0, "I like cherries.");

            trace.TraceEvent(TraceEventType.Warning, 0, "This program will end soon...");

            trace.TraceEvent(TraceEventType.Information, 0, "Ending program.");

            trace.TraceEvent(TraceEventType.Stop, 0);

        }

    }

}


Add these settings to the configuration section of your app.config:

  <system.diagnostics>

    <sources>

      <source name="log" switchValue="All">

        <listeners>

          <add name="Console" type="FlimFlan.Diagnostics.ColorConsoleTraceListener, ConsoleApplication1" />

        </listeners>

      </source>

    </sources>

  </system.diagnostics>


And you should see something like this:

Thursday, August 03, 2006 10:02:45 PM (Central Daylight Time, UTC-05:00) #    Comments [0]  | 

 

ProfileView can now be used for user administration#
By far, the most common feature request for my ProfileView control was the ability to edit the profile of any user in the system. Until now, the control could only be used by the current user to edit their own profile. I thought it was a limitation of the Profile provider framework, until I discovered ProfileBase.Create(). I apologize to everyone that has been waiting for this much needed feature for so long.

Just set the UserName property on the control to edit a different user. The property is null by default, which loads the current user.

Example usage in a WebForm used to edit a profile:

    protected void Page_Load(object sender, EventArgs e)

    {

        if (!Page.IsPostBack)

        {

            string userName = Request.QueryString["username"];

            if (!String.IsNullOrEmpty(userName))

            {

                ProfileView1.UserName = userName;

            }

        }

    }



Download the latest code from the ProfileView project page.
Saturday, June 24, 2006 11:47:41 AM (Central Daylight Time, UTC-05:00) #    Comments [1]  | 

 

Safely running background threads in ASP.NET 2.0#
The .NET Framework 2.0 no longer allows background threads to die silently when an unhandled exception is thrown. I'm not going to dive into the details of the change, as it is already well-documented on the web (this post describes the behavior for different application types & Scott Allen focuses on the ASP.NET impact).

The impact is more significant for ASP.NET applications
I think this is actually a bigger change for ASP.NET apps, because it has always had a "safety net". In console/WinForms applications, if there is an unhandled exception on the primary thread, the application dies. Having your application die now because of an unhandled exception on a background thread is not that big of a difference. You have an unhandled exception in your application, the application dies. Consistent.

However, in ASP.NET, exceptions that you did not handle in your own code on the primary thread were always caught by a default handler (affectionately known as the "yellow screen of death"). Your application did not die. You could add a handler for the HttpApplication.Error event (Application_Error in global.asax) to centralize your exception handling and logging logic. In 1.x, exceptions that occurred on a background thread would die silently. Your application did not die. Consistent. But now, in 2.0, exceptions on your primary thread still do not kill your application, and can be logged by your global handler. But exceptions on your background threads will kill your application, and will not be logged.

My approach to exception handling and how it is now more difficult to implement
My philosophy is to never catch exceptions unless there is something I can do to address the issue. I prefer not to catch all possible exceptions throughout the internals of my code, and instead rely on try...finally blocks to clean up in case of an exception, and allow the exception to bubble up to a top-level handler where it can be logged (of course I catch exceptions when there is something the code can do to resolve the issue or continue in a known state).

The new background thread exception behavior forces me to create "top level handler and logging" code for the application (global.asax) AND within every method that I run on a background thread. I end up with a lot of duplicate code, and explicit exception handling and logging that I would prefer to be hidden behind the scenes instead of cluttering up my application logic.

A proposed solution
My goals:
  • Allow unexpected exceptions on all threads to be logged by a top level exception handler
  • Do not let the application die because of an exception, no matter which thread it is running on (at least not before I get a chance to log it).
  • Do not clutter the application logic with repeated try..catch and logging code.
To satisfy the top level exception handler goal, I would like to make use of the existing HttpApplication.Error / Application_Error mechanism. That means I need to somehow transfer the exception that occurred on a background thread to a primary ASP.NET thread. To satisfy the "do not die" and "do not clutter" goals, I need to wrap all calls to background thread methods with a single piece of code.

I've created the SafeWaitCallback class. It's Call method is used in place of a WaitCallback delegate. For example, if I want to execute the method DoTask on a background thread, I would call it like this:

System.Threading.ThreadPool.QueueUserWorkItem(new SafeWaitCallback().Call(DoTask));

SafeWaitCallback does two things: it wraps calls to the target method in a try...catch block, and it forwards the caught exception to a custom HttpHandler. It forwards the exception by serializing it to a byte array, and then POSTing it to the handler, which will receive the bytes and deserialize back to an Exception object.

using System;
using System.Net;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;

public class SafeWaitCallback {
  public static Uri ApplicationUri;
  System.Threading.WaitCallback callback;

  public System.Threading.WaitCallback Call(System.Threading.WaitCallback callback) {
    this.callback = callback;
    return CallbackWrapper;
  }

  private void CallbackWrapper(object state) {
    try
    {
      callback(state);
    }
    catch (Exception e)
    {
      byte[] exceptionData;

      MemoryStream stream = new MemoryStream();
      BinaryFormatter formatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Persistence));
      formatter.Serialize(stream, e);
      exceptionData = stream.ToArray();

      WebClient client = new WebClient();
      Uri handler = new Uri(ApplicationUri, "TransferException.axd");
      try
      {
        client.UploadData(handler, exceptionData);
      }
      catch (WebException) { }
    }
  }
}

By forwarding the exception to the custom HttpHandler, I can re-raise the exception, and since the handler will be running on a primary ASP.NET thread (not in the background), the exception will be caught by my global handler, and logged just like any other exception.

using System;
using System.Web;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;

public class TransferredExceptionHandler : IHttpHandler {
  public bool IsReusable { get { return true; }}

  public void ProcessRequest(HttpContext context) {
    byte[] exceptionData = new byte[context.Request.ContentLength];
    context.Request.InputStream.Read(exceptionData, 0, exceptionData.Length);

    Exception transferredException;
    MemoryStream stream = new MemoryStream(exceptionData);
    BinaryFormatter formatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Persistence));
    transferredException = (Exception)formatter.Deserialize(stream);
    throw new Exception("[Background exception transferred - see InnerException]", transferredException);
  }
}

The handler is registered in web.config with the following entry:
      <httpHandlers>
        <add verb="POST" path="TransferException.axd" type="TransferredExceptionHandler" />
      </httpHandlers>

Let me know what you think
I've attached the full source code in the form of a Web Site project. It includes a Default.aspx page which demonstrates the different scenarios of an unhandled exception (primary thread, background thread, "safe" background thread using my new code).

This is not production ready code, it is still in the proof of concept stage. For one, it probably needs better exception handling within the CallbackWrapper exception handler (what if the serialization blows up?). More importantly, I'm not yet convinced this whole idea is a worthwhile approach. It meets my goals, but it feels a little hackish. I wanted to see if it was possible, and then throw it out there to get some feedback. Anyone see any major flaws in the concept? Even better, does anyone know of a more elegant approach to achieve the same goals?

Download AspNetBackgroundExceptions.zip (2.55 KB)

Wednesday, June 21, 2006 9:50:02 PM (Central Daylight Time, UTC-05:00) #    Comments [3]  | 

 

Add your two cents to the MSDN documentation#

Now, instead of burying in your blog your sage advice on how to really use the classes in the .NET Framework, despite what the documentation says, you can add it right to the source for everyone to see.
http://msdnwiki.microsoft.com

I only mention it, because that's where I will be putting most of my sage advice going forward.

Thursday, June 15, 2006 11:24:19 PM (Central Daylight Time, UTC-05:00) #    Comments [0]  | 

 

Temporary Projects in Visual Studio 2005#
Jeff Key created a great little tool called Snippet Compiler, which allowed you to experiment with some throwaway .NET code. This is useful for doing a quick technical proof of concept (a "spike" in agile practitioner terms). Before Snippet Compiler, I used to fill my Visual Studio Projects folder with a bunch of junk projects that I intended to delete eventually.
However, with Visual Studio 2005, I think Snippet Compiler's days are numbered (no offense Mr. Key). VS2005 supports a seemingly little known feature that I'll call "temporary projects". It allows you to create and work on a new project without saving it to disk. You can work on the project as long as you want. When you close the project, you have the option to save it, but more importantly, you have the option to just discard it (this should all sound familiar to fellow VB6 converts). If you use the new Web Application Project model, you can even use this feature to create throwaway websites.

I use this feature all the time while trying to brush up on on the new functionality in the 2.0 framework.

To enable temporary projects:

1) In Visual Studio 2005, go to the Tools | Options... menu
2) Go to the Projects and Solutions node
3) Uncheck "Save new projects when created"
Thursday, March 23, 2006 11:00:28 PM (Central Daylight Time, UTC-05:00) #    Comments [1]  | 

 

New release of ProfileView#
I've posted the source and binary for the latest release of my ProfileView ASP.NET server control. I was finally able to address the much requested fix to make the order of the properties displayed at runtime reflect the order they are declared in the web.config. Here are the release notes:
  • Properties now display in the order they are listed in web.config
  • Design-time view now uses same rendering method as runtime
  • Fixed design-time bug that caused it to fail when a profile property type was not specified
  • Design-time view now has a Smart Tag to allow you to preview an anonymous user's profile
Sunday, January 08, 2006 8:46:49 PM (Central Standard Time, UTC-06:00) #    Comments [7]  | 

 

Debug classic ASP pages in Visual Studio 2005#
Mikhail Arkhipov already posted exactly what you need to know to debug classic asp pages in Visual Studio 2005. His post includes the instructions for Win2K3, but the comments include instructions for WinXP.

I'm only posting this in hopes that it helps the search engines lead people to his post. When I searched for "debug classic asp visual studio 2005", I had no luck. If I would have abbreviated visual studio as vs (as he did in his post title), I would have found it immediately.
Saturday, January 07, 2006 3:35:08 PM (Central Standard Time, UTC-06:00) #    Comments [2]  | 

 

Visual Studio Command Prompt Toolbar Button#

Of the billion toolbar buttons that Visual Studio 2005 includes, I couldn't find one to open a command prompt to the project directory. This is a must-have for us command-line SubVersion users. Luckily, a quick Google search turned up this post (its actually the comment from Barry Gervin that I credit). Updated for VS 2005:

From the Tools -> External Tools... menu, click Add, and fill in:

Title: Command Prompt &Here
Command: C:\WINDOWS\system32\cmd.exe
Arguments: /k ""C:\Program Files\Microsoft Visual Studio 8\VC\vcvarsall.bat"" x86
Initial Directory: $(ProjectDir)

Note the Title is important only because I wanted to use the letter 'H' as my shortcut key since it is close to the 'T', and unused on the default Tools menu. This enables the very easy one-handed keystroke ALT+T,H.

I then went to Tools->Customize..., selected Tools from the Categories, then found "External Command 3" (there were 2 other tools already registered in VS) in the Commands section, and dragged it up to my toolbar. Right-click on the new button on the toolbar, select Default Style, then Change Button Image, and choose the square (its a good starting point). Using Edit Button Image... I drew a crude "C:".

Wednesday, January 04, 2006 8:56:36 PM (Central Standard Time, UTC-06:00) #    Comments [2]  | 

 

Accessing web.config at Design Time in .NET 2.0#

When creating a designer for an ASP.NET server control, it is often very useful to read settings from the user's web.config file. In previous versions of the .NET Framework, you had to jump through hoops to access the web.config file at design time. I was very thankful for this solution when I needed it, but I'm happy to move on. The dependency on EnvDTE felt wrong, and the fact that it used COM interop with the VS.NET IDE meant it probably would not work with any other tool.

Thankfully, in .NET 2.0, it is much simpler. If you want to get the full path to web.config (useful for parsing the XML manually), it has been reduced to:

using System.Web.UI.Design;
//...
IWebApplication webApp = (IWebApplication)Component.Site.GetService(
typeof(IWebApplication) );
IProjectItem item = webApp.GetProjectItemFromUrl("~/web.config");
string webConfigPath = item.PhysicalPath;

Note: you will want to check for nulls and handle appropriately. The call to GetService could return null if the design tool hosting your control does not support this service (something other than VS.NET). And of course the call to GetProjectItemFromUrl could fail if there is no web.config in the project.

Even better, instead of parsing web.config yourself, you can get strongly-typed access to the configuration using the new classes in the System.Configuration namespace. For example, in my ProfileView control, I use code similar (more null checking) to the following to discover information about the configured profile properties:

using System.Configuration;
using System.Web.Configuration;
using
System.Web.UI.Design;
//...

IWebApplication webApp = (IWebApplication)Component.Site.GetService(
typeof(IWebApplication) );
Configuration config = webApp.OpenWebConfiguration(true);
ConfigurationSectionGroup systemWeb = config.GetSectionGroup("system.web");
ProfileSection profileSection = (ProfileSection)systemWeb.Sections["profile"];

foreach (ProfilePropertySettings configProperty in profileSection.PropertySettings)
{ // read the settings in each configProperty }

Once you get the Configuration object as demonstrated above, reading a value out of the appSettings section is as easy as:

config.AppSettings.Settings["maxRetries"].Value

Monday, January 02, 2006 12:23:51 PM (Central Standard Time, UTC-06:00) #    Comments [1]  | 

 

ASP.NET Role Provider Visual Studio template#

Of all the new providers that ASP.NET 2.0 introduces, I think the RoleProvider will be the most frequently implemented in a corporate IT environment. Intranet applications can usually take advantage of Windows integrated authentication for user identification. By default, this will create a WindowsPrincipal object that contains all of the user's domain groups as roles to be used for authorization. However, it has been my experience that most applications do not use domain groups for their role-based authorization. It is more likely that they have a database (or some other store) that associates a user's Windows login name with application specific roles.

In the past (.NET 1.x), you could get around this by hooking into the HttpApplication.AuthenticateRequest event (via Global.asax or an IHttpModule). You would lookup the user's roles in your data store, initialize a GenericPrincipal with the roles, and then use it to replace the WindowsPrincipal on the current thread*.

This process is greatly simplified by the introduction of the RoleProvider framework, which is already hooked into the request processing lifecycle by way of the RolePrincipal class. This means you no longer need to manually hook into an application event or create a custom principal, just to set the user's roles. The provider model allows you to focus your code on the singular task of retrieving the roles for a user.

The biggest drawback to the new approach is that the contract for a RoleProvider is pretty extensive. In addition to the large number of methods that must be implemented, there are additional "expected implementation details" that the signatures do not cover (a topic recently discussed by K. Scott Allen).

The good news is that these implementation details have been thoroughly documented in a white paper on MSDN. The bad new is that because of the wealth of information, you will probably find yourself needing to re-read that paper every time you try to implement a new provider.

That is exactly the problem I am trying to solve by introducing my ASP.NET RoleProvider Visual Studio template. Download the zip file, save it or rename it so it has a .vsi exentsion, run it, and accept the prompts (and warnings - sorry, I did not sign the code**). You will now have a new option when adding a new file to a project.

Visual Studio Add New File dialog

Just name the file, and a class with that name will be added to your project. The class inherits from RoleProvider, and has a stub implementation for all of the methods so it can immediately compile. At the very least, you will need to modify the GetRolesForUser() method, and take a look at each section marked with a TODO: comment. Most of the other remaining methods will just throw a NotSupportedException. This is actually a legitimate implementation for many scenarios, as you may not need the additional functionality of the RoleManager. However, if you do want to implement a full solution, the whitepaper guidance for each method is included as comments. If you find the guidance comments distracting, I provided instructions on how to do a quick find/replace to delete them all.

I plan to create additional templates for the other common provider types, but I would like to get some feedback first. I want to know if people find this helpful. How could it be better? The biggest thing I struggled with is how much documentation to include. I always get a little overwhelmed when I add a new file, thinking I'm starting fresh, only to be presented with a bunch of code or comments. It creates the feeling that the person has to read and understand it all before they modify anything. I tried to put focus on the parts that matter (with the TODO comments, and calling out the main method GetRolesForUser). What do you think? Should there be more guidance comments, or less? Should I include the XML Documentation comments for each method, copied from MSDN?

Download the installation file here and provide feedback in the comments below.

 

* For a few more details on the 1.x approach, check out this article on FormsAuthentication. Step 4 is the relevant part, and works just as well for Windows authentication (use the WindowsIdentity instead of FormsIdentity).

** If you do not trust the automatic installation, you can perform a manual install. Keep the file extension from as .zip and extract the contents (a .vscontent file that you can delete, and 2 .zip files). Copy the 2 zip files to your user item templates folder - the "Visual Studio user item templates location" setting in the Projects and Solutions | General tab of the Visual Studio 2005 options dialog.

Thursday, December 08, 2005 6:00:56 PM (Central Standard Time, UTC-06:00) #    Comments [5]  | 

 

Tips for migrating .TEXT to dasBlog#

A little while ago, I made the move from the .TEXT blogging software to dasBlog. Migrating the actual content was pretty straightforward (as mentioned in the previous post). The problem was that any existing links to my content that existed out in the wild would no longer resolve, since .TEXT used a different URL format for identifying posts. Additionally, the URL for my RSS feed changed, so any aggregators that tried to pull from my site would get an error, and probably eventually drop me.
It turns out that both of these problems were relatively easy to solve. I credit the dasBlog development team for making a flexible platform.

To allow .TEXT permalink posts to resolve, I simply added the following entry to the end of the newtelligence.DasBlog.UrlMapper section of web.config:
<!-- .TEXT archive compatibility -->
<!-- Translates
  FROM: /blog/archive/2004/07/27/194.aspx
  TO: /blog/default.aspx?date=2004-07-27
-->