Friday, September 30, 2005

Samples are not the -right way- to do things.

Today, once again, I've seen the corruption of code caused by reading code examples and mindlessly parroting the style and not the substance. Every sample you see these days has code that using String.Format for simple concatenation. That's not what it's for, folks! String.Format is to allow formatting (not just conversion to String) and parameter rearrangement. Code like this is wasteful and silly:

private string CreateSelect()
{
   return String.Format("SELECT TOP {0} {1} FROM {2}", this.rowLimit, this.selectFields, this.fromTables);
}
This will needlessly have to scan the format string looking for the replacement parameters {x} and substituting the parameters positionally. This code should be:
private string CreateSelect()
{
   return "SELECT TOP " + this.rowLimit + " " + this.selectFields + " FROM " + this.fromTables;
}
Which C# will actually turn into this behind the scenes:
private string CreateSelect()
{
   String[] temp = new String[6] { "SELECT TOP ", this.rowLimit.ToString(), " ", this.selectFields, " FROM ", this.fromTables };
   return String.Concat(temp);
}
Which is tons faster. This sort of thing is important to get right in the lowest-level of your code, in things like a DAL or UI framework library.
Not that I'm whining...

Tuesday, September 27, 2005

Statistics aren't easy, good design is just as hard. Want to see both?

I've been a fan of Jeffrey Sax's work in the mathematical area for .Net for quite a while (since he and I helped out a developer on CodeProject with a Fractions library for .Net). Today his company, Extreme Optimization, released the Statistics Library for .Net. This is a major boon for those doing statistical work in .Net.
The Extreme Optimization Statistics Library for .NET contains classes for probability distributions, random number generation, hypothesis tests, and statistical models, including linear regression and analysis of variance. The library is easy to use without compromising performance or reliability.

  • Supports 25 probability distributions
  • Uses a General Linear Model (GLM) approach for unified treatment of regression and ANOVA models
  • Integrates inference and validation tests with statistical models
  • Provides 4 robust pseudo-random number generators
  • Is fully compliant with Microsoft's Design Guidelines for Class Library Developers.

That last point is something near and dear to my heart. All libraries should be written to comply with those guidelines. It could be argued that some of the guidelines are not perfect, but adhering to them leads to a library that feels like the FCL, making it easy to learn.
Brad Abrams, the lead program manager on the Microsoft .NET Framework team and co-author of Framework Design Guidelines : Conventions, Idioms, and Patterns for Reusable .NET Libraries has this to say about Extreme Optimization and its products:
I have made it my mission to institutionalize the value of good API design. I strongly believe that this is key to making developers more productive and happy on our platform. It is clear that Extreme Optimization values good API design in their work, and takes to heart developer productivity and synergy with the .NET framework.
In short, I strongly recommend this product I also strongly recommend Brad Abrams' new book Framework Design Guidelines.

Tuesday, September 20, 2005

Dean Edwards, you are my hero!

While looking for yet another CSS hack-fix for IE, I stumbled upon a really sweet site. Dead Edwards has built two really nice tools (and more). First there is his IE7 Compatibility fixes library, which basically makes all recent versions of IE (before 7) not have the commonly CSS layout bugs. This is cool. The other package is cssQuery, which adds the ability to select DOM elements using the CSS selectors in both CSS1 and CSS2 syntax. Who needs getElementsByTagName now?

Friday, September 16, 2005

Exception handling in .Net (some general guidelines)

Based on a generic query on the Advanced .Net mailing list I dumped this:

Regarding Exceptions themselves

  1. Do NOT catch exceptions you don't know how to handle - this means correct for the problem or do some unwind work. If you know what to do when an Exception happens, catch. If not, DON'T.

  2. Do NOT catch exceptions to merely translate the to another type of exception (even if you do set the InnerException property). If you don't have something to add, don't catch. This is wrong:
    catch (Exception ex)
    {
       // do something interesting (see 1)
       throw new MyCoolException("nothing of value here", ex);
    }
  3. Do NOT catch an exception then throw it again, rather do a "rethrow". This is wrong:
    catch (Exception ex)
    {
       // do something interesting (see 1)
       throw ex; // StackTrace now loses everything below this level
    }
    Rather do this:
    catch (Exception ex)
    {
       // do something interesting (see 1)
       throw; // notice there's no ex! The original StackTrace is left intact.
    }
  4. Do NOT derive all of your exceptions from System.ApplicationException or System.SystemException (or some other base level exception of your creation), as you are not adding value by doing so. If you want to standardize your exception creation, write builder methods to create specific exceptions in regular "forms".

  5. If you do have contextual information you can add to an exception, DO SO. Use the Exception.Data collection, that's what it is there for! You can add the values of interesting parameters to you methods. This is especially useful in the context of a database layer or other low-level library. You can squirrel-away the SQL and all the parameters. Only do this if you think that these details will be useful for post-mortem diagnosis. If the information you log is transitory it will NOT help tracking down errors from logs. This is (mostly) good:
    catch (Exception ex)
    {
       ex.Data.Add("SQL", command.Text);
       ex.Data.Add("key", myKey); // or enumerate command Parameters collection
       throw; // (see #3)
    }
  6. If you add things to the Exception.Data collection, make sure that you don't conflict with what is already there as this is a HashTable. I use the catching-class's name to scope the values. This is much better than #5:
    catch (Exception ex)
    {
       ex.Data.Add(String.Format("{0}.{1}.SQL", System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName, System.Reflection.MethodBase.GetCurrentMethod().Name), command.Text);
       throw; // (see #3)
    }
  7. If you are catching exceptions to do some undo-work (like rolling back transactions, closing handles, etc.) Strongly consider creating a very small wrapper for that resource and implementing IDisposable on it. Then you can have a using clause to hide the try { } finally { } block.

  8. Never catch System.Exception, System.SystemException or System.ApplicationException without doing a rethrow except at the top level logging handler. This also applies to the the catching of non-CLS exceptions (don't throw them in the first place). In CLR 2.0, those will be wrapped in RuntimeWrappedException anyway.

On logging exceptions

  1. Always put a top-level exception catch that logs the exception and don't make (or allow) anyone else do it.

  2. If you are going to handle the exception (see #1 above), then you can log the one you caught with an "informational" level, then handle it and DO NOT throw/rethrow unless the handling is only undo/release (see #3, #7 above).

  3. Always log at the highest level possible, anything not handled as in #B above is an "error" level.

  4. When logging a message, you should assign a correlation ID to the log entry and make sure that any user-displayed message contains that correlation ID so they can report an error and you can look it up in the logs

  5. Don't log ThreadAbortExceptions unless you really care! In ASP.Net applications, those happen on Response.Redirect("xxx", true) calls. You can do:
    if (exception as ThreadAbortException == null)
    {
       // log it.
    }

Where to log exceptions

  1. Event logs are easliy monitored by WMI et al.

  2. Event logs can "fill up" and truncating them or setting the attributes so that they automatically truncate requires administrative rights, so you could get errors that you cannot log.

  3. You should really create your OWN event log and not use the generic Application event log, so you can set it up correctly from the get-go.

  4. Text files need to be placed somewhere the user has rights (don't you DARE put it on C:\)!

  5. If you use text files, disks can fill up so you can get errors that you cannot log.

  6. To monitor a text file, you need access to the file (net share, FTP, etc..) and you have to parse them to filter.

  7. If you care to do backups of the logs, you have that option and responsibility

  8. XML files == text files for the purposes of this discussion. They might be easire to parse for monitoring, but all other comments are the same.

  9. For database logging the rules are similar to XML files, excepting you can leverage the backup and space management policies in place for the database itself.

  10. Databases can be easily monitored remotely by doing simple SELECTs which makes the parsing and processing easier.

  11. Databases can fail... what do you do if the logging database is down? You'll need a backup plan to log THAT error (typically event), and possibly want to redirect the original log entry elsewhere (either file/event).

  12. If you want centralized reporting of log messages, you should also consider using a store-and-forward repository like SMTP or MSMQ to send the messages to somewhere else. If course you need to be able to log transport errors somewhere.

  13. If you do store-and-forward, make sure you build in heartbeat log entries so you can tell if messages are getting through (from the receipient's view).

Final thoughts

  1. There is no one-best solution... your requirements and restrictions govern to much to make blanket statements. If you can't require administrative rights, event logs are bad. If you can't do file shares, text files are bad, etc.
  2. Defensive-in-depth is important. If you want to catch all errors, you need to make an attempt to log errors in you error logging somewhere.

  3. It doesn't matter where you log messages if you are not monitoring the log.

  4. If it is possible to keep the AppDomain alive then, go ahead. For ASP.Net applications, that's usually done by forwarding the user to an error page.
[Edit: 16 Sept, 2005 based on comments from Peter Ritchie and Paul Mehner, added links to relevent sources and clarifications to System.ApplicationException and System.SystemException]

Friday, September 02, 2005

Mini-Microsoft: Microsoft Financials: "And then?"

I think I figured out where the Microsoft Vista name came from. In bit of delicious irony, I think it came from MiniMicrosoft. Check the quote:

No real break-out product announcements on the horizon, other than the ever-so-cool immediate money losing XBox 360. And just how many times can you talk about releasing Yukon and Whidbey? Maybe FY07 we'll have a new vista to stand from and show how the next Windows and next Office will save our stock.