The Best of Simple Talk
The Best of Simple Talk
The Best of Simple Talk
In association with
Copyright Amirthalingam Prasanna, Ben Hall, Brian Donahue, Damon Armstrong, Francis Norton, James Moore, Jeff Hewitt, Jesse
Liberty, John Papa, Mike Bloise and Tilman Bregler 2008
ISBN 978-1-906434-12-0
The right of Amirthalingam Prasanna, Ben Hall, Brian Donahue, Damon Armstrong, Francis Norton, James Moore, Jeff Hewitt, Jesse
Liberty, John Papa, Mike Bloise and Tilman Bregler to be identified as the author of this work has been asserted by him in accordance with
the Copyright, Designs and Patents Act 1988
All rights reserved. No part of this publication may be reproduced, stored or introduced into a retrieval system, or transmitted, in any form,
or by any means (electronic, mechanical, photocopying, recording or otherwise) without the prior written consent of the publisher. Any
person who does any unauthorised act in relation to this publication may be liable to criminal prosecution and civil claims for damages.
This book is sold subject to the condition that it shall not, by way of trade or otherwise, be lent, re-sold, hired out, or otherwise circulated
without the publisher’s prior consent in any form other than which it is published and without a similar condition including this condition
being imposed on the subsequent publisher.
CONTENTS
Contents............................................................................................................................................................3
About the Authors ..........................................................................................................................................7
acknowledgements ..........................................................................................................................................9
Introduction .....................................................................................................................................................9
NET Application Architecture: the Data Access Layer..........................................................................11
Designing and building a robust data access layer..................................................................11
Layered design and the data access layer..............................................................................11
Design principals in the data access layer ............................................................................14
Exchanging Data with the DAL............................................................................................14
Putting theory into practice: the demo application ............................................................17
NET Application Architecture: Logging SQL Exceptions in the Data Access Layer.......................19
Managing SQL exceptions in .NET applications ...................................................................19
The real-world scenario ..........................................................................................................19
Solution architecture: objectives and overview...................................................................20
Building the SQL statement logger ......................................................................................22
The code in action: demo application ..................................................................................35
Conclusion ....................................................................................................................................36
ADO.NET 2.0 Factory Classes ..................................................................................................................37
Achieve database independence by developing a pluggable data layer ...............................37
Introduction..............................................................................................................................37
Supporting many database products.....................................................................................37
System.Data.Common namespace........................................................................................37
Conclusion ....................................................................................................................................40
Implementing Real-World Data Input Validation using Regular Expressions....................................41
Some real validation requirements .......................................................................................41
Basics: Implementing NUM using "^"…"$", "["…"]", "?" and "+"...............................41
Using "{" … "}", "(" … ")", "", and "d" to implement Repetition.................................42
Using "|" to implement a logical OR...................................................................................44
Using "(?=" … ")" to implement a logical AND ...............................................................44
Using "(?!" … ")" to implement AND NOT......................................................................45
Conclusion ................................................................................................................................46
References .................................................................................................................................46
NET 3.5 Language Enhancements ............................................................................................................47
Automatic Properties .............................................................................................................47
Object Initializers.....................................................................................................................48
iv
Collection Initializers...............................................................................................................49
Extension Methods .................................................................................................................50
Anonymous Types and Implicitly Typed Variables ............................................................51
Wrapping Up ............................................................................................................................52
NET Collection Management with C# 3.0 ..............................................................................................53
Using C# 3.0 to manage a collection of objects ....................................................................53
Introduction..................................................................................................................................53
Sorting a List.................................................................................................................................53
Searching a List.............................................................................................................................54
List Conversion ............................................................................................................................55
Linq ................................................................................................................................................55
Conclusion ....................................................................................................................................56
Exceptionally expensive ...............................................................................................................................57
Need to loosen my bindings........................................................................................................................61
Extending MSBuild.......................................................................................................................................62
Project Files are MSBuild Files..............................................................................................62
The Default Build Process .....................................................................................................62
Property, Item and Target Evaluation ..................................................................................63
Side Effects of Static Evaluation ..........................................................................................64
Discovering Predefined Properties and Items ....................................................................65
Referencing Different Dlls for Release and Debug Builds ...............................................65
Executing Custom Targets .....................................................................................................66
Extending All Builds ...............................................................................................................67
MSBuild Constrictions............................................................................................................67
Conclusion ................................................................................................................................68
Controls Based Security in a Windows Forms Application ...................................................................69
Users and roles .............................................................................................................................69
Roles and controls........................................................................................................................69
The database .................................................................................................................................70
Application and control-security forms ...................................................................................71
Creating the application ..............................................................................................................71
Creating users and roles..........................................................................................................73
The Manage Permissions page ..............................................................................................75
Implementing permissions – step-by-step...............................................................................78
ManagePermisssions constructor..........................................................................................79
ShowControls...........................................................................................................................79
Populate Permission Tree.......................................................................................................80
Adding a new restriction to a control...................................................................................83
v
Summary .................................................................................................................................115
Testing Times Ahead: Extending NUnit.................................................................................................117
Why Extend? ..............................................................................................................................117
NUnit Extension Points............................................................................................................117
How to Extend NUnit? ............................................................................................................118
Hello World ............................................................................................................................118
Suite Builders..........................................................................................................................121
Test Case Builders..................................................................................................................123
Test Decorators......................................................................................................................126
Event Listeners ......................................................................................................................129
Deployment and execution ..................................................................................................130
Summary......................................................................................................................................130
Using SQL Data Generator with your Unit Tests .................................................................................131
SQL Data Generator .................................................................................................................131
Automating Data Generation ..................................................................................................131
The Attribute Based approach.............................................................................................132
The POCO Wrapper approach ...........................................................................................134
Maintainability and coping with schema changes .................................................................134
Testing your Business Layer .....................................................................................................134
Summary......................................................................................................................................136
vii
John Papa
John Papa, Senior .NET Consultant at ASPSOFT, is a Microsoft MVP [C#], MCSD.NET, and an
author of several data access technology books. John has over 10 years' experience in developing
Microsoft technologies as an architect, consultant, and a trainer. He is the author of the Data Points
column in MSDN Magazine and can often be found speaking at user groups, MSDN Web Casts, and
industry conferences such as VSLive and DevConnections. John is a baseball fanatic who spends
most of his summer nights rooting for the Yankees with his family and his faithful dog, Kadi. You
can contact John at johnpapa.net.
Tilman Bregler
Tilman started work at Red Gate as a tester and is now build engineer. He has a BSc Maths and
Philosophy (comb.) from York and Part III Maths, Cambridge.
Francis Norton
Francis Norton works at iE (http//:www.ie.com), developing online retail finance applications in C#,
ASP.Net and SQL Server 2005, with special interests in XML technologies, Powershell and mainframe
integration. In an earlier life he contributed chapters to three Wrox books on XML. He's still trying to
work out if he gets more satisfaction from solving problems or from explaining the solutions.
ix
ACKNOWLEDGEMENTS
Grateful thanks are here given to all those who helped in the production of this Ebook, including
Claire Brooking, Anna Larjomaa, Simon Galbraith, Laila Lotfi and Tony Davis at Red-Gate. We’d also
like to thank the authors, who all submitted cheerfully to the editing process that had their
contributions altered to conform with Simple-Talk’s ‘house style’.
INTRODUCTION
This Ebook is a republication of the best of Simple-Talk’s .NET articles. These articles are written to
appeal to a wide range of readership. Simple-Talk has readers who work professionally as Developers,
Database Administrators, testers, Technical authors, Architects, IT managers. The articles are
designed to inform without patronising or ‘talking down’ to the less technical of the readers. Simple-
Talk always likes to take a slightly different approach to ‘Internet-based’ IT publishing and we are not
afraid of introducing humour or in going out of our way to explain highly esoteric subjects.
Simple-Talk is an online IT magazine that is designed for those who are professionally involved in
software development and who work with Microsoft Technologies, the most important to us being
SQL Server and .NET. Many of our readers are those who have bought, or shown an interest in, Red-
Gate tools, and the magazine is used, not only to provide general technical content, but also to
provide technical content about Red-Gate tools, and keep our readers abreast of any important
changes or developments at Red-Gate. We are independent of Microsoft, but we are, of course, Red-
Gate’s ‘house’ magazine, so are unapologetic for occasionally featuring Red-Gate products amongst
Microsoft’s and others, where it is relevant.
x
NET Application Architecture: the Data Access Layer 11
business layer. When used appropriately, a layered design can lessen the overall impact of changes to
the application.
The business tier
And of course, this brings us to the topic of business objects and the Data Access Layer (also known
as the DAL), two sub-layers within the business tier. A business object is a component that
encapsulates the data and business processing logic for a particular business entity. It is not, however,
a persistent storage mechanism. Since business objects cannot store data indefinitely, the business tier
relies on the data tier for long term data storage and retrieval. Thus, your business tier contains logic
for retrieving persistent data from the data-tier and placing it into business objects and, conversely,
logic that persists data from business objects into the data tier. This is called data access logic.
Some developers choose to put the data access logic for their business objects directly in the business
objects themselves, tightly binding the two together. This may seem like a logical choice at first
because from the business object perspective it seems to keep everything nicely packaged. You will
begin noticing problems, however, if you ever need to support multiple databases, change databases,
or even overhaul your current database significantly. Let's say, for example, that your boss comes to
you and says that you will be moving your application's database from Oracle to SQL Server and that
you have four months to do it. In the meantime, however, you have to continue supporting whatever
business logic changes come up. Your only real option is to make a complete copy of the business
object code so you can update the data access logic in it to support SQL Server. As business object
changes arise, you have to make those changes to both the SQL Server code base and the Oracle code
base. Not fun. Figure 2 depicts this scenario:
A more flexible option involves removing the data access logic from the business objects and placing
it all in a separate assembly known as the DAL. This gives you a clean separation between your
business objects and the data access logic used to populate those business objects. Presented with the
same challenge of making the switch from Oracle to SQL Server, you can just make a copy of the
Oracle DAL and then convert it to work with SQL Server. As new business requirements come in,
you no longer need to make changes in multiple locations because you only maintain a single set of
business objects. And when you are done writing the SQL Server DAL, your application has two
functional data access layers. In other words, your application has the means to support two
databases. Figure 3 depicts separating data access logic out into a separate DAL:
Figure 4 – Business objects assembly references the DAL, so the DAL has no concept of business
objects
Figure 5 – The business object assembly and the DAL assembly both reference a shared assembly, so
they can exchange information using classes and data structures from the shared assembly.
In practice, I find that building out custom classes solely to exchange data doesn't give you much
return for your effort, especially when there are other acceptable options already built into .NET.
The XML approach
You could opt to use XML since it's the poster child of flexibility and data-source independence and
can easily represent any data imaginable. Of course, it also means that you will be doing a lot of XML
parsing work to accommodate the data exchange, and I'm not a fan of extra work.
The database interface approach
You could also use the database interfaces from the System.Data namespace to exchange data
between business objects and the DAL. Database specific objects such as SqlDataReader,
SqlCommand, and SqlParameter are tied to SQL Server, and exposing them from the DAL would
defeat the purpose. However, by exposing an IDataReader, IDBCommand, or IDataParameter
object you do not tie yourself to particular database so they are an acceptable option, though not my
first choice.
From an academic standpoint, the database interface objects do tie you to using a "database
management system" even though they do not tie you to a specific database. Pure academics will tell
you that the DAL should be "data-source independent" and not just "database independent" so be
prepared for that fight if you have a Harvard or Oxford grad on your development team who
majored in theoretical application design. Nobody else on the planet cares because the chances of
your application moving away from a database system are fairly slim.
My preferred approach: DataSets
Another option for passing information, and the one that I gravitate towards because of its flexibility,
is the DataSet. Microsoft created the DataSet class specifically for storing relational information in
a non-database specific data structure, so the DataSet comes highly recommended for returning query
information containing multiple records and or tables of data. Your work load shouldn't suffer too
significantly from using the DataSet because DataAdapters, which fill DataSets with information,
already exists for most database systems. Furthermore, getting data out of the DataSet is fairly easy
because it contains methods for extracting your data as tables, rows, and columns.
Also note that a DataSet is technically data-source independent, not just database independent. You
can write custom code to load XML files, CSV files, or any other data source into a DataSet object.
Additionally, you can even manipulate and move information around inside the DataSet, something
that is not possible with the database interfaces from the System.Data namespace.
16 by Damon Armstrong
DataSet Person_GetAll()
{
//Returns a DataSet containing all people records in the database.
}
bool Person_Save(ref int personID, string fname, string lname, DateTime dob)
{
// Locates the record for the given personID. If the record exists,
// the method updates the record. If the record does not exist, the
// method adds the record and sets the personID variable equal to
// the identity value assigned to the new record. Then the method
// returns the value to the business layer because personID is
// marked with the ref modifier.
}
int Person_DeleteInactive()
{
//Deletes all inactive people in the database and returns a value
//indicating how many records were deleted.
}
Figure 6 – Breaking down the DAL into multiple data service classes
Notice that all of the data service classes depicted in Figure 3 derive from a single base class named
DataServiceBase. The DataServiceBase class provides common data access functionality like
opening a database connection, managing a transaction, setting up stored procedure parameters,
executing commands, and so forth. In other words, the DataServiceBase class contains the general
database code and provides you with a set of helper methods for use in the individual data service
classes. The derived data service classes use the helper methods in the DataServiceBase for
specific purposes, like executing a specific command or running a specific query.
The objective of the DAL is to expose information from the Data Tier to the Business Objects
without tying the Business Objects to any particular data source. It does this by using data-source-
specific calls to retrieve data from a particular data store, places the data in a non-data-source specific
variable or object, then passes the information to a Business Object for use in some operation.
Changing from one data source to another (say Oracle to SQL Server) is simply a matter of making a
new DAL to access the new data store and putting the data into the format the Business Objects
expect. You never need to touch the business objects when making the switch.
NET Application Architecture: Logging SQL Exceptions in the Data Access Layer 21
Figure 2 – Breaking down the Data Access Layer (DAL) into multiple Data Service classes
have to do is set up your application to catch the exception and log it along with the SQL statement
that accompanies it.
A note on transactional support and configuration
One issue that came to mind while I was putting the code for this article together was the fact that
some SQL statements occur within a transaction. If that is the case, and you just capture the SQL
statement that caused the error, without providing the rest of the SQL statements that preceded it in
the transaction, then you may not be getting everything that you need to effectively track down an
issue. However, if you want to capture all of the SQL in a transaction, it means you have to log all the
"good" statements that lead up to the bad statement, and chances are that a "bad" statement won't
happen very often. So you'll be doing a lot of logging to ensure the information is available when you
need it, but most of it will simply be discarded because you won't need it that often. And that is not
good for performance.
But there may be a time when the benefit of tracking down an error outweighs the performance cost,
so we'll make full transactional logging a configurable setting in the web.config in case you ever really
need it. And while we're at it, we'll also put in a configuration setting that allows you to turn off SQL
statement logging altogether. The configuration settings will be stored in the <appSettings> section
of the web.config, as shown in Listing 1.
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="SqlLogging" value="true" />
<add key="SqlFullTxnLogging" value="true" />
</appSettings>
...
</configuration>
Listing 1 – Configuration settings for SQL Logging in the appSettings section of the web.config
At this point you should have a good high-level overview of the solution, so we’ll shift gears and
begin looking at the actual implementation details next.
using System;
namespace Demo.Common
{
[Serializable]
public class SqlWrapperException : Exception
{
/////////////////////////////////////////////////////////
private string _sql = string.Empty;
/////////////////////////////////////////////////////////
public string SQL
{
get { return _sql; }
set { _sql = value; }
}
/////////////////////////////////////////////////////////
public SqlWrapperException(string sql, Exception inner)
: base(inner.Message, inner)
{
this.SQL = sql;
}
}
}
The SqlWrapperException class inherits its base exception functionality from the
System.Exception class. It's marked as Serializable because Microsoft says it's a best practice
(presumably because there are a number scenarios where exceptions need to be serialized). Other
than that, this class is pretty simple. It has a private field named _sql to store the SQL statement
value, and a public property named SQL that exposes that field. There is a single constructor that
accepts two parameters: the SQL statement that you want to log and the data-access exception
thrown while trying to execute that statement. The call to base(inner.Message, inner) sets the
Message property of the SqlWrapperException equal to the Message property on the original
exception, and assigns the InnerException property of the SqlExceptionWrapper to the
original data-access exception. This effectively wraps the data-access exception with our custom
exception wrapper. The constructor then sets the SQL property of the exception wrapper so the SQL
statement can travel along with the exception to wherever it ultimately needs to go.
New SQL exception logging fields in the DataServiceBase class
Since this article builds off the demo application from my previous article, I'm just going to cover
what you need to add to the DataServiceBase class to add support for SQL exception logging
instead of going back over everything that it does. Listing 3 shows the four new fields the class uses
for SQL statement logging.
////////////////////////////////////////////////////////////////////////
// Fields for SQL Error Logging
////////////////////////////////////////////////////////////////////////
private StringBuilder _errorSQL = null; //Stores SQL Text
24 by Damon Armstrong
The first field, _errorSQL, is a StringBuilder that stores the logged SQL statement. I opted to
use a StringBuilder because the code needs to do a lot of string concatenation to build the SQL
statement, a situation in which the StringBuilder's performance is far better than regular fixed-
length string. After that, we have two static configuration fields, _sqlLogging and
_sqlFullTxnLogging. These fields store values that determine whether or not SQL logging is
enabled and whether or not to use full transactional logging, respectively. Although both of these
fields are integer variables, they actually represent Boolean data, but we'll discuss that in more detail
when we take a look at the properties that expose these values. Last, we have a static Dictionary
field named _sqlTxnSBMapping. A Dictionary object allows you to reference values in the
Dictionary based on a key. In this case, our key is an integer, and our value is a StringBuilder
object. What the _sqlTxnSBMapping field allows us to do is associate a StringBuilder object
(the Dictionary value) with a particular database transaction (the Dictionary key). Why isn't the
key a SqlTransaction then? Because we store the hash value (an int) of the SqlTransaction as
the key and not the actual SqlTransaction object itself.
Configuring logging with the SqlLogging and SqlFullTxnLogging properties
We want to make the SQL statement logging capabilities of this demo configurable so they can be
turned on and off. We already have the fields that we need to store the values, _sqlLogging and
_sqlFullTxnLogging, so now we have to create a way to get the settings from the web.config. I
opted to put that code directly in the properties that expose those fields. The only problem is that
we're dealing with configurable Boolean values. Configurable Boolean values have three states: un-
initialized, true (on), and false (off). But a Boolean variable only has two states: true and false. That's
why the _sqlLogging and _sqlFullTxnLogging fields are integers. We use -1 to represent un-
initialized, 0 to represent false, and 1 to represent true. Listing 4 shows how this all plays out inside
the SqlLogging property.
////////////////////////////////////////////////////////////////////////
public static bool SqlLogging
{
get
{
if (_sqlLogging == -1)
{
bool value = Convert.ToBoolean(
ConfigurationManager.AppSettings["SqlLogging"]);
_sqlLogging = value ? 1 : 0;
}
return _sqlLogging == 1 ? true : false;
}
set
{
_sqlLogging = value ? 1 : 0;
}
}
Inside the Get portion of the property, the code begins by checking to see if the _sqlLogging field
is equal to -1. If so, it indicates that it is in its un-initialized state and we need to get the appropriate
configuration value from the web.config. Inside the if block, we acquire the SqlLogging value
NET Application Architecture: Logging SQL Exceptions in the Data Access Layer 25
from the appSettings section of the web.config, convert that string value to a bool value, then
store it in the value variable. What happens if you don't have a SqlLogging setting defined in the
appSetting section? Then the ConfigurationManager returns a null value for
AppSetting["SqlLogging"] and Convert.ToBoolean interprets the null value as false. So if
you don't specify the settings, then it's the same as setting them to false. Then the code determines if
_sqlLogging is equal to 1 and, if so, returns true. Otherwise it returns false. The Set portion of the
property is fairly simple. It takes the value assigned to the property and sets _sqlLogging to 1 if the
value is true, or 0 if the value is false.
Listing 5 contains the code for the SqlFullTxnLogging property. It's basically the same code that
you saw for the SqlLogging property, but it checks to see if SqlLogging is enabled before running
any of the code that initializes and returns the SqlFullTxnLogging value. If SqlLogging is not
enabled, then SqlFullTxnLogging returns false because disabling SqlLogging disables all logging.
If SqlLogging is enabled, then it runs through the same logic that we discussed for the
SqlLogging property to determine if SqlFullTxnLogging is enabled.
////////////////////////////////////////////////////////////////////////
public static bool SqlFullTxnLogging
{
get
{
if (SqlLogging)
{
if (_sqlFullTxnLogging == -1)
{
bool value = Convert.ToBoolean(
ConfigurationManager.AppSettings["SqlFullTxnLogging"]);
_sqlFullTxnLogging = value ? 1 : 0;
}
///////////////////////////////////////////////////////////
public StringBuilder ErrorSQL
{
get
{
if (_errorSQL == null)
{
_errorSQL = new StringBuilder();
}
return _errorSQL;
26 by Damon Armstrong
}
set
{
_errorSQL = value;
}
}
///////////////////////////////////////////////////////////
private string GetSqlStatementForException()
{
if (_errorSQL != null)
{
string value = _errorSQL.ToString();
_errorSQL.Length = 0;
return value;
}
else
{
return string.Empty;
}
}
And last, we have the ClearSqlStatementLog method shown in Listing 8. This method just gives
you an efficient way to clear any SQL statements in the _errorSQL StringBuilder. You could
accomplish the same thing by calling ErrorSQL.Clear(), but remember that ErrorSQL will create
a new StringBuilder if _errorSQL is null. ClearSqlStatementLog allows you to avoid
inadvertently creating a StringBuilder. We never really use this in the demo app, but it's there in
case you ever need it.
////////////////////////////////////////////////////////////
public void ClearSqlStatementLog()
{
if (_errorSQL != null)
{
_errorSQL.Length = 0;
}
}
log all of the SQL statements that run in a given transaction. It is possible, and fairly likely, that you
will have different Data Service classes participating in a given transaction. Listing 9 shows one
possible scenario:
txn.Commit();
Remember, SQL statements are stored in the StringBuilder object associated with the ErrorSQL
property in a DataService class. So the question is, how do you share a StringBuilder between
different DataService classes? One option is to pass the StringBuilder object around all over
the place, but that would make for some pretty nasty code. Instead, we're going to store
StringBuilder objects in a static property and associate those StringBuilder objects with a
particular transaction. And this is the entire reason the _sqlTxnSBMapping Dictionary field exists.
Whenever we instantiate a DataService that uses a transaction, we can use the
_sqlTxnSBMapping Dictionary to look up the appropriate StringBuilder object for that
transaction. But that means that you need a structured way of adding the StringBuilder to the
Dictionary when you begin a transaction, and a structured way of removing the StringBuilder
when you commit or roll back the transaction.
So, there are three new static methods on the DataServiceBase class that assist you in that
endeavor: BeginTransaction, CommitTransaction, and RollbackTransaction. Listing 10
shows the code for all three of these methods.
////////////////////////////////////////////////////////////
public static IDbTransaction BeginTransaction()
{
SqlConnection txnConnection =
new SqlConnection(GetConnectionString());
txnConnection.Open();
if (SqlFullTxnLogging)
{
StringBuilder sbTemp = new StringBuilder();
_sqlTxnSBMapping.Add(txn.GetHashCode(), sbTemp);
sbTemp.AppendLine("BEGIN TRANSACTION");
}
return txn;
}
////////////////////////////////////////////////////////////
public static void RollbackTransaction(IDbTransaction txn)
{
if (txn != null)
{
if (SqlFullTxnLogging)
28 by Damon Armstrong
{
StringBuilder sbTemp;
if (_sqlTxnSBMapping.TryGetValue(
txn.GetHashCode(), out sbTemp))
{
sbTemp.Append("ROLLBACK TRANSACTION");
_sqlTxnSBMapping.Remove(txn.GetHashCode());
}
}
txn.Rollback();
}
}
////////////////////////////////////////////////////////////
public static void CommitTransaction(IDbTransaction txn)
{
if (txn != null)
{
if (SqlFullTxnLogging)
{
StringBuilder sbTemp;
if (_sqlTxnSBMapping.TryGetValue(
txn.GetHashCode(), out sbTemp))
{
sbTemp.Append("COMMIT TRANSACTION");
_sqlTxnSBMapping.Remove(txn.GetHashCode());
}
}
txn.Commit();
}
}
When you call BeginTransaction, the method needs to create the StringBuilder object that's
going to capture the SQL for the duration of that entire transaction. It also needs to associate that
StringBuilder object to the transaction in the _sqlTxnSBMapping dictionary. In the bolded
BeginTransaction code, you can see the if statement that checks to see if full transaction logging
is enabled. If so, the method creates the new StringBuilder then adds it to the dictionary. Notice
that it uses the txn.GetHashCode() method to generate the int value used as the dictionary key.
Then it outputs BEGIN TRANSACTION to the StringBuilder so your SQL will run in a transaction
when you copy and paste it into a query window. Once the StringBuilder is in the static
_sqlTxnSBMapping dictionary, the individual DataService instances can easily share that
StringBuilder. We'll see how, when we get to the constructor changes in a moment.
Next we have the RollbackTransaction and CommitTransaction code. These are identical
methods except that one commits the transaction, and one rolls the transaction back. In the bolded
code for each method, you can see if the if statement that runs when full transaction logging is
enabled. It starts by declaring a StringBuilder variable, then calls _sqlTxnSBMapping.-
TryGetValue in an attempt to find the StringBuilder associated with the transaction passed into
the method. If it finds the StringBuilder in the dictionary, it then loads that object into the
sbTemp variable and appends ROLLBACK TRANSACTION or COMMIT TRANSACTION depending on
which method you called. Finally, it removes the StringBuilder object from the dictionary because
you are, in theory, done with the transaction.
So, here's the deal. If you want the full transaction logging to work appropriately, you need to use
these methods to manage your transactions. Failing to call BeginTransaction means that you may
not get all of the SQL statements from the transaction in your SQL log, and failing to call
CommitTransaction or RollbackTransaction means that the _sqlTxnSBMapping will have
orphaned StringBuilder objects hogging up memory. And if you are doing transactions within
transactions, then you will need to write some custom code that stores parent-to-child relationships
between transactions because, if I account for that here, then this article is just going to get that much
NET Application Architecture: Logging SQL Exceptions in the Data Access Layer 29
longer. This should suffice for most needs. Listing 11 shows an updated example of how you use
these new methods in your code.
Listing 11 – Multiple Data Service classes participating in a transaction with transaction logging
////////////////////////////////////////////////////////////
public DataServiceBase(IDbTransaction txn)
{
if (txn == null)
{
_isOwner = true;
}
else
{
_txn = (SqlTransaction)txn;
_isOwner = false;
if(SqlFullTxnLogging)
{
_sqlTxnSBMapping.TryGetValue(
_txn.GetHashCode(), out _errorSQL);
}
}
}
The code in bold shows the updates made to the transaction constructor. As you can see, the if
statement checks to see if full transaction logging is enabled and, if so, the code looks for the
StringBuilder associated with the transaction in the dictionary. If there is no StringBuilder in
the dictionary it means that you did not call BeginTransaction to start the transaction. When your
DataService goes to log a SQL statement, it will call the ErrorSQL property, which automatically
creates a StringBuilder object and assigns it to _errorSQL. So, the SQL statements the
30 by Damon Armstrong
DataService executes will still be logged, but you may not get to see a complete log of the
statements in the transaction if an exception occurs.
Logging SQL statements with the BuildSQL method
The BuildSQL method is responsible for taking a SqlCommand object and producing the SQL
statement that gets attached to the SqlWrapperException. Although there's a lot of code in this
method, the majority of it has to do with formatting the SQL statement appropriately. So it's lengthy,
but not overly complex. This particular method focuses on creating SQL statements to run stored
procedures. You can modify it to write both stored procedure statements as well as ad hoc SQL
statements if you so choose (all you have to do is switch the logic based on the cmd.CommandType
property). There are four sections of code in the BuildSQL method:
1. Validating the CMD object
2. Declaring output parameters
3. Writing the EXEC statement
4. Writing out the stored procedure parameters and values
In Listing 13 you will find all of the code for the BuildSQL method. Bolded comments indicate the
starting point for each of the four sections outlined above. We'll discuss each section of code in more
detail after the listing.
///////////////////////////////////////////////////////////////////////
protected void BuildSQL(SqlCommand cmd)
{
}
}
ErrorSQL.Append(cmd.Parameters[index].ParameterName);
switch (cmd.Parameters[index].Direction)
{
32 by Damon Armstrong
case ParameterDirection.Input:
ErrorSQL.Append(" = ");
if (cmd.Parameters[index].Value == DBNull.Value
|| cmd.Parameters[index].Value == null)
{
ErrorSQL.AppendLine("NULL");
}
else
{
ErrorSQL.Append("'");
ErrorSQL.Append(cmd.Parameters[index].
Value.ToString());
ErrorSQL.Append("'");
}
break;
case ParameterDirection.InputOutput:
case ParameterDirection.Output:
ErrorSQL.Append(cmd.Parameters[index].
ParameterName);
ErrorSQL.Append(" OUTPUT");
break;
}
}
}
ErrorSQL.AppendLine(";");
ErrorSQL.AppendLine("GO");
Section 1 deals with validating the SqlCommand object. All this code does is check to see if cmd is
null. If it is null, then the method writes a SQL comment to the ErrorSQL StringBuilder
indicating that it could not write out a SQL statement for the command. Then it calls return to exit
the method because the method can't do much with a null command.
Section 2 declares output parameter variables to help in debugging the stored procedure. Many
stored procedures return information back from the stored procedure in the form of a return value
or an output parameter. If you want to check the values of these outputs, you need a way to reference
them after the stored procedure executes, so you need to declare SQL variables to store those
outputs. To help out in this endeavor, the BuildSQL method automatically creates variable
declarations for all return value and output parameters in your stored procedure. Listing 14 shows an
example SQL statement that includes variables to store the return value (@RV) and an output
parameter (@PersonID).
To write the output parameters, Section 2 begins by checking to see if the ErrorSQL
StringBuilder contains text. If you have enabled SqlFullTxnLogging then it is possible for
SqlError to contain previous SQL statements from the transaction, and you do not want multiple
NET Application Architecture: Logging SQL Exceptions in the Data Access Layer 33
statements to run together because this would be hard to read. So the code adds a line break between
the statements to help break them apart and to make the SQL statements easier to read. Next, the
code iterates through all of the parameters in the command and runs an if statement that checks to
see if the Direction property is anything other than ParameterDirection.Input. If so, it
means that the parameter handles some form of output from the stored procedure and that we need
to write out a variable to store that output. The first four lines inside the if block output the
DECLARE keyword, parameter name, a space, and the variable type. Then the code checks to see if the
parameter has a size specified. If so, the method checks to see if the parameter also has a precision.
If both the size and precision are specified, then method outputs them both in the form
(<size>,<precision>). If only the size is specified, then the method outputs (<size>). Finally, the
method appends the ending semicolon, writes a comment indicating the parameter direction, and
appends a line break.
Next, the code determines whether or not the parameter's Direction is set to
ParameterDirection.InputOutput. If it is, it means that the stored procedure is expecting a
value to come in via the parameter and that the stored procedure can pass a value out via the
parameter as well. For simple input parameters, we can declare the parameter values in the EXEC
statement (see the @NameFirst parameter in Listing 14). But SQL does not allow you to pass in a
value and specify the OUTPUT keyword. So you have to set the parameter value before you call the
EXEC statement (see 3rd line of Listing 14). So, if the parameter is an InputOutput parameter, the
code outputs the SET statement, the parameter name, and an equals sign. It then checks to see if the
parameter value is set to DBNull.Value. If so, it outputs SQL that sets the parameter variable to
null. If not, it outputs SQL that sets the parameter variable to the appropriate value. And finally, it
appends a semicolon.
Section 3 is responsible for writing out the EXEC statement, and begins by writing out the EXEC
keyword. Then it iterates through all of the parameters, checking to determine which parameter, if
any, handles the return value. If it finds a parameter whose Direction is
ParameterDirection.ReturnValue, it outputs the parameter name and an equals sign. This sets
up the parameter to receive the return value of the procedure (refer to Listing 14). Then the code
outputs the name of the stored procedure.
Section 4 writes out the stored procedure parameters and their values. The code in this section uses a
for loop to iterate through all of the parameters in the command. The if statement directly inside
the for loop checks the Direction property of the parameter to make sure we don't add the return
value parameter to the list of stored procedure parameters, because we've already accounted for the
return value in Section 3. Once the code has determined it's dealing with a stored procedure
parameter, it checks the index of the for loop to see if this is the first parameter in the list. If it is the
first parameter, the code appends a space to separate the stored procedure name from the parameter
list. If it is not the first parameter, the code appends a comma and a space to separate the parameters.
Then it outputs the parameter name. After that, the code checks the Direction property of the
parameter. If the parameter is an Input parameter, then the code writes an equals sign and the
parameter value to ErrorSQL using the same logic discussed in Section 2. If the parameter is an
Output or an InputOutput parameter, the code writes the OUTPUT keyword to indicate that the
stored procedure passes a value back out using the parameter. And at the very end, the code writes
out a semicolon, a line break, and the GO keyword, to finish off the SQL statement.
Whew! That was the final and biggest addition to the DataServiceBase class. Now we just need to
see exactly how to use all of these new properties and methods in the existing code.
Catching exceptions and logging the SQL statements
When you execute a SQL statement and it fails, your code throws an exception. All you have to do is
catch the exception, pass the failed command to the BuildSQL method to log the SQL, create a
SqlWrapperException to wrap the actual exception, assign the contents of ErrorSQL to the
SqlWrapperException, then throw the wrapped exception. This allows the exception to propagate
back up to the caller along with the SQL that caused the exception to occur. There are only two
places in the DataServiceBase class where we execute SQL commands: the ExecuteDataSet
34 by Damon Armstrong
method, and the ExecuteNonQuery method. Listing 15 contains the updated code for the
ExecuteNonQuery method.
//////////////////////////////////////////////////////////////////////
protected void ExecuteNonQuery(out SqlCommand cmd, string procName,
params IDataParameter[] procParams)
{
//Method variables
SqlConnection cnx = null;
cmd = null; //Avoids "Use of unassigned variable" compiler error
try
{
//Setup command object
cmd = new SqlCommand(procName);
cmd.CommandType = CommandType.StoredProcedure;
for (int index = 0; index < procParams.Length; index++)
{
cmd.Parameters.Add(procParams[index]);
}
Inside the catch statement, the code begins by checking to see if SQL logging is enabled. If not, the
code simply re-throws the original exception, without worrying about logging anything. If SQL
NET Application Architecture: Logging SQL Exceptions in the Data Access Layer 35
logging is enabled, the code passes the cmd variable to the BuildSQL method. The cmd variable
contains the SqlCommand that was executing when the exception occurred, and contains all of the
information that needs to be logged. When the BuildSQL method finishes, the StringBuilder in
the _errorSQL field contains the SQL log information. It then appends a SQL comment to the log
indicating that the last command in the log caused the exception. After that, the code checks to see if
there is an active transaction in the _txn variable and if full transaction logging is enabled. If that is
the case, then the code appends a ROLLBACK TRANSACTION statement to the SQL log to avoid
accidentally committing the SQL during debugging. Then, regardless of whether or not full
transaction logging is enabled, it creates and throws a new SqlWrapperException. Notice that it
uses GetSqlStatementForException to pass the SQL statement and to clear the SQL log. You
will find similar code in the catch block of the ExecuteDataSet method.
And the last bit of code we need to discuss in the ExecuteNonQuery method is the line of bolded
code that appears in the final block. When full transaction logging is enabled, you have to log the
good statements as well as the bad ones. If the method gets to this line of code, it means that the
command succeeded. The line simply checks to see if full transaction logging is enabled and, if so,
logs the successful command.
Notice that the first catch statement explicitly looks for the SqlWrapperException. If the
RandomProcThrowError method throws a SqlWrapperException (I say "if" because you can
turn off SQL logging altogether), the code in the first catch blocks executes and displays the
statements stored in the SQL property of the SqlExceptionWrapper. If the
RandomProcThrowError does not throw a SqlWrapperException, then the second catch block
catches the exception. This demonstrates a key point. Somewhere in your exception logging code you
will need to check and see if the exception is a SqlWrapperException, and then process the SQL
property accordingly, to ensure it gets stored for future reference. Most exception logging tools allow
you to store extended exception properties, so it's just a matter of using the exception logging tool
36 by Damon Armstrong
appropriately. You can also check out the PersonCreateErrorInTXN.aspx page to see the output
from an exception that occurs within a transaction. The demo application has SQL logging and full
transaction logging enabled by default. Feel free to change the settings and rerun the pages to see the
various outputs.
Conclusion
You've seen the basics for logging exception-causing stored procedures, and you've seen how to pass
a log of SQL statements back to your application using an exception wrapper. So you've got one
more tool in your arsenal for tracking down nasty bugs. Remember, if you are worried about
performance, then shy away from the full transaction logging because it has to build out SQL
statements for every command that executes. SQL logging alone should not affect performance too
badly, since it only runs when an exception occurs. If you want, you can always expand on the
solution and add support for ad hoc SQL statements or transaction-inside-transaction support. You
could even go as far as to add configuration options to turn on full transaction logging for a specific
DataService, instead of all of them, to help out in the performance area. At any rate, good luck with
it!
ADO.NET 2.0 Factory Classes 37
This article explains how to use .NET 2.0’s data provider factory classes to develop a pluggable
data layer that is independent of database type and ADO.NET data provider.
Introduction
If you want to develop a data layer that supports many types of database products, it should not be
tightly coupled with a particular database product or ADO.NET data provider. The fact that
ADO.NET has data providers that are enhanced for specific databases makes that independence
more difficult and cumbersome to achieve.
You should have a good understanding of the .NET framework and familiarity with the ADO.NET
library before using .NET 2.0’s data provider factory classes to create a pluggable data layer.
System.Data.Common namespace
Take the following ADO.NET code that connects to a SQL Server database and execute an arbitrary
SQL statement:
C# Code
There are quite a few problems in the code above that would make it difficult to modify to work with
a different database product. One obvious change that is needed is to move the hard-coded
connection string information out to a configuration file. Another problem is that we are tying our
code to a particular ADO.NET data provider, in this case the SQL Client data provider. This
increases the changes that are needed if we want to support another database product.
Now let us see how the code is changed after we move the connection string out to the application
configuration file and use the classes in the System.Data.Common namespace instead of the SQL
Client ADO.NET data provider:
C# Code
App.config file
<configuration>
<appsettings>
<add key = "provider" value ="System.Data.SqlClient"/>
<add key = "connectionString" value ="Data Source=.;
initial catalog=Northwind;Integrated security=true" />
</appsettings>
</configuration>
In the code above, other than isolating the connection string, we have used the common ADO.NET
data provider in the System.Data.Common namespace. This is a simple implementation of the
abstract factory pattern. Each ADO.NET data provider has a factory class that enables us to create
ADO.NET objects of its provider type.
The SQL Client ADO.NET data provider, for example, has a SqlClientFactory that can be used to
create SqlConnection, SqlCommand, and other SQL Client ADO.NET data provider-specific objects.
Based on the string value that is passed to the GetFactory method of the DbProviderFactories class,
a concrete instance of a particular ADO.NET data provider factory will be created. Instead of
creating the connection and the command objects directly, we use this factory instance to create the
necessary ADO.NET objects for us.
The code above shows that we are passing the string value System.Data.SqlClient from the
application configuration file, indicating that we want a SqlClientFactory object to be created and
assigned to the factory variable. From that point on, all the create methods of the DbProviderFactory
object will create ADO.NET objects of the SQL Client ADO.NET data provider.
The classes in ADO.NET have been altered from .NET 1.1 to inherit common base classes from the
SystemData.Common namespace. ADO.NET connection classes such as SqlConnection and
OleDbConnection inherit from the DbConnection class, for example. The following diagram shows
the inheritance hierarchy of the factory classes and the ADO.NET classes:
ADO.NET 2.0 Factory Classes 39
Provided we have used standard SQL statements, we can easily make our product work with a
different ADO.NET data provider by changing the provider in the application configuration file. If
we set it to System.Data.OleDb, an OleDbFactory class will be created, which will create OleDb data
provider-specific ADO.NET objects such as OleDbConnection and so on.
You might also want to list all of the available ADO.NET data providers. You can do so using the
GetFactoryClasses method of the DbProviderFactories class:
C# Code
DataTable tbl =
System.Data.CommonDbProviderFactories.GetFactoryClasses();
dataGridView1.DataSource = tbl;
foreach (DataRow row in tbl.Rows)
{
Console.WriteLine(row["InvariantName"].ToString());
}
The GetFactoryClasses method returns a data table containing information about the available
ADO.NET data providers. The InvariantName column provides the necessary string value needed to
pass to the GetFactory method in order to create a factory for a particular ADO.NET data provider.
One disadvantage of using the factory classes and developing a common data layer is that it limits us
to standard SQL statements. This means we cannot take advantage of the full functionality of a
particular database product.
One way to overcome this is to make a check on the type of ADO.NET object created by a factory
and execute some statements based on it. Though it’s not an elegant approach, it is useful when we
need to execute database product-specific SQL statements. For example:
C# Code
DbProviderFactory factory =
DbProviderFactories.GetFactory("System.Data.SqlClient");
DbCommand cmd = factory.CreateCommand();
if(cmd is System.Data.SqlClient.SqlCommand)
{
//set command text to SQL Server specific statement
}
else if (cmd is System.Data.OleDb.OleDbCommand)
{
//set command text to OleDb specific statement
}
40 by Amirthalingam Prasanna
Conclusion
The ADO.NET data providers in .NET 2.0 provide factory and common ADO.NET classes that
make it easy to keep your code independent from a particular ADO.NET data provider or database
product.
Implementing Real-World Data Input Validation using Regular Expressions 41
NOTE:
If you're new to Powershell, you can read "[Regex]::IsMatch" as "use the static method
'IsMatch' of the .NET library 'Regex'". In fact we could use Powershell's "–cmatch"
operator, which is precisely equivalent to a [Regex]::IsMatch() expression, but I like the
clarity of using the .NET class directly.
The square bracket expression is a character class. In effect, it gives us a concise way of doing a
character-level OR expression, so "[0-9]" can be understood as "does the input character equal 0, 1,
2…or 9?" The dash ("-") acts as a range operator in this context so "[0-9]" is exactly equivalent to
"[0123456789]".
At the moment we're simply testing whether the test string contains a match for the regex, which
would be fine for searches, but when we're doing validation we want to ensure that the test string
doesn't also contain non-matching text. For example:
We can stop that behaviour using the special characters "^" and "$" to specify that the regex pattern
must match from the start to the end of the test string:
Now we'll make the regex accept one or more digits by using the "+" modifier on the "[0-9]"
character class. The "+" means, in general, "give me one or more matches for whatever I've been
attached to", so in this case means "give me one or more digits".
That just leaves the optional minus sign. The good news and the bad news is that outside a character
class (like "[0-9]") the dash is just a literal character (good news because it means we won't have to
escape it; bad news because treating the same character as a literal in some parts of a pattern and a
special character in others is a triumph of terseness over readability). We'll make it optional with the
"?" modifier, which can be read as "give me zero or one matches".
Using "{" … "}", "(" … ")", "\", and "d" to implement Repetition
These "?" and "+" modifiers are very nice and convenient, but suppose we have a counting system
that can express more than None, One, and Many?
Let's take another look at the DECIMAL format requirement:
Implementing Real-World Data Input Validation using Regular Expressions 43
Dec: May be fixed length. A numeric amount (positive or negative) including a maximum of 2
decimal places unless stated otherwise, for example 12345.5, 12345, 12345.50 or -
12345.50 are all valid Dec 7 inputs
Ignoring the fixed length option for now, let's look at the decimal section. It seems that we're
expected to accept numbers with a decimal point and one or two decimals or with no decimal point
and decimals at all.
Our first challenge is the decimal point. We want to use the "." sign, but this gives us some strange
behaviour:
We've discovered that "." is a special character in regular expressions – in fact it matches any
character. We need to escape it with the "\" prefix to make it a literal:
The next step is to use the braces modifier to specify that we want one to two digits following the
decimal point – we can put the minimum and maximum number of matches (in our case 1 and 2,
which we'll test with zero to three) inside the "{" and "}" curly brackets:
Now we can add the entire decimal suffix pattern, " \.[0-9]{1,2}", to our existing number pattern, and
test it:
Aha, we should still be accepting numbers with no decimal places, but we're not. We know how to
make a single character optional using the "?" modifier, but how can we do this to larger sub-
patterns? The pleasantly obvious answer is to use parentheses to wrap the decimal suffix sub-pattern
in "(" and ")", and then apply the "?".
And before we leave this pattern, one more trick to make regular expressions more readable and more
robust: we can replace "[0-9]" with "\d" (escape + d) which is pre-defined to mean "any digit". Be
aware that this is case-sensitive and "\D" means the opposite!
We can match one pattern or the other using the "|" (or) operator. We're going to have to use
parentheses too, as we'll discover when we start testing.
PS C:\Notes> [Regex]::IsMatch("123456",
"^\d\d\d\d\d\d|\d\d-\d\d-\d\d$")
True
PS C:\Notes> [Regex]::IsMatch("123456 la la la",
"^\d\d\d\d\d\d|\d\d-\d\d-\d\d$")
True
What happened when we matched that second value? The "$" sign at the end of the pattern was
intended to reject input with text following the sort code itself, but the "|" meant that it was only
applied to the right-hand sub-pattern. (Try working out how to get a sort code with leading junk
accepted by the pattern above)
We can fix this by using parentheses again:
PS C:\Notes> [Regex]::IsMatch("123456",
"^(\d\d\d\d\d\d|\d\d-\d\d-\d\d)$")
True
PS C:\Notes> [Regex]::IsMatch("123456 la la la",
"^(\d\d\d\d\d\d|\d\d-\d\d-\d\d)$")
False
pattern, which is relatively simple and well-tested, and apply a second regular expression to count the
number of digits, each optionally preceded by a non-digit character.
Remembering that "\d" means "any digit" and "\D" means "any non-digit", we can do this to restrict
the input to, say, no more than seven digits:
This is fine if we're in a position to validate a single input with multiple regular expressions, but
sometimes we're going need to do it all in one regex. This raises a problem – both of our expressions
necessarily start at the beginning of the input string and work their way, character by character, to the
end. If we are going to do "logical and" patterns as opposed to simply "and then" patterns, we need a
way of applying multiple sub-patterns to the same input.
Fortunately .NET regular expressions support the obscurely named, but very powerful, "lookahead"
feature which allows us to do just that. Using this feature we can, from our current position in the
input string, test a pattern over the rest of the string (all the way to the end if necessary), then resume
testing from where we were.
A lookahead sub-pattern is wrapped in "(?=" … ")" and here's how we can use it to implement the
requirement "up to seven digits AND a valid decimal number" by combining our two existing
patterns:
PS C:\Notes> [Regex]::IsMatch("-123456.7",
"^(?=(\D*\d){1,7}$)-?\d+(\.\d{1,2})?$")
True
PS C:\Notes> [Regex]::IsMatch("-123456.78",
"^(?=(\D*\d){1,7}$)-?\d+(\.\d{1,2})?$")
False
PS C:\Notes> [Regex]::IsMatch("-12345.6.7",
"^(?=(\D*\d){1,7}$)-?\d+(\.\d{1,2})?$")
False
Now let's write a pattern that will find any obvious variation of "P O Box" anywhere after the start
of the input, which is where we test it. Remember from earlier that the space character is a literal, and
that the "." is a special character unless we escape it, "\."
46 by Francis Norton
Next, we'll reverse the result by asking for the pattern not to be found, and combine it with our
alphanumeric pattern, both done using "(?!" … ")" notation:
Finally, we'll make the PO Box rule case-insensitive. This can be done by setting a mode at the start
of the expression that will apply to everything that follows it. We can specify "case insensitive mode"
with the notation "(?i)" – notice that since we're going to be case-insensitive anyway, I've also
simplified the alpha bit of the alphanumeric pattern
Conclusion
Like any good tool, regular expressions can be used or abused. The purpose of this article is to help
you write regular expressions that are fit for the purpose of validating inputs against typical business
validation rules.
In order to do this we've covered writing straight-forward patterns using literals, special characters
and character classes, and applying them to the whole input using "^" … "$". We've also seen how to
combine simple patterns to implement logical OR, AND and NOT rules.
References
Using Regular expressions to validate input in ASP.NET:
http://www.devhood.com/tutorials/tutorial_details.aspx?tutorial_id=46
Using Regular expressions to in SQL Server 2005:
http://msdn.microsoft.com/msdnmag/issues/07/02/SQLRegex/default.aspx
Regular expression options in the .NET library:
http://msdn2.microsoft.com/en-us/library/yd1hzczs(VS.80).aspx
A concise summary of all special characters recognised by .NET regular expressions:
http://regexlib.com/CheatSheet.aspx
NET 3.5 Language Enhancements 47
Automatic Properties
Since creating classes by hand can be monotonous at times, developers use either code generation
programs and IDE Add-Ins to assist in creating classes and their properties. Creating properties can
be a very redundant process, especially when there is no logic in the getters and setters other than
getting and setting the value of the private field. Using public fields would reduce the code required,
48 by John Papa
however public fields do have some drawbacks as they are not supported by some other features such
as inherent data binding.
One way to get around having to type the code for a private field and its public property getter and
setter is to use a refactoring tool. However, there is a new language feature called Automatic
Properties that allows you to type less code and still get a private field and its public getter and setter.
You declare the automatic property using a shortcut syntax and the compiler will generate the private
field and the public setter and getter for you. For example, Figure 2 shows a Customer class that has
several private fields that are exposed through a series of corresponding public properties. This class
has 4 properties including one that is of the class type Address.
Figure 3 shows how the same result can be achieved through automatic properties with less code than
Figure 2. The Customer class in Figure 3 uses automatic properties to create the class’ properties
without writing all of the code to declare a field and its property getter and setter.
Object Initializers
It is often helpful to have a constructor that accepts the key information that can be used to initialize
an object. Many code refactoring tools help create constructors like this with .NET 2. However
another new feature coming with .NET 3.5, C# 3 and VB 9 is object initialization. Object Initializers
allow you to pass in named values for each of the public properties that will then be used to initialize
the object.
NET 3.5 Language Enhancements 49
For example, initializing an instance of the Customer class could be accomplished using the following
code:
However, by taking advantage of Object Initializers an instance of the Customer class can be created
using the following syntax:
The syntax is to wrap the named parameters and their values with curly braces. Object Initializers
allow you to pass in any named public property to the constructor of the class. This is a great feature
as it removes the need to create multiple overloaded constructors using different parameter lists to
achieve the same goal. While you can currently create your own constructors, Object initializers are
nice because you do not have to create multiple overloaded constructors to handle the various
combinations of how you might want to initialize the object. To make matters easier, when typing the
named parameters the intellisense feature of the IDE will display a list of the named parameters for
you. You do not have to pass all of the parameters in and in fact, you can even use a nested object
initialize for the BusinessAddress parameter, as shown below.
Collection Initializers
Initializing collections have always been a bother to me. I never enjoy having to create the collection
first and then add the items one by one to the collection in separate statements. (What can I say, I like
tidy code.) Like Object Initializers, the new Collection Initializers allow you to create a collection and
initialize it with a series of objects in a single statement. The following statement demonstrates how
the syntax is very similar to that of the Object Initializers. Initializing a List<Customer> is
accomplished by passing the instances of the Customer objects wrapped inside of curly braces.
Collection Initializers can also be combined with Object Initializers. The result is a slick piece of code
that initializes both the objects and the collection in a single statement.
The List<Customer> and its 3 Customers from this example could also be written without Object
Initializers nor Collection Initializers, in several lines of code. The syntax for that could look
something like this without using these new features:
Extension Methods
Have you ever looked through the list of intellisense for an object hoping to find a method that
handles your specific need only to find that it did not exist? One way you can handle this is to use a
new feature called Extension Methods. Extension methods are a new feature that allows you to
enhance an existing class by adding a new method to it without modifying the actual code for the
class. This is especially useful when using LINQ because several extension methods are available in
writing LINQ query expressions.
For example, imagine that you want to cube a number. You might have the length of one side of a
cube and you want to know its volume. Since all the sides are the same length, it would be nice to
simply have a method that calculates the cube of an integer. You might start by looking at the
System.Int32 class to see if it exposes a Cube method, only to find that it does not. One solution for
this is to create an extension method for the int class that calculates the Cube of an integer.
Extension Methods must be created in a static class and the Extension Method itself must be defined
as static. The syntax is pretty straightforward and familiar, except for the this keyword that is passed as
the first parameter to the Extension Method. Notice in the code below that I create a static method
named Cube that accepts a single parameter. In a static method, preceding the first parameter with
the this keyword creates an extension method that applies to the type of that parameter. So in this
case, I added an Extension Method called Cube to the int type.
When you create an Extension Method, the method sows up in the intellisense in the IDE, as well.
With this new code I can calculate the cube of an integer using the following code sample:
int oneSide = 3;
int theCube = oneSide.Cube(); // Returns 27
As nice as this feature is I do not recommend creating Extension Methods on classes if instead you
can create a method for the class yourself. For example, if you wanted to create a method to operate
on a Customer class to calculate their credit limit, best practices would be to add this method to the
Customer class itself. Creating an Extension method in this case would violate the encapsulation
principle by placing the code for the Customer’s credit limit calculation outside of the Customer class.
NET 3.5 Language Enhancements 51
However, Extension Methods are very useful when you cannot add a method to the class itself, as in
the case of creating a Cube method on the int class. Just because you can use a tool, does not mean
you should use a tool.
Notice that the code above creates a new instance of a class that describes a dog. The dog variable
will now represent the instance of the class and it will expose the Breed, Coat and Ferocity properties.
Using this code I was able to create a structure for my data without having to create a Dog class
explicitly. While I would rarely create a class using this feature to represent a Dog, this feature does
come in handy when used with LINQ.
When you create an Anonymous Type you need to declare a variable to refer to the object. Since you
do not know what type you will be getting (since it is a new and anonymous type), you can declare the
variable with the var keyword. This technique is called using an Implicitly Typed Variable.
When writing a LINQ query expression, you may return various pieces of information. You could
return all of these data bits and create an Anonymous Type to store them. For example, let’s assume
you have a List<Customer> and each Customer has a BusinessAddress property of type Address. In
this situation you want to return the CompanyName and the State where the company is located. One
way to accomplish this using an Anonymous Type is shown in Figure 4.
Pay particular attention to the select clause in the LINQ query expression. The select clause is
creating an instance of an Anonymous Type that will have a Name and a State property. These values
come from 2 different objects, the Customer and the Address. Also notice that the properties can be
52 by John Papa
explicitly renamed (CompanyName is renamed to Name) or they can implicitly take on the name as
happens with the State property. Anonymous Types are very useful when retrieving data with LINQ.
Wrapping Up
There are a lot of new language features coming with .NET 3.5 that both add new functionality and
make the using of existing technologies easier. As we have seen in the past, when new technologies
have been introduced, such as with generics, they often are the precursors to other technologies. The
introduction of Generics allowed us to create strongly typed lists. Now because of those strongly
typed lists of objects we will be able to write LINQ query expressions against the strongly typed
objects and access their properties explicitly even using intellisense. These new features such as
Object Initializers and Anonymous Types are the building blocks of LINQ and other future .NET
technologies.
NET Collection Management with C# 3.0 53
Generics in C#, enable you to define classes, interfaces, delegates or methods with placeholders
for parameterized types used within. This allows you to define classes that use a generic type, and
define the type at the time of instantiation or method calls. This makes your code strongly typed,
but makes maintenance easier. Prasanna describes the improvements in .NET v3.5
This article looks into some of the new features in C# 3.0 and introduces Linq in managing a
collection of objects within a generic List
Introduction
A couple of years ago, I wrote an article entitled “.NET Collection Management” for Simple-Talk. The
purpose of that article was to introduce generics and to show how generics can be used to manage a
collection of strongly typed objects within a generic List using C# 2.0. Generics allow us to write
code without binding the code to a particular type, and at the same time ensures we can use strongly
typed objects. I thought I’d revisit the article to see how much my code would be simplified and
improved in C# 3.0, and introduce some of the new features in C# 3.0.
Let us define an Employee class that we will be using throughout the examples in this article. The
Employee class has the properties Name and Salary.
We have omitted the implementation of the properties because their implementation is very simple.
You set a value to a private field and get the value from a private field. So we are going to let the
compiler implement the properties for us. This is a feature called “Automatic Properties” that saves a
few lines of code and improves the readability of the code when the property implementation is very
simple.
Next we will define and use a collection of Employee objects using a List<T>.
In this code, we have used a special syntax in initializing the property values at the time of creating
Employee objects. This is a feature called “Property Initialization” that provides a very easy way of
initializing one or more properties when creating an object.
Sorting a List
We can use the Comparison delegate and pass it into the Sort method of List<Employee>. In the
following code we will use an anonymous method to pass the instance of a Comparison delegate to
54 by Amirthalingam Prasanna
do the sorting operation. The anonymous method simplifies the call to the Sort method since we do
not need to define a separate method.
We could have written this code in C# 2.0. But in C# 3.0 we can further simplify the implementation
by using Lambda expressions for method implementations. Lambda expression is an inline method
implementation that is translated to an instance of a delegate by the compiler. These expressions use
the syntax “(parameter 1, parameter 2 …) => method implementation”. Lambda expressions allow
us to define methods on the fly with a simpler syntax compared to anonymous methods. So the above
code can be simplified by using the Lambda expression syntax as follows:
By using the Lambda expression, I have omitted defining the type for emp1 and emp2. Since the Sort
method accepts an instance of a Comparison delegate for Employee objects, the compiler is
intelligent enough to understand that emp1 and emp2 has to refer to Employee objects. The
expression “(emp1, emp2) => emp1.Salary.CompareTo(emp2.Salary)” will be translated to an
instance of the Comparison delegate.
Another way of sorting the generic List is by using the static method Enumerable.OrderBy. This
method will return an ordered collection of Employee objects
The OrderBy method is an extension method. An “Extension method” is a new feature in C# 3.0
that allows you to call a static method belonging to a class as if it is an instance method belonging to
an object. This also allows us to extend types which normally we might not be able to extend. So the
OrderBy method can be called as if it is an instance method because it is an extension method. The
compiler would replace it as a call to the static Enumerable.OrderBy extension method:
Searching a List
The generic List has the methods Find or FindAll to search for one or more objects within the List.
Both these methods accept an instance of the Predicate delegate as a parameter. The Predicate
delegate instance can be defined by creating a method or an anonymous method or a lambda
expression.
We can also use the Enumerable.Where extension method to search within the generic List. The
following code segment returns a collection of Employee objects where the Salary property value is
greater than 1000.
There are many operations that are available through extension methods that can be performed on
objects within a List. Most of these operations require looping through the objects within the
collection. But with the use of extension methods, we can perform these operations without the need
to loop through the collection.
For example let us assume we want to retrieve the maximum Salary amount within the collection of
Employee objects within List<Employee>. We can use the Max extension method as show in the
below code:
Similarly we can use many other operations such as Min, Sum, and Count that are available.
List Conversion
Converting a List of one type to a List of another type is very simple. We can still use the Converter
delegate that was available in C# 2.0. Another way of converting the type of a List is to use the
Enumerable.Select extension method.
This method call would return a collection of employee names. However, let‘s assume that we want
to convert the collection of Employee objects into a collection of objects that has the name of the
employee and a boolean value indicating whether the employee has a salary over 1000. We would need
to create a new type as a class or a structure that has a string property and a boolean property. C# 3.0
supports a new feature called “Anonymous Types” that allows us to define types on the fly.
var emps = col.Select((emp) => new { Name = emp.Name, BigSalary = emp.Salary > 1000
});
We’ve defined a new type that has the properties Name and BigSalary. Another thing that you might
have noticed here is the use of the new keyword “var”. This is a new feature called “Type Inference”.
Type inference is used when we do not know the name of the type of the variable and we require the
compiler to help us out in inserting the name of the type. This is used with anonymous types, since
the compiler defines the type anonymously.
Linq
We went through sorting, searching, performing operations and converting a collection of Employee
objects in a generic List. The extension methods OrderBy and Where returns an IEnumerable of the
type that we use within the generic List – in this instance the Employee type. The extension method
Select return an IEnumerable of the type we want to convert the employee objects to. We can
combine these extension methods to search, sort and convert the objects within the generic List
This is where Linq comes in. Linq stands for Language INtegrated Query and provides a SQL like
syntax for accomplishing what we did in the above code.
This code uses Linq to query the collection of Employee objects. The syntax is very similar to SQL
except that the select clause is at the end of the query expression. It makes sense because the process
of converting the objects within the generic List would be the last step. Each expression in the Linq
statement followed by the where, orderby and select keywords are lambda expressions. These lambda
expressions are used to make method calls to the extension methods as shown in the above examples.
Conclusion
This article looks at the capabilities of the new features in C# 3.0 that helps to better handle
collection of objects. It also introduces language integrated query and how it helps in managing
collections.
Exceptionally expensive 57
EXCEPTIONALLY EXPENSIVE
26 March, 2008 1:09
By Brian Donahue
Many years ago, when switching from programming in plain old C to the managed environment of
.NET Framework, I had discovered exceptions. The idea was not completely new to me because I'd
already seen try/catch blocks in JavaScript and I liked that method of error handling a lot, especially
when compared to the ON ERROR GOTO handling that VBScript uses, which is why I prefer using
JScript whenever I can, although sometimes in the scripting environment you begrudgingly have to
use VBS.
.NET had significantly enhanced the try/catch block by allowing the programmer to extend the
exceptions by adding their own properties and methods to them. In addition, the exceptions can be
typed, so if you are interested in one type of exception, say a file can't be opened, but not in another
type of exception, for instance an out-of-memory condition, you can have that sort of granularity.
So I thought, great!, I will use exceptions everywhere. I will use them all over the place, not only to
handle catastrophic and unusual errors, but also anywhere an object could not be created or a value
exceeded a certain threshold or any one of 1001 completely common situations where something
happened that needed some conditional branching to happen. This, as I found out, could have some
particularly nasty performance implications!
Here is a simple example to demonstrate just how much slower throwing exceptions can make your
.NET Program:
using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
namespace ExceptionTest
{
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 1000000; i++)
{
HandleViaException();
HandleViaCondition();
}
}
static void HandleViaException()
{
string s = null;
try{
string ss = s.Substring(0, 2);
}
catch (System.NullReferenceException)
{
}
}
static void HandleViaCondition()
{
string s = null;
if (s != null)
{
string ss = s.Substring(0, 2);
}
}
}
In the HandleViaException method, I attempt to execute a method on a string object set to a null
value, which will cause a NullReferenceException and make the exception handling code run. In the
HandleViaCondition method, I simply check the value of the string, and if it is null, I do not run the
58 By Brian Donahue
Substring method on the string. Although these methods perform the same function, there should be
a noticable performance difference when the methods are both run a million times. I had tested this
using the ANTS Profiler code profiling tool with the following results:
Using Exceptions to trap a null condition is roughly a hundred times slower than simply checking to
see if the string is null. I knew that using exceptions would incur a performance penalty but mama
mia that is slow! I've taken a program that should return in less than a second and turned it into an
excuse to hang out at the water cooler and gossip for awhile.
Could I make this any worse? Oh, yes I can, by attaching a debugger (cdb.exe) to the program as well!
Well, that's not too much worse, but then again, cdb is pretty lightweight. Let's attach to it using
Visual Studio 2005's debugger:
See, now I have a convenient excuse to go down to the cantine and get a donut. Mmmmmm, donuts.
The Visual Studio debugger is particularly invasive when it encounters an exception in the code that
you're debugging. You expect a debugger to pause your code when an exception is encountered, grab
information about the stack and heap, and allow your code to continue on. With CDB.exe, the
overhead is pretty minimal, but Visual Studio 2005 seems to pause my running code for much longer.
The end result is that if this code was part of a real-world application, I would probably spend all day
trying to debug it, and I frankly have better things to do, like eat bacon sandwiches. On toast. With
some of that nice Brown Sauce they have over here.
From now on, I use exceptions in my code very sparingly, and try to avoid using them in code loops
altogether because the cumulative effect of wasting a few milliseconds in a tight code loop can turn
an application into sludge if you're not careful!
Exceptionally expensive 59
60 By Brian Donahue
Need to loosen my bindings 61
EXTENDING MSBUILD
06 December 2007
by Tilman Bregler
Because MSbuild underpins the Visual Studio 'build' process, you can use MSBuild to explore and
extend the build process to suit your needs. If you are finding the job of building Microsoft .NET
applications tedious , the chances are that there is a way that using 'extended MSBuild' for your
automated builds will save you time, effort and code.
MSBuild is the build platform for Microsoft and Visual Studio. Unlike other build systems, for
example NAnt, MSBuild not only provides you with a scripting language for controlling builds, but
also with a default build process that can be extended and altered to make it suit your needs.
The benefits of MSBuild, over other build tools, are time savings, code base reduction, harnessing of
tried and tested Microsoft code (don't laugh), and even one or two unique features (assuming that you
are using .NET, of course!).
In this article I will first explore the default MSBuild process and then show how it can be altered by
modifying and overwriting predefined properties and items, and by inserting your own custom targets.
The overall aim is to promote an understanding of the default build process that will enable you to
discover your own ways of extending MSBuild.
NOTE:
This article will refer to .NET 2.0, but as far as I know everything applies equally to
.NET 3.5. The article requires some prior knowledge of MSBuild. See here for an
overview from the horse's mouth: MSDN: Visual Studio MSBuild Concepts.
msbuild app.csproj
Notice that we didn't specify a target, i.e. when MSBuild runs; it will call the default target of
app.csproj.
Now, let's look at a project file generated by Visual Studio. Below is the project file of a freshly
created C# class library (with some omissions).
Extending MSBuild 63
<Project DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.50727</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{5B9EEF3B-7CF4-4D38-B80D-E07F4B1E3CD0}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ClassLibrary1</RootNamespace>
<AssemblyName>ClassLibrary1</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
…
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
…
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Class1.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>
As you can see, there is not a lot in it. There are some properties which you might recognise from the
Properties pages of your projects. Furthermore, there are two item groups, one called 'Reference',
containing the references, and another called 'Compile', containing the source files.
So where is the action? In particular, where is the default 'Build' target that's called when Visual
Studio builds the project?
The answer is of course this line:
'MSBuildBinPath' is a reserved property and evaluates to the path where MSBuild.exe resides, i.e.
usually something like C:\WINNT\Microsoft.NET\Framework\v2.0.50727. (See here for a list of
reserved properties http://msdn2.microsoft.com/en-us/library/0k6kkbsd(VS.80).aspx )
However, perusing Microsoft.CSharp.targets reveals that there isn't much in there either, appart
from a target called 'CoreCompile' which calls the Csc task. In particular, there is no 'Build' target
here either.
However, at the bottom you'll find another import,
This is where all the action is. Microsoft.Common.targets contains the 'Build' target we were
looking for, and most of the targets, properties and items executed during a build are defined here.
a) Properties, items and targets are evaluated in order from top to bottom. Furthermore, properties,
items and targets that appear later in the build script always overwrite those of the same name that
appear earlier in the build script.
b) Properties and items defined in property and item groups are evaluated statically. What that means
is that all properties and items, no matter where they appear in the build script, are evaluated at the
start, before any targets have executed.
This implies that, most of the time, you will want to make your additions after the default build
process has been declared, that is in your project file below the Microsoft.CSharp.targets import.
This will allow you to use properties and items defined by Microsoft, to overwrite and amend those
properties and items, and to overwrite and extend predefined targets. Moreover, even though you
modify properties and items at the bottom of your build script, these modifications will have been
evaluated by the time the Microsoft targets run. You can thus provide custom values to default
targets. (See the section below, 'Referencing Different Dlls for Release and Debug Builds', for an
example.)
This also means that you must be very careful not to modify or overwrite existing properties, items
and targets when you don't intend to. To avoid conflicts, I suggest always adding a unique pre-fix to
your custom property, item and target names, for example your company name followed by an
underscore.
Other points to note about property evaluation are, that a) property values passed in via the
command line always overwrite property values set in build scripts, and b) all environment variables
are automatically available as properties in your build scripts. For example, '$(PATH)' evaluates to the
value of the PATH environment variable. Properties defined in scripts overwrite properties defined
by environment variables, however.
It is possible to not overwrite earlier properties by using the following idiom:
This will only set the value of Property to Value if property hasn't been assigned a value previously.
Exercise care though, as this does not work for properties for which the empty string is a valid value.
<ItemGroup>
<OutputFile Include="$(OutputPath)\**\*" />
</ItemGroup>
<Target Name="CopyOutputFiles">
<Copy SourceFiles="OutputFile" DestinationFolder="$(DestFolder)" />
</Target>
On the face of it, we are defining an item which contains all output files. We then use this item to
copy the output files to another folder. If you run the script, however, you will find that not a single
file was copied. The reason is that, due to static evaluation, the item got evaluated before any targets
ran. In other words, before any output was generated. The item is thus empty.
Dynamic items get around this problem. The example would then look like this,
<Target Name="CopyOutputFiles">
<CreateItem Include="$(OutputPath)\**\*">
<Output TaskParameter="Include" ItemName="OutputFile"/>
</CreateItem>
<Copy SourceFiles=" @(OutputFile)" DestinationFolder="$(DestFolder)" />
Extending MSBuild 65
</Target>
Now the item is created just before the Copy task is called and after all the output creating targets
have run. Sadly, dynamic items and properties are not very nice, in that they require an excessive
amount of difficult-to-read code.
This will result in all defined properties and items, together with their values, being listed at the top of
the output. From there it's easy to pick the ones you need. Bear in mind, though, that dynamic
properties and items will not be listed. For those it's back to scanning the Microsoft build scripts, I'm
afraid.
Diagnostic verbosity is also useful for debugging your scripts, in general. It gives very detailed output,
as well as the aggregate timings of each task and target.
When running from the command line, I recommend redirecting output to a file, as printing to the
screen can slow down execution considerably. That way you also get the complete output, and you
can search it in a text editor.
<ResolveAssemblyReference
…
SearchPaths="$(AssemblySearchPaths)"
…
</ResolveAssemblyReference>
So, all we need to do is extend the AssemblySearchPaths property. To do so, add the following to the
bottom of your project file,
<PropertyGroup>
66 by Tilman Bregler
<AssemblySearchPaths>
$(Configuration);
$(AssemblySearchPaths)
</AssemblySearchPaths>
</PropertyGroup>
What we're doing here is defining a new property, also called AssemblySearchPaths. Since our definition
occurs below the original definition we overwrite the original definition. The new definition states
that AssemblySearchPaths consists of the value of Configuration followed by whatever the old value of
AssemblySearchPaths was. In effect, we prepend the value of Configuration to the value of the
AssemblySearchPaths property.
Now, when the ResolveAssemblyReference task runs, it will use our new definition of AssemblySearchPaths,
thanks to static evaluation. It will look for referenced dlls in a folder, called the value of the
Configuration property, before it looks anywhere else. In the case where you are building a Debug build
it would look first in a subfolder of the current folder called 'Debug'. Since the current folder is
always the folder in which your 'start-up project' resides, i.e. the project file, we are done.
The changed value of the AssemblySearchPaths property can be verified by looking at the build output
with verbosity set to diagnostic.
The cool thing is that this change takes effect even when building inside Visual Studio. In other
words, when you set the configuration drop down to 'Release' you are referencing a release build and
when you set it to 'Debug' a debug build.
<Target Name="BeforeBuild"/>
As you can see, the above target does nothing. However, it gets called during each build at the
approprieate time, i.e. before the build starts. So, if you now define a target of the same name, your
target will overwrite the one in Microsoft.Common.targets, and your target will be called instead.
There is a list of available targets here,
http://msdn2.microsoft.com/en-us/library/ms366724(VS.80).aspx.
The second method for inserting your own targets into the build process is to modify the
'DependsOn' properties. Most targets in Microsoft.Common.targets have the DependsOnTargets
attribute set to be the value of a property, whose name is of the form 'xxxDependsOn'. Where 'xxx'
is the name of the target. For example, the 'Build' target depends on whatever the value of the
BuildDependsOn property is.
<Target
Name="Build"
DependsOnTargets="$(BuildDependsOn)"/>
To insert your own target, you have to modify the value of the BuildDependsOn property, like so,
<PropertyGroup>
<BuildDependsOn>
CustomTargetBefore;
$(BuildDependsOn);
CustomTargetAfter
</BuildDependsOn>
Extending MSBuild 67
</PropertyGroup>
The outcome of this will be that CustomTargetBefore will run before all previously defined targets in
BuildDependsOn, and CustomTargetAfter will run after all previously defined targets. The advantage
of using the 'DependsOn' properties is, that you don't inadvertently overwrite somebody elses targets,
as is possible when overwriting predefined targets.
Most of the time you will want to use Custom.After.Microsoft.Common.targets, so that you can
overwrite and extend existing properties, items and targets, as you would in your project file.
Coming back to the obfuscation example, you would have to create a project file called
'Custom.After.Microsoft.Common.targets' and put it in %program files%\MSBuild\v2.0. In
that file you would have to define a target and hook it in like so,
<PropertyGroup>
<BuildDepensOn>
$(BuildDependsOn);
Obfuscate
</BuildDepensOn>
</PropertyGroup>
<Target Name="Obfuscate">
...
</Target>
MSBuild Constrictions
Having evangelised, at some length, the wonderful ways in which one can extend MSBuild, I can't
help but mention two serious flaws. The first problem is that Visual Studio solution files are not
MSBuild files. MSBuild can, and does execute solution files, but these files are not in the MSBuild
format and hence cannot be extended in the same way as project files. What actually happens when
68 by Tilman Bregler
MSBuild is called with a solution file, is that it converts the solution file to an MSBuild file in memory
and then runs that file. You can even save that file to disk and then extend it when running command
line builds. You cannot, however, make extensions at the solution level, that take effect when building
inside Visual Studio. Also, when you change your solution file you will have to merge the changes into
any generated 'MSBuild Solution'.
The second problem concerns managed C++ projects. And yes, you guessed it, they are not MSBuild
projects either. They are VCBuild projects. When MSBuild builds a managed C++ project it simply
calls VCBuild and passes it the project. From there, it gets very tricky and labour intensive to integrate
managed C++ projects into your build system. So from a build manager's point of view it's good
advice to steer clear of managed C++.
Conclusion
If you're using Visual Studio, you are already using MSBuild, because Visual Studio calls MSBuild
whenver you click the 'Build' button. The Visual Studio integration of MSBuild allows you to extend
your local builds in lots of wonderful ways. In addition, if you're using .NET, using 'extended
MSBuild' for your automated builds is a good idea, given the savings in time, effort and code. I hope
to have shown how you can explore and extend the default build process to make it suit your needs.
Controls Based Security in a Windows Forms Application 69
NOTE:
In a "real" application, I'd add a middle tier of business objects to represent the user,
role, control and the relationships, but again, to keep this paper stripped down to the
essence, the sample application will have the presentation layer talk directly to the
persistence layer (a practice I generally recommend against! Please see this article for
more on this topic).
The database
The database (for this part of the application) is very straight forward. We'll create five tables, as
shown in the figure 1:
NOTE:
To make this article as accessible as possible, I created the database using SQLExpress,
though I accessed and manipulated the database using the SQL Server Management
Studio from SQL Server 2005.
There are three data tables (Users, Roles and Controls) and two tables that create many-to-many
relationships: UsersToRoles and ControlsToRoles. Users and Roles are more or less self-
Controls Based Security in a Windows Forms Application 71
explanatory. The Controls table represents a control on a form, and ControlsToRoles is the heart of
the control-based security approach; it represents the permissions of a given role for a given control
on a given form, as is explained in detail below.
And NewContract asks for details about the work requested, as shown in figure 3.
Controls Based Security in a Windows Forms Application 73
I've intentionally made these application forms crude and simple to allow us to focus on the control-
based security rather than on the form design. In any case, none of the data retrieved in these forms
will be persisted, and I encourage you to create your own forms that more closely represent your own
business needs.
You will want to bind the list boxes to data sources tied to your data tables. When the user clicks on
AddNew you'll get the name from the text box and create the new record for the database and for
the list box:
ControlSecurityDataSet.RolesRownewRolesRow;
newRolesRow = controlSecurityDataSet.Roles.NewRolesRow();
newRolesRow.RoleName = newName;
this.controlSecurityDataSet.Roles.Rows.Add( newRolesRow );
try
{
this.rolesTableAdapter.Update
( this.controlSecurityDataSet.Roles );
}
catch ( Exceptionex )
{
this.controlSecurityDataSet.Roles.Rows.
Remove( newRolesRow );
MessageBox.Show( "Unable to add role "+ newName
+ ex.Message,
"Unable to add role!", MessageBoxButtons.OK,
MessageBoxIcon.Error );
}
RolesListBox.SelectedIndex = -1;
When you add a user to a role (by clicking on the arrow key), you'll make an entry in the
UsersToRoles table (adding the UserID and RoleID and then updating the TreeView (far right)). To
update the TreeView, you'll retrieve the entries fro the database and iterate through the rows of the
table crating a new parent node each time you come across a new user name (if the "Name" radio
button is selected) or a new RoleName (if the Role radio button is pressed):
currentName = row["Name"].ToString();
UsersInRoles.Nodes.Add( ParentNode );
}
}
Since our focus is on the controls-based security, I won't go into more detail here, though as
mentioned earlier, the complete source is available for you to download and try out.
To accomplish this, you'll want to extend your data source to include the Controls and
ControlsToRoles tables as shown in Figure 6:
76 by Jesse Liberty
Once again you'll populate the Roles list box by dragging the Roles table from the data sources view
onto the list box, thus creating both a BindingSource and a TableAdapter
Populating the Controls permission list box is a bit trickier. To do this you need access to the list of
controls for the form.
The ManagePermissions constructor will take three arguments: a reference to a Form, and two
references to ToolTip objects.). The constructor will stash these away in private member variables.
formToolTip1 = toolTip1;
formToolTip2 = toolTip2;
formToolTip1.Active = false;
formToolTip2.Active = true;
}
Controls Based Security in a Windows Forms Application 77
Note that after it stashes away the references it sets the title to indicate which form (page) it is setting
permissions for. The reason we pass in the two tool tips is to change the tooltips from whatever the
programmer was using the tooltips for to now using the tooltips to indicate the name of every
control on the page. This allows the administrator to identify the exact control to be restricted, as
shown in figure 7:
In Figure 7 you can see that when the permissions dialog is open if the user hovers over any control on the
page for which permissions are being set, the tool tip has been changed to indicate the name of the
control. This corresponds directly to the name listed in the Control Permissions list box (which is
sorted alphabetically).
Setting permissions
The administrator selects (or multi-selects) one or more controls in the Controls Permission list box,
then selects (or multi-selects) one or more roles in the roles list box and then checks either or both of
invisible and disabled. The handler for the Save button loops through each indicated control and
each indicated role and calls a stored procedure to make a record in ControlToRoles (first ensuring
there is a record for that control in Controls). All of this is done under transaction support, as
explained in detail below).
Cleaning up
When the Control Permissions page closes, we reset the original tool tips (which we stashed away).
The nitty gritty
There are a number of juicy technical details, and the best way to see them is to walk through them
step by step.
ManagePermisssions constructor
Control now switches to the constructor of the ManagePermissions form, which was shown,
slightly-excerpted, before. The ommission was that I hid the Dictionary named oldMenuToolTips
that I was forced to create despite the fact that I hold on to ToolTips1. This dictionary is needed
because tool tips work differently for menus than they do for other controls. This is a detail we'll
come to in a bit.
Stepping into the ManagePermissions constructor, the class members are initialized and then the
body of the constructor itself is called:
formToolTip1 = toolTip1;
formToolTip2 = toolTip2;
formToolTip1.Active = false;
formToolTip2.Active = true;
NOTE:
If you are stepping through, you'll find yourself in the designer. Put a breakpoint on
ShowControls in the constructor and hit F5 to jump there, as it is the next interesting
piece to examine.
ShowControls
After setting the title bar ShowControls is called, passing in the controls collection of the form.
Stepping in you see the controls collection defined as a Control.ControlCollection. The trick with
80 by Jesse Liberty
this method is that Controls themselves can contain other controls (e.g., a panel can contain controls)
and so the method must be made recursive:
A bit nastier, menu strips handle their members differently, so if the control is a menu strip, you'll
need to call a different method:
if ( c is MenuStrip)
{
MenuStrip menuStrip = c as MenuStrip;
ShowToolStipItems( menuStrip.Items );
}
if ( mi.DropDownItems.Count > 0 )
{
ShowToolStipItems( mi.DropDownItems );
}
PageControls.Items.Add( mi.Name );
}
}
Notice the last line; it is here that we add the menu items to the list of controls that might be
restricted – that is we treat the menu items just like any other control for purposes of control-based
security.
Returning to the ShowControls method, we are now ready to see if the control is of a type that we
might want to restrict (you are free to expand the list of types). If so, we'll set its second tool tip to its
name and we'll add it to the list box of controls:
formToolTip2.SetToolTip( c, c.Name );
PageControls.Items.Add( c.Name );
rolesBindingSource and a rolesTableAdapter and letting them do the work), we are up to the last
line in the constructor in which we invoke PopulatePermissionTree.
This method is factored out because it is invoked from a number of places (which the debugger is
happy to point out to you, just go to the method, right click and then click on "Find all references."
NOTE:
Here, as in many places in the code, I would normally remove all calls to the database to
a business object (or at least to a helper object with static member methods). Once again,
to focus on the task at hand, and to keep the code straight forward, I've put the data
access code directly into the presentation layer code, whch gives me the willies but does
make for an easier to follow example.
The method begins by retrieving the connection string from the AppSettings.cs file, using the
ConfigurationManager object (the new and preferred way to do so in 2.0 applications). Once this is
obtained, a SQL connection is created and opened:
ConnectionStringSettingsCollectionconnectionStrings =
ConfigurationManager.ConnectionStrings;
stringconnString = connectionStrings[
"ControlBasedSecurity.Properties.Settings.
ControlSecurityConnectionString"].
ToString();
The queryString to obtain the controls we want is hard wired into the code (typically, we'd use a
stored procedure) and the order clause is set by which radio button is chosen by the user. A hack, but
an effective one:
if( ByControlRB.Checked )
{
queryString += " order by ControlID";
}
else
{
queryString += " order by RoleName";
}
In the parallel method, in ManageRoles, I had an if statement to check which radio button was
checked (by user or by role) and set the varaibles subNode and parentNode accordingly:
if( rbName.Checked )
{
subNode = new TreeNode( row["roleName"].ToString() );
if ( currentName != row["Name"].ToString() )
{
parentNode = new TreeNode( row["Name"].ToString() );
currentName = row["Name"].ToString();
UsersInRoles.Nodes.Add( parentNode );
}
}
else
{
subNode = new TreeNode( row["Name"].ToString() );
82 by Jesse Liberty
if ( currentName != row["RoleName"].ToString() )
{
parentNode = new TreeNode( row["RoleName"].ToString() );
currentName = row["RoleName"].ToString();
UsersInRoles.Nodes.Add( parentNode );
}
In this method, I'll use the C# ternary operator to consolidate this code:
stringdataName = ByControlRB.Checked ?
row["ControlID"].ToString() : row["RoleName"].ToString();
NOTE:
You read the ternary operator statement as follows "Is the radio button ControlRB
checked? If so, assign what is in the column ControlID to the string DataName,
otherwise assign what is in the column RoleName to that string.
This avoids duplicating the code in an else statement, and thus makes the code more robust.
NOTE:
The opportunity to factor the two PopulateTree methods (from ManagePermissions and
ManageRoles, into a single helper method is left as an exercise for the reader.
With the query string created, we can retrieve the permissions from the database into a dataset, and
from that extract the DataTable whose Rows collection we'll iterate through to get all the existing
controlsToRows relations.
With the DataTable in hand, the next step is to prep the TreeView by calling BeginUpdate (which
stops it from updating until we call EndUpdate) and by clearing all its existing nodes, so that we can
add all the nodes from the database and not worry about duplication. We then iterate through each
row, creating sub-nodes for each parent, and adding the parents to the TreeView as we find new
parents. The parents are defined as a new control (when sorting by controls) or a new role (when
sorting by roles).
Controls Based Security in a Windows Forms Application 83
PermissionTree.BeginUpdate();
PermissionTree.Nodes.Clear();
TreeNode parentNode = null;
TreeNode subNode = null;
if ( parentNode != null )
{
parentNode.Nodes.Add( subNode );
}
}
PermissionTree.EndUpdate();
We've chosen to have the sub-nodes tell whether the control is invisible or disabled no matter what
the view, allowing for displays as shown in figures 9 and 10:
the control that was selected (InternatioanlRB) and then within that loop iterates through each of
the selected items in the PermissionRoles list box, retrieving the DataRowViews corresponding to
the selected items (remember that the PermissionRoles list box was populated through data
binding).
With these in hand, you are ready to create records in ControlsToRoles which you will do by calling
the stored procedure spInsertNewControlToRole, shown here:
PROCEDUREspInsertNewControlToRole
@RoleID int,
@PageName varchar(50),
@ControlID varchar(50),
@invisible int,
@disabled int
AS
BEGIN
Begin Transaction
commit transaction
return
ErrorHandler:
rollback transaction
return
END
Note first that this stored procedure uses transactions to ensure that either the Control row is added
(if needed) and the ControlsToRoles row is added, or neither is added. Second, the stored procedure
checks whether the table already has an entry for this control/page combination and only attempts to
insert one if it does not already exist.
The ControlsToRoles row does double duty; it manages the relation between a control and a role
and it manages the state for that relationship (is invisible set? Is disabled set?). While this may be in
some ways counterintuitive, it ensures that (1) a role can have only one relationship with any given
control and (2) when you set a control invisible for two roles, and then set it visible for one of the
roles you do not inadvertently set it visible for the other. That is, the relationship between a role and a
control (and the state of that control) is atomic.
The code to call this stored procedure is shown here:
ConnectionStringSettingsCollectionconnectionStrings =
ConfigurationManager.ConnectionStrings;
introwsInserted = cmd.ExecuteNonQuery();
if( rowsInserted < 1 || rowsInserted > 2 )
{
DisplayError(
controlID, roleID,
"Rows inserted = "+
rowsInserted.ToString() );
}
}
catch ( Exception ex )
{
DisplayError( controlID, roleID, ex.Message );
}
}
}
conn.Close();
PopulatePermissionTree();
}
Once the new row(s) is inserted, we call PopulatePermissionTree to repopulate the permission tree
to reflect the change and give positive feedback to the user.
Clean up on exit
When the administrator is finished setting restrictions, the ManagePermissions page is closed. An
event is fired as the page is closed (FormClosing) which we trap, providing us an opportunity to reset
the Tooltips for the form that we were setting permissions for:
private voidManagePermissions_FormClosing(
object sender, FormClosingEventArgse)
{
foreach ( Control c inworkingForm.Controls )
{
if ( c is MenuStrip)
{
MenuStrip ms = c as MenuStrip;
RestoreMenuStripToolTips(ms.Items);
}
}
86 by Jesse Liberty
formToolTip1.Active = true;
formToolTip2.Active = false;
}
The menu item tool tips are restored through the recursive method RestoreMenuStripToolTips:
RestoreMenuStripToolTips recurses down to leaf menu items and then retrieves their value from
the dictionary into which we stashed them in ShowToolStipItems which we called from
ShowControls which was called from the constructor.
Form closing then makes our special ToolTips object inactive and reactivates the normal ToolTips
object and the form is back to normal. The database is fully updated and it is up to the form designer
to check the ControlsToRoles table to ensure that the current user's role does not prohibit displaying
or enabling any given control.
Wrap-up
The goal of this article was not to provide a complete solution, but rather, to demonstrate an
approach that utilizes the ability to find all the controls on the page, to assist in finding the names of
each control at run time by taking over the tool tips, and by storing the permissions in a database.
This is an approach I've used with some success for clients who need control-by-control security in
their applications.
Controls Based Security in a Windows Forms Application 87
Since I've left you with work to do to create a full, working application, let me compensate by saying
that I also leave you with support. Please post any questions you may, or difficulties that you
encounter, in the comments at the end of this article and/or on the support forum to my web site
(clicking on books, and then clicking on "Free Support Forum").
Building Active Directory Wrappers in .NET 89
IADsLargeInteger wrapper
The ADDateTime class in our .NET 2 library wraps the IADsLargeInteger object. Several Active
Directory schema properties including accountExpires, badPasswordTime, lastLogon,
lastLogoff and passwordExpirationDate return IADsLargeInteger values, all of which can be
wrapped using the ADDateTime object.
The wrapper exposes the following three members.
The constructor for the wrapper is straight forward and accepts the IADsLargeInteger value from
the Active Directory repository.
Public ReadOnly Property ADsLargeInteger() As IADsLargeInteger
This read-only property is also fairly straight forward. It exposes the IADsLargeInteger and is used
for getting the value back from the wrapper when saving it to the repository.
Public Property StandardDateTime() As DateTime
This property exposes the ADsLargeInteger value as a standard .NET DateTime object. The
conversions to the underlying IADsLargeInteger are done when the property is invoked (both
getting and setting) ensuring that the latest version of the IADsLargeInteger will be returned when
reading this property, as well as when invoking the read only ADsLargeInteger property.
The code for the StandardDateTime() property is listed below:
Return Me.IADsLargeIntegerToDateTime(Me._ADLI)
End Get
Set(ByVal Value As DateTime)
Me._ADLI = Me.DateTimeToIADsLargeInteger(Value)
End Set
End Property
The private member _ADLI is the underlying IADsLargeInteger value. The two methods that this
property uses are private methods of the ADDateTime class. These methods convert between
IADsLargeInteger values and standard DateTime objects using calls to unmanaged code, and can
be found in the source code region IADsLargeInteger CONVERSION METHODS.
ObjectGuid wrapper
The ADObjectGuid object wraps identifier byte arrays returned from Active Directory. Every
schema object in Active Directory, including users and groups, is uniquely identified using a string of
bytes. This value is returned by the Active Directory schema property objectGuid. Because the
identifier values should not be modified, only read only properties are exposed on this class. The
ADObjectGuid opbject exposes four members. Firstly, the constructor accepts the 128 bit byte array
returned from the Active Directory repository.
Me._bytes = bytes
End Sub
Building Active Directory Wrappers in .NET 91
The read only property, bytes, returns the byte array as it was passed into the constructor.
The read only property, guid, returns the byte array in the form of a Guid.
The read only property, splitOctetString, returns the identifier byte array as an octet string with each
byte displayed as a hexadecimal representation and delimited by a '\' character. This format is required
when using the System.DirectoryServices.DirectorySearcher to search for Active Directory
objects by the objectGUID schema property.
builder = _
New StringBuilder((values.GetUpperBound(0) + 1) * 2)
For iterator = 0 To values.GetUpperBound(0)
builder.Append("\" & values(iterator).ToString("x2"))
Next
Return builder.ToString()
End Get
End Property
ObjectSid wrapper
The ADObjectSid object is used to wrap the value of an Active Directory object's objectSid schema
property. It is very similar to the ADObjectGuid. In fact, they both have the same constructor and
all of the same properties, except ADObjectSid does not have a guid property. That's because the
object's SID byte array is 224 bits instead of 128 and cannot be converted to the Guid data type.
When an Active Directory object is created, it is assigned a SID value by the system. This value can
subsequently be changed by the system but once it changes the system will never again reuse the old
value with a different object. Old values are stored in the object's schema property, sidHistory, which
returns an object array of SID byte arrays.
Like the ADObjectGuid, the splitOctetString property of the ADObjectSid can also be used in
search filters when searching for objects by the object's SID value. This value is also often used when
searching for objects by association as the association often references this value.
UserAccountControl wrapper
The ADUserAccountControl object wraps the value of the Active Directory schema property,
userAccountControl. The value is simply an integer that represents several different common
account control flags. Once you know what the flags are and their values, you only need to perform
bitwise operations on the value to set the flag or see if the flag is set. The following snippet from the
ADUserAccountControl class is the enumeration of the available flags with their values.
92 by Jeff Hewitt
Most of the rest of the class contains public properties, one for each flag above, to set the flag or see
if the flag is set. As an example, below, is the code for the LOCKED_OUT property.
The bitwise operations actually take place in two convenience methods that can be seen used above:
• isFlagSet, which takes an enumUserAccountControlFlag and returns a Boolean value
indicating whether or not the flag is set
• updateFlag which takes an enumUserAccountControlFlag and a Boolean value, true
to set the flag and false to remove it.
Every other flag property of the ADUserAccountControl class is implemented this way as well.
The repositoryPath string will specify the path to your Active Directory repository. The username
string contains the domain username of the user you are going to query. The filter specifies the
"query", if you will, that will be executed to find this user. The ADUsername and ADPassword
strings specify the credentials for the user that will be used when binding to the user entry that you
are searching for in the Active Directory repository.
In the typical domain environment, your domain credentials will give you access to bind to your own
user object entry in the Active Directory repository. In other words, depending on your domain's
security settings, you may not have access to query any other username in the repository but your
own. Therefore, if you run into any problems running the example code, try setting the value of
username to your domain username and the values for ADUsername and ADPassword to your
domain username and password.
NOTE:
It's worth mentioning at this point that although rare, depending on your domain's
security settings, this application may not work at all for your credentials. If you continue
to experience problems after a tweaking the configuration variables a few times, you may
need to contact your system administrator to gain the necessary access.
Once the configuration variables have the correct values, the application can initialize the directory
service objects.
The repositoryRootEntry will be the starting location in the search. In my case, when I instantiated
the repositoryRootEntry, I passed in the path to the root of my domain's Active Directory tree and
the administrator's username and password. The directorySearcher is used in executing searches
against an entry, in our case the repositoryRootEntry using the specified filter.
Next, the application executes the search by invoking the directorySearcher's FindOne method to
return a DirectoryServices.SearchResult object:
The remaining four method calls test the four wrapper methods described earlier in the article. Since
they are all similar, let's look at the testADObjectGuid in detail.
It may look like more code than necessary, but that's because the ControlChars end up bloating the
Console.WriteLine lines. First, the value is retrieved using another method in the module,
getProperty, which takes the user DirectoryEntry and the name of the property to retrieve, in this
case, objectGUID. If getProperty returns nothing then a message is printed. Otherwise, the value
object is loaded into a new ADObjectGuid wrapper object. Finally, the method writes the Guid and
the split octet string representations of the identifier to the console.
The getProperty function is used to retrieve the value because it takes a few lines of code to get just
one value from an entry. That's because, as mentioned earlier, when you request a property from a
DirectoryEntry it returns an array of objects. All of the properties being requested in this module
are only expected to return one value. So, this function extracts that value from the first index of the
array returned. The code for the getProperty function is listed below.
Notice that before the function actually requests the value from the directoryEntry, it first checks to
see if the entry contains the property. Although the schema may support a property on the entry, if
the entry doesn't have a value for that property, it won't exist.
The properties that the entry has values for can be obtained by invoking
DirectoryEntry.Properties.PropertyNames, which is an array of strings representing the property
names of the properties that have values for the specific entry. There are actually several hundred
properties available for many types of DirectoryEntry objects. A list of the properties can be
obtained programmatically including information about which properties are mandatory and which
are optional. However, this being outside of the scope for this article, to find out more information
go to:
http://msdn2.microsoft.com/en-us/library/ms675085.aspx.
DirectoryEntry and a new email address string. Before you uncomment this method, it may be a
good idea to contact your network administrator to ensure that updating Active Directory properties
won't have adverse effects on the other systems running on the network. For example, there may be a
system on your network, like a spam filter or scheduled task that is expecting a certain user's email
address to be a certain value. If this value is changed, this system may not be able to send out a
notification to that user.
First, the method retrieves the user's email address, just as for the previous four tests, by calling the
getProperty function and passing in the user and the property name mail, which returns the user's
email address. Once it has the email address, it writes the current address to the console and then
writes the newEmailAddress to the console. Then the user's email address is updated to the
repository using the following code.
user.Properties("mail")(0) = newEmailAddress
user.CommitChanges()
When using the wrapper classes, the values are saved back to the repository in the same way. Simply
replace the newEmailAddress value with the underlying value of the wrapper object. For example,
updating the accountExpires property of a user would look like this:
user.Properties("accountExpires")(0) = _
ADDateTimeWrapper.ADsLargeInteger
user.CommitChanges()
At this point, an exception may be thrown if the ADUsername and ADPassword do not belong to
a user with sufficient rights to update the entry's properties. Typically, as mentioned earlier, users have
sufficient rights to bind to and read from their own entries but not enough rights to commit changes
to the entry. Depending on your domain security settings you may experience varied results when
invoking this method.
To make sure that the change worked, if you have access to actually view the Active Directory users
and computers on your network, you will see that the email address for the given user has been
updated. If you don't have access, you can run the TestConsole a second time and see what email
address is written to the console before the email address is changed.
James shows how to add a simple WMI provider to a service so that you can monitor it, and make
changes to it, remotely across the network
If you are writing an application, such as a service, that you want to be able to monitor and configure
remotely then you'll want to ensure that your application integrates smoothly with Windows
Management Instrumentation (WMI). WMI is the Microsoft implementation of an industry standard
interface called Web Based Enterprise Management. It allows a user to access, and change,
management information from a wide variety of devices across the enterprise. The classes that
support WMI in the .NET Framework reside in the system.management namespace, within the
framework’s class library.
In this article, we will first create a Windows Service that accepts a TCP connection and echoes back
any text typed into a telnet connection. We will then add some WMI code to our service in order to
enable it to publish information about the number of characters that have been echoed back. In other
words, we will turn our Windows Service into a WMI Provider.
Although this is a fairly simple project, it does highlight the key steps that are required to WMI-enable
your application.
Thread m_engineThread;
We then want to kick it off this new listening thread during service startup:
The code for ThreadMain is fairly simple; it just sets up a TCPListner and accepts connections. It
then prints out any line entered via the TCP connection, until a single "." is received on a line by
itself:
data = null;
Finally, we need to get the service to install itself. To do this, add a project reference to
System.Configuration.Install.dll and then add a new class to your project, called MyInstaller.
This class should derive from Installer and be attributed with the RunInstallerAttribute:
[System.ComponentModel.RunInstaller(true)]
public class MyInstaller : Installer
{
…..
In the constructor of the MyInstaller class, we need the following code to install the service:
Integrating with WMI 99
public MyInstaller()
{
ServiceProcessInstaller procInstaller = new
ServiceProcessInstaller();
ServiceInstaller sInstaller = new ServiceInstaller();
procInstaller.Account = ServiceAccount.LocalSystem;
sInstaller.StartType = ServiceStartMode.Automatic;
sInstaller.ServiceName = "Simple-Talk Test Service";
Installers.Add(sInstaller);
Installers.Add(procInstaller);
}
All this does is to ensure that the service installs correctly and appears in the services.msc control
panel.
[InstrumentationClass(InstrumentationType.Instance)]
public class EchoInfoClass
{
public int CharsEchoed;
}
The InstrumentationClass attribute specifies that the class provides WMI data; this WMI Data
can either be an instance of a class, or a class used during a WMI event notification. In this case, we
want to provide an instance of a class. Next, in order to WMI-enable our project, we need to modify
the installer class we wrote earlier so that it registers our WMI object with the underlying WMI
framework.
For safety, first run InstallUtil.exe /u against the binary we built before to uninstall the service.
Now, we can change the installer class so that it registers our WMI object correctly with the
underlying WMI subsystem. Luckily, the .NET Framework architects made this easy for us. There is a
class called DefaultManagementProjectInstaller in the framework that provides the default
100 by James Moore
installation code to register classes attributed with InstrumentationClass. To take advantage of this
we simply change the class MyInstaller to derive from DefaultManagementProjectInstaller rather
than Installer.
[System.ComponentModel.RunInstaller(true)]
public class MyInstaller :
DefaultManagementProjectInstaller
{
…
We need to create and register an instance of this service class on service startup. To do this, first add
a member variable to the service class:
EchoInfoClass m_informationClass;
This creates the class instance and registers it with the WMI framework so that it is accessible via
WMI. Once that is done we just use the class as normal.
We have now told the WMI Framework about our class (via the installer) and published an instance
of it (in our OnStart method). Now, we just need to update the information we are publishing via
WMI. To do this we increment the m_informationClass.CharsEchoed field whenever we echo
a character back to the client. To do this add the following line to ThreadMain:
byte[] msg =
System.Text.Encoding.ASCII.GetBytes(data);
stream.Write(msg, 0, msg.Length);
stream.WriteByte((byte)'\r');
stream.WriteByte((byte)'\n');
m_informationClass.CharsEchoed += msg.Length;
}
C:\Simple-Talk>InstallUtil.exe TestService.exe
Integrating with WMI 101
The telnet command opens up a blank screen, waiting for you to type something in. I typed in
"simple-talk" and hit enter and the service duly echo'd back "simple-talk" to the screen.
So, the service returned 11 characters and, hopefully, our WMI provider worked correctly and
recorded that. Microsoft provides a WMI information browser called wbemtest – it's fairly ropey,
but it will do for now so open that up:
C:\Simple-Talk>wbemtest
Click connect, leave all the settings at their default value, and click OK:
102 by James Moore
WQL returns instances of the classes we requested, rather than rows, and presents us with the
following screen:
NOTE:
WQL is very similar to SQL – in fact it is a subset of SQL and allows you to query
management information in a very similar way to an RDBMS. WQL generally returns
“instances” rather than rows. However, these can be thought of as analogous.
Integrating with WMI 103
The CharsEchoed property shows us that 11 characters have been sent back from the service.
Summary
WMI is a wide ranging infrastructure on Windows (and other platforms) for managing machines and
programs across an enterprise. Although our example is fairly simple it should give you enough
information to be able to include WMI integration next time you are writing a service or website that
requires remote monitoring and management.
It is equally as easy to consume WMI information from .NET however that topic can wait for
another article.
Make sure your .NET applications perform 105
James Moore is a developer and runs the .NET Tools division at Red Gate Software. Previously,
he developed the UI for SQL Compare 5 and SQL Data Compare 5, and was technical lead on
SQL Backup 5.
In today's world of high processor speeds, and vast amounts of available memory, many
programmers seem to ignore the limitations on these resources in applications.
This can often lead to scalability and performance problems that can often be solved with very little
difficulty. This article discusses common pitfalls and problems when creating applications in .NET. It
will cover some nomenclature first and then present several common issues that I have come across
in a wide variety of applications. It also describes some common ways of solving these problems.
Future articles will delve deeper into some of the specific techniques or problems covered here
A big Oh
It took me a long while to fully grasp the big-O notation (also known as Big-Oh, Landau notation or
asymptotic notation), and how it could be used to understand the scalability of your code. It's well
worth your time in investigating it.
Basically the big-O notation is used to describe how an algorithm uses resources (time and memory)
depending on the size of its input. So, what do I mean if I say that a function has a time complexity
of O(n2)? I mean that the amount of time spent in the function grows roughly in proportion to the
square of the size of the input. When n is small, this is not necessarily significant, but this changes as
n grows.
Consider the following function:
6: {
7: for (int j = 0; j < array.Length; j++)
8: {
9: Thread.Sleep(10);
10: }
11: }
12: }
This function has a time complexity of O(n2). If we pass in an array of 1 element, then the function
will spend longest in the sleep on line 3 of our function. The same is true if we pass a 100 element
array. However, if we pass in a 1000 element array, the same amount of time is spent in line 9 as line
3.
As the number of elements in the array grows beyond 1000, the sleep on line 9 becomes more and
more dominant and the sleep on line 3 loses its influence on the run time of the function.
Below is a table outlining several common function complexities, illustrating how they grow with the
size of the input:
>O(1) O(lg N) >O(n) O(n lg n) O(n2) O(n3)
1 1 1 1 1 1 1
10 1 3 10 33 100 1000
50 1 6 50 282 2500 125000
100 1 7 100 664 10000 1000000
1000 1 10 1000 9966 1000000 100000000
0
10000 1 13 10000 132877 100000000 1012
1000000 1 20 1000000 19931569 1012 1018
We have been talking about time complexities so far but it is also important to remember the same
can be true of memory usage as well.
You will often find that to decrease a function's time-complexity, you need to increase its memory
complexity. Can you come up with a function that has an O(1) time complexity (i.e. the function's
execution time does not vary however large the number given) and a different function with an O(1)
memory complexity, to tell if a number under 10000 is prime or not? What are the respective memory
and time complexities of both of these functions?
Get real
The problem is, back in the real world, that my function is 5000 lines of code, calls other functions
and has 20 parameters; there is no way I can figure out the time/memory complexity of my function!
Fortunately, you don’t need to figure it out by hand. The way that I do it is to use a profiler and run
the application with one set of inputs, and rank the functions in order of their execution times.
ANTS Profiler is my personal favourite as it supports line level timings. I then repeat the process,
varying the size of the inputs.
Next, I compare the two lists and look at the differences in function rank. Functions that have moved
up relative to the others are the ones that are likely to have scalability problems, and can be
investigated further. A note of caution about this: although you do not need to go to the extremes of
input size differences you do need to make sure they are large enough.
Think about the function introduced above; if I started with a 10 element array input and only tested
up to a 100 element array, then I would conclude that the sleep on line 3 is dominant. However, if I
pass in a 10 element array and a 10,000 element array then I will see the function’s rank change.
Loops
The most obvious source of poor performance is loops, whereby the number of iterations around
the loop cannot be determined at compile time. These loops generally scale as a function of some
piece of input.
Watch out for places where you call a function in a loop, and where that function also has a
complexity of O(n) or above, as you can then end up with a function with a complexity of O(n2)
without realising it. This is probably the main cause of poor scalability in .NET applications.
How you fix the problem really depends on your application, but I always find that it's useful to think
about the following "rules" when trying to solve performance issues related to loops.
Do less work
This seems fairly obvious, but I always ask myself if there is a way to move some of the processing
out of the function, and pre-compute or cache some of the information I am computing. This often
comes at the expense of using some extra memory, so be careful!
Modelling
Can you model your problem in a similar way to another problem? Does your problem resemble a
sort or is it more like a flow optimization? If so, then find an algorithm which performs well and
adapt it to solve your problem. If you have never looked at it then I would suggest getting a copy of
"Introduction to Algorithms" by Cormen, Leiserson, Rivest and Stein. It sits on my desk and is well
thumbed. I find it much easier to follow than Knuth.
Dynamic programming
You should consider using dynamic programming when your problem can be split into multiple sub-
problems; however, these sub-problems are not independent. In other words, each sub- problem
shares a sub-sub-problem with other sub problems, as illustrated below:
A divide and conquer approach will do more work than is necessary in these cases and can be
optimised by storing the solutions to these sub-sub problems in a lookup table, computing them the
first time they are used, and then reusing the result next time rather than computing it again.
108 by James Moore
Blocking functions
If your application is interactive, as is the case with a website or windows forms application, then be
very careful when calling blocking functions, for example when making a web service request, or
executing a query. If the web service runs on a remote machine, or the query has not been properly
tuned, then never run these on your main thread. Use the ThreadPool class or BackgroundWorker
class and show a progress indicator using the main thread.
Database queries are one of the most common causes of poor performance. They behave in a similar
way to function calls and their performance often depends on the number of records in the tables
accessed. Check that your queries scale properly by using a SQL Profiler and varying the number of
records in the tables. Also, examine your query execution plans and make sure that the indexes you
have on your tables are relevant and that your database’s statistics are up to date, so that the correct
indexes are used. Optimizing queries can be tricky and the exact optimisations to use differ from
query to query. You are generally best-advised of to talk to an expert. Luckily there are plenty willing
to give their advice for free at sites such as SQLServerCentral.com.
In the past, rewriting a query and ensuring that the correct indexes are available has allowed me to
decrease a query's execution time form over 30 minutes to two or three seconds. It’s well worth
spending a few hours testing the scalability of your queries.
Batch it up
The first solution is to look at how your program accesses the data structure in question. Can you
batch up several accesses to the structure and only acquire the lock once? If your process is:
1. Acquire the lock
2. Do something
Make sure your .NET applications perform 109
Lock free
If your levels of lock contention are high, but you very rarely get update clashes, then you might want
to look into using a lock-free data structure. These are great in some situations, but if you get a large
number of update conflicts you will see a large drop in performance while using these.
NOTE:
A lock-free data structure is one that is built to support concurrent access and writing to
data without a lock being acquired. This can be achieved through transactional memory
or through atomic actions such as Compare and Swap.
Conclusion
Improving the performance of your application often boils down to rephrasing the problem you are
trying to solve. There are two approaches to this – do less, or do it differently. Doing less generally
involves storing and caching information, and doing something differently involves rephrasing the
computation in terms of some other problem. Take for example the Radix sort whereby, given a
list of numbers, say 12, 15, 24, 2, 108, 1067, we sort by the least significant digit first, resulting in: 12,
2, 24, 15, 1067, 108, we then sort by the tens column: 2, 108, 12, 15, 24, 1067, the hundreds column:
2, 12, 15, 24, 1067, 108 and finally the thousands column: 2, 12, 15, 24, 108, 1067. This odd sorting
algorithm seems fairly counter intuitive but, when sorting using a most-significant-digit-based radix
sort, each sub-problem is independent from other sub-problems, allowing for effective use in highly
parallel systems.
This article is admittedly lacking in the hard and fast rules you can apply to your code – unfortunately,
performance improvements are often trade offs. There is the old saying that you don’t get something
for nothing and this is equally true for computer science as for any other area of life. The choices you
make when developing you applications will affect your user’s perception of the application’s quality
so it is definitely worth spending a little time ensuring you get the quick performance and scalability
wins – of which there are often many.
Tracing memory leaks in .NET applications with ANTS Profiler 111
Mike Bloise is a lead developer at Recognin Technologies. This case study recounts his
experiences on a recent CRM project, built using C#, where he found himself facing severe
memory leak issues and a very tight deadline.
Memory leaks are relatively easy to introduce into your programs when coding in C or C++ (no-one
could have enjoyed having to write destructors for every single one of their C++ classes). However,
when you code in a .NET language, such as C#, you are working in managed code, with automatic
garbage collection, so memory management becomes a bit of a non-issue, right?
That certainly pretty much describes my mindset when I developed the brand new C# 2005 desktop
version of my company's sales and CRM application. I was new to C#, and while I was aware that
there could still be problems if references weren't cleaned up properly, I hadn't given memory
management much thought during development. I certainly wasn't expecting it to be a major issue. As
it turns out, I was wrong.
The problems begin…
I knew I was in trouble the first time my customer called with specific memory use numbers from the
Windows Task Manager. Jack is a salesperson by trade, but by volunteering to beta-test my new
desktop application, he had unknowingly put himself in line for a crash course in memory leak
awareness. "The memory is up to five hundred thousand K since restarting this morning", he said,
"What should I do?"
It was a Friday afternoon and I was at an out of town wedding for the weekend. Jack had noticed this
issue the day before and I had advised the temporary fix of close-out-and-come-back-in. Like all
good beta testers he was happy to accept the temporary solution, but like all excellent beta testers he
wasn't going to accept having to do a temporary fix over and over.
Jack wasn't even the heaviest user of the application and I knew that the installed memory of his
machine was above average, so going live wouldn't be possible until I could trace and fix this leak.
The problem was growing by the minute: the scheduled go-live date was Monday and I'd been on the
road, so I hadn't been able to look through the code since the memory issue had arisen.
Fighting memory leaks armed only with Task Manager and Google
I got back home on Sunday evening and scoured the search engines, trying to learn the basics of C#
memory management. My company's application was massive, though, and all I had was the Task
Manager to tell me how much memory it was using at any given time.
Displaying an invoice seemed to be part of the problem; this was a process that involved a large
number of different elements: one tab page, a usercontrol on the page, and about one hundred other
controls within that usercontrol, including a complicated grid control derived from the .Net ListView
that appeared on just about every screen in the application. Every time an invoice was displayed, the
memory use would jump, but closing the tab wouldn't release the memory. I set up a test process to
show and close 100 invoices in series and measure the average memory change. Oh no. It was losing
at least 300k on each one.
By this point it was about 8pm on Sunday evening and needless to say, I was beginning to sweat. We
HAD to go live the next day. We were already at the tail end of our original time estimate, other
projects were building up, and the customer was already starting to question the wisdom of the entire
re-design process. I was learning a lot about C#'s memory management, but nothing I did seemed to
keep my application from continuing to balloon out of control.
112 by Mike Bloise
Enter ANTS
At this point, I noticed a banner ad for ANTS Profiler, a memory profiler for .NET. I downloaded and
installed the free trial, mentally composing the apologetic 'please give me a few more days' email I
would need to write the next morning if I didn't find a resolution.
How ANTS worked was pretty clear as soon as I opened it. All it needed was the path to the . exe,
after which it launched the system with memory monitoring turned on. I ran through the login
process in my application, and then used the main feature in ANTS to take a 'snapshot' of the
application's memory profile before any invoices or other screens had been shown.
Browsing that first profile snapshot, I was stunned at the amount of information available. I had been
trying to pinpoint the problem using a single memory use number from the Task Manager, whereas
now I had an instance-by-instance list of every live object my program was using. ANTS allowed me
to sort the items by namespace (the .NET ones as well as my own), by class, by total memory use, by
instance count, and anything else I could possibly want to know.
Armed with this information, I mentally put my apology email on hold, brought up my application,
ran the process that displayed 100 invoices, and then took another snapshot. Back in ANTS, I
checked the list for instances of the main invoice display usercontrol. There they were; 100 instances
of the control along with 100 instances of the tab and 100 instances of everything else, even though
the tabs themselves had all been closed on the screen.
Tracing memory leaks in .NET applications with ANTS Profiler 113
This was really puzzling to me, and I wrote to Red Gate Software, the developers of the ANTS
system, asking for some additional help. Their support staff promptly responded and explained that
in situations with lots of complex references and event handlers, the .NET runtime can leave event
handlers in place when they should be disposed. They suggested manually removing each handler in
the Dispose method of the usercontrol that was causing the problem.
I added the 20 or so minus-equals statements to remove each handler, and for good measure, I added
a System.GC.Collect()statement after the tab closing process.
Re-running the ANTS profiler and the 100 invoice process, I found that the memory use remained
rock solid. Then, when re-checking the ANTS snapshot, I could see that all of the invoice-related
controls had been released, and the memory use numbers in the task manager never moved.
I re-compiled and uploaded the new version. Now it was my turn to call Jack.
Tracing memory leaks in .NET applications with ANTS Profiler 115
Summary
What did I learn from all this? Firstly, that the "it's managed code so we don't have to worry about
memory leaks" assumption falls short of the mark.
Although automatic memory management in .NET makes our lives as .NET developers a whole lot
easier, it is still easy to introduce memory leaks into your application. Even in managed memory, there
can be issues. The memory manager cannot free memory that is still 'live' – and it will still be
considered live if it is referenced directly or indirectly through the "spider's web" of references that
link the various objects. Also, when complex reference trees and event handlers are involved, the
memory manager doesn't always deregister these event handlers, and so the memory will never be
released unless you forcibly release it.
Secondly, I learned that tracking down these sorts of issues equipped only with Task Manager was
simply not possible – certainly not in the timeframe I had. Tools such as Task Manager (and
Performance Monitor) were able to tell me that my application was using up a lot of memory, but I
needed a dedicated memory profiler like ANTS Profiler to really show me what objects made up that
memory and why they were still there.
Testing Times Ahead: Extending NUnit 117
If you want to get serious with Unit Testing, then you'll need to understand how to extend the
NUnit framework in different ways for your own particular test requirements and how to tailor test
behaviour. Test Expert Ben Hall, of the SQL Data Generator team, shows how it is done, with
special reference to the art of testing SQL code.
This article discusses why you might want to extend NUnit with custom addins and attributes, and
how to go about doing it. NUnit has become the de-facto standard unit-testing framework for the
.Net platform. Originally a port of JUnit, it has now grown, and has been completely re-written to
take advantage of the .Net framework. This article is based on NUnit 2.4.6.
The sample source code for this article can be downloaded from http://www.simple-talk.-
com/content/file.ashx?file=777.
Why Extend?
Why would you need to extend NUnit? NUnit provides an excellent harness for unit testing, but you
will have requirements which cannot be solved using the framework.
By extending NUnit, you can move this generic repeatable code from your test code into an attribute
that can then be added to your test methods.
This has a number of advantages:
• Your tests are more readable because an attribute can describe more about the test than
some code in a setup method,
• You can reuse your extensions instead of them being isolated in test code.
• You can also extend the code to allow additional possibilities not possible out of the box,
such as dynamically creating your tests and test data.
Where Suite builders work by redefining the TestFixture, Test Case Builders redefine how
the Test attribute behaves. By using the Test Case Builder to hook into NUnit, you can
define a custom test within a standard NUnitTestFixture.
These custom tests work by dynamically creating the test objects and adding them to an
existing Test Fixture to execute. This allows you to dynamically build the tests to execute
within your test fixture based on data that might not be known until run time, for example
the contents of a directory or database.
3) Test Decorators
Test decorators are combined with an existing [Test] attribute in order to add additional
behaviour to a test when it is executed, such as modifying the description of the test or
executing a command before the test is started. Unlike the two previous tests, we still use
NUnit’s basic features but tweak the actual execution.
An example of this is being able to add a RepeatAttribute to a test which causes the test to
execute a certain number of times instead of just once.
4) Event Listeners
The fourth main way to extend NUnit is to listen to the various events Nunit fires during
execution of the test suite. The events you can hook into are:
• Run Started
• Run Finished
• Test Started
• Test Finished
• Suite Started
• Suite Finished
• Unhandled Exception
• Test Output
By listening to these events, you can add a response as required. A common requirement for this
would be to provide additional/different reporting functionality of test execution.
NOTE:
Version 2.4.6 has a known bug with event listeners that cause the loading of the addin to
fail.
Hello World
To start with, I am going to create a Hello World attribute. NUnit addins can be created as any .Net
assembly as long as they reference the correct NUnit assemblies and implement the interfaces
correctly.
To get started, you will need to create a class library (I will be using C#), you will then need to
reference the correct NUnit assemblies, which can be found in the nunit directory, which are
nunit.framework.dll, nunit.core.dll and nunit.core.interfaces.dll. With those in place, we can
create our first addin.
The type of extension point and addin you wish to create will depend on how the addin gets
implemented. For this example, I will create a Test Case Builder addin that will take a test object and
add a description before passing it back to NUnit to execute. The Test Case Builder requires an
object to implement the NUnit.Core.Extensibility.ITestCaseBuilder interface.
Testing Times Ahead: Extending NUnit 119
The interface has two method, one called CanBuildFrom which returns true if the current test
method is supported by the attribute and false if it isn’t. The second method is called BuildFrom
that takes the current method as a MethodInfo object and returns a constructed Test object to
execute. Figure 1 demonstrates the code required to implement the addin. The CanBuildFrom just
checks to make sure it has the correct attribute (see below), the BuildFrom method then simply
creates the built-in NUnitTestMethod object based on the current method, then sets the description
property and finally returns it to the calling code (NUnit).
The next stage is to create the correct attribute so it can be used by the test code. The first time I
attempted this, I put the attributes into the same assembly as the addins. However, this meant that
my test code also had to reference nunit.core and nunit.core.interfaces if it wanted to use my
attribute: this is not great for ‘zero fiction’ usage, so I moved my attribute code into a separate
assembly that my test code could then reference in order to use my addin. This had the benefit that
they don’t need to reference any additional assemblies.
As the attribute is used to tell NUnit to use the addin instead of NUnit’s Test attribute, the attribute
itself is simple as shown in figure 1.
namespace NUnitAddInAttributes
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited =
false)]
public class HelloWorldAttribute : Attribute
{
}
}
Now we have a useable addin, we need to tell NUnit about it. The first step is to add a
NUnitAddinAttribute to the addin class so that NUnit can find it.
Figure 3: - NUnitAddinAttribute
The next step is to install it into the correct extension point. The IAddin interface has a single
method called Install; this has a IExtensionHost parameter that we can use to gain access to the
different extension points. By calling the method GetExtensionPoint and passing in the name of
the extension we get a IExtensionPoint object. We can then call the Install method on this object,
120 by Ben Hall
passing in the object that implements the correct interface, finally returning true to say it completed
successfully.
Figure 4 is the code for installing a Test Case Builder addin.
If we load NUnit and go Tools > Addins the following dialog box will appear.
You should see the addin successfully listed, together with a RepeatedTestDecorator, which is
included within NUnit.
Figure 5 is an example of how to use the attribute with your test. Remember, a Test Case Builder
addin attribute is used instead of the [Test] attribute which you would normally expect, so if we use
the attribute there is no need to redefine it.
[HelloWorld]
public void Test()
{
Assert.IsTrue(true);
}
Within the NUnit GUI, in the properties dialog for the test, we can see that the description has
successfully been set via the addin.
This is a very simple example, but shows how to create the Addin. Now for more detail.
Suite Builders
As discussed above, the suite builder allows you to redefine how the tests are found and executed.
Addins that extend this will require the ISuiteBuilder interface. This is similar to the
ITestCaseBuilder interface, and has two methods – CanBuildFrom and BuildFrom. However,
instead of BuildFrom returning a single test, it returns a complete test suite with all the tests added.
The NUnit object model is slightly confusing where a TestSuite is actually a type of Test, which is
why BuildFrom can return either single tests or a suite of tests.
With BuildFrom returning a TestSuite object, we need to implement the object and add any tests
required. Figure 7 is the code for my custom Test Suite. Within the constructor, I use reflection to
get a list of all the methods within the Type, which is the class containing all of the methods. If the
method name starts with MyTest, I add it to the TestSuite as a standard NUnitTestMethod, at this
point I could also have redefined how each test is executed, I will discuss this in more detail in the
next section.
With our TestSuite code completed, we need to install the addin. To install the addin, we need to
access the extension point called “SuiteBuilders” and pass in the ISuiteBuilder object as shown in
figure 8. We also need to add the NUnitAddinAttribute to the class.
builders.Install(this);
return true;
}
Finally, we need to implement an attribute for use within our tests, this is identify which classes
should use the addin.
Figure 10 is an example of how to use the addin. There is no reference to TestFixture or Test
attributes, as we would expect. All we need to do is add the SuiteBuilderAttribute to the top of the
class, then prefix any tests we want to be executed with ‘MyTest’, which is what the addin uses to
identity the methods.
[SuiteBuilder]
public class SampleSuiteExtensionTests
{
public void MyTest1()
{
Console.WriteLine("Hello from test 1");
}
The NUnit GUI should now show that we have two tests listed for that object which can be executed
as normal.
Testing Times Ahead: Extending NUnit 123
This is a very powerful extension, but with limited usage. Test Case Builders and Test Decorates have
much more of an impact.
When the tests are executed, it will pass in the values (one parameter for each column returned) so we
can use them to test against. This is useful for dynamically creating the tests, especially if you do not
know all the possible test values when you are creating your tests. By using this attribute, anyone can
include additional test cases for the method.
We have already created a Test Case Builder before in the Hello World example so I won’t go into the
concept here. Once we have inherited from the ITestCaseBuilder interface, we need to determine
if tests can built from the method.
The major different is that our attribute now has properties which we can use to pass data into our
attribute. Within our attribute code, we simply add the properties we want.
Figure 13 defines if the test method is supported by the SqlDataSource Addin we are creating. The
first task is to see if the current method has a SqlServerDataSourceAttribute, this is done using the
Reflect class which is a wrapper around System.Reflection and provides an interface that is easy to
understand – this can be found in NUnit.Core. If the method does not have the attribute then it is
124 by Ben Hall
not supported so we return false. Otherwise, we access the attribute using Reflect, and check to make
sure the conversion works correctly and return true to indicate it is a supported method.
return false;
}
Once NUnit knows that it should use the Addin we need to define how it can actually build a test, or
in this case a collection of tests. Figure 14 shows the code for defining how to build the test suite.
The first task is to gain access to the attribute again, enabling us to access the connection string and
query provided by the user. We then need to define a standard TestSuite which is passed to the
CreateRowsFromSQL method along with the method and the attribute (Figure 14), finally returning
the populated suite back to NUnit.
return suite;
}
Within CreateRowsFromSQL we create all of the tests to be executed and add them to the suite. I
am just using standard ADO.net to query the database based on the attribute’s properties. Once I
have queried the database, I iterate each field and use it to help create a test name which allows the
test to be identified when executing. Finally, I create a SqlServerTestMethod object that defines
how the test is executed. To setup the object; I pass in the test method, the name of the test and then
the rows returned from the query as an object array for use during execution. I then add the created
SqlServerTestMethod into the test suite for execution.
while (sdr.Read())
{
try
{
object[] args = new object[fieldCount];
string methodName = "";
for (int i = 0; i < fieldCount; i++) //Create test name
{
object v = sdr.GetValue(i);
if (string.IsNullOrEmpty(methodName))
methodName = v.ToString().Replace(" ", "");
else
methodName = methodName + "," + v.ToString().Replace(" ", "");
If an error occurs during the setup we need to alert the user someone. One approach is use the catch
block create another test, set the running state to be Not Runnable and then set a reason, which is
displayed to the user in the GUI. When the tests are executed, they will have a yellow icon and the
message will appear under the Tests Not Run tab.
If it was more of a serious error that affects the entire test suite we could set the same properties
directly on the test suite.
suite.RunState = RunState.NotRunnable;
suite.IgnoreReason =
string.Format(
"SQL Exception thrown. Check query and connection strings are correctly for
{0}. Exception {1}", suite.TestName, ex.Message);
This code will give us a test suite containing a number of tests based on rows of the query, if our
SQL statement returned 10 rows of data then 10 tests would be created in the test suite. The next
stage is to define how they can be executed. One approach would be to use the standard
NUnitTestMethod, as we did in suite builder. This does not support parameters and so, as that is a
major requirement, we need to create our own TestMethod object.
126 by Ben Hall
The first step is to inherit from NUnitTestMethod, which will give us the fundamental support for
executing the test. We then need to define our own constructor so we can pass in the arguments to
the object.
Within the constructor, I set the TestName property to be the test name we created based on the row.
The next step is to pass in the arguments to the test. To do this, we need to override the
RunTestMethod that is called to execute the test. The first task is to ensure we have arguments, if
not, then we need to pass in null. We thereby gain access to the current TestSuite in order to access
the test fixture. Finally, we use Reflection to invoke the test method within the test fixture, passing in
the arguments as a parameter.
Under the covers, the CLR will set the arguments and do any type conversion required, finally
invoking the test just like NUnit does with the [Test] attribute.
In summary, by using the Test Case Builder extension, a single test method can be reused in several
different ways to meet your own requirement for defining test values.
Test Decorators
A test decorator allows us to make simple modifications to the test without having a great affect on
the way that NUnit processes the test. Test Decorators allow us to add additional attributes to the
standard [Test] that to either define the way the test is executed, set properties on the test such as
description, or run commands before/after the test has been executed. For this extension, I will
create a decorate which can execute a SQL Statement before or after, or before and after the test is
executed – great for creating and deleting test data.
Figure 19 is an example of how the test decorator could be used. The TestSequence enumeration
simply indicates when to execute the query.
[Test]
[ExecuteSql(ExecuteWhen = TestSequence.BeforeAndAfter, Connection = "Data
Source=.;Initial Catalog=Northwind;Integrated Security=True", Script = "DELETE FROM
[Order Details]")]
public void Test()
{
Assert.IsTrue(true);
Testing Times Ahead: Extending NUnit 127
All test decorators must implement the ITestDecorator and install into the TestDecorators
extension point. The ITestDecorator has a single method called Decorate. This has the parameters
of the constructed Test object (built via the Test attribute) and the member info for the test method.
This member info can be used to access all the metadata about the method, such as attributes.
Figure 20 is the Decorate method for the ExecuteSQL extension. The first task is to check that the
test is actually a NUnitTestMethod (if you want to support other Test Case Builders then you will
need to add checking for this) to ensure it is not a test suite. I then get the attributes for the method,
and for each attribute of type ExecuteSqlAttribute I add it to the list. Once I have finished
processing, I then create an ExecuteSqlTestMethod so I can configure how the test is executed , I
also pass in the collection of ExecuteSqlAttributes as a parameter. The purpose of processing all
the attributes as a list is to allow support for multiple execute sql attributes per test method, each of
which could define a different query and test sequence.
attribute.ExecuteWhen == TestSequence.BeforeAndAfter)
ExecuteSql(attribute);
}
base.Run(testResult);
}
}
This decorator can then be used to execute sql commands during the execution of the test itself.
This is great for defining SQL to be executed on a test-by-test basis. If the query execution fails then
the test will fail, but with some correct try...catch blocks you can change this.
[Test]
[ExecuteSql(ExecuteWhen = TestSequence.BeforeAndAfter, Connection = "Data
Source=.;Initial Catalog=Northwind;Integrated Security=True", Script = "DELETE FROM
[Order Details]")]
[ExecuteSql(ExecuteWhen = TestSequence.Before, Connection = "Data Source=.;Initial
Catalog=Northwind;Integrated Security=True", Script = "select 'before'")]
[ExecuteSql(ExecuteWhen = TestSequence.After, Connection = "Data Source=.;Initial
Catalog=Northwind;Integrated Security=True", Script = "select 'after'")]
public void Test()
{
Assert.IsTrue(true);
}
Event Listeners
As mentioned earlier, event listeners are currently broken in 2.4.6; this should be fixed in later
releases.
Testing Times Ahead: Extending NUnit 129
Event Listeners allow you to hook into various events that NUnit fires at various points in the test
suite execution. This is great for providing additional reporting functionality, which is the example I
will do so every event will write information out to the Debug listener.
In order to create an event listener addin, the class needs to inherit from the EventListener object.
Figure 23 is a list of all the methods on the object where you can provide implementation hooks.
Note that all the methods must be included in the addin; the actual method body can be empty if you
do not wish to do anything with that event.
An example of how to implement the code for the different events is shown in figure 24.
Finally, to install the addin we need to use the extension point named “EventListeners”.
listeners.Install(this);
return true;
}
Once the addin has been deployed, it will automatically take affect for any NUnit tests executed.
There is no need to modify the tests.
were compiled against; so, if someone compiled an addin against 2.4.5, then it wouldn’t work in
2.4.6.
To install the addin, you need to copy the assembly into the addin folder in your NUnit folder. For
example, C:\Program Files\NUnit\2.4\addins\, this could vary on different NUnit installations.
You will also need to install the addin on any machines that will execute the test suite, such as build
servers. When NUnit loads, it automatically searches for assemblies in this directory and attempts to
load them.
In terms of executing tests containing your addin, the NUnit GUI and console application will
support your addin correctly. However, support within 3rd party runners depends on the runner.
TestDriven.Net works with all types of NUnit extensions, whereas ReSharper does not support any
type of extensions. This is something to keep in mind when developing and executing.
Once your addin is installed and your tests are executing, you might find that you need to debug into
your assembly to fix problems. The solution is to use the Attach to Process option within Visual
Studio. After you have set breakpoints within your code, I always
1. start the NUnit exe,
2. attach to the NUnit process,
3. load my test assembly using the addin
…at which point your debugger should kick it and allow you to step through your code.
Summary
In summary, NUnit 2.4.6 has a number of different ways of extending the framework for your own
requirements and to tailor test behaviour for different scenarios. The article has demonstrated how to
extend NUnit via the built-in extension points which are Suite Builders, Test Case Builders, Test
Decorators and Event Listeners.
Using SQL Data Generator with your Unit Tests 131
Ben Hall, one of the team who wrote SQL Data Generator, has gone on to develop ways of using
it to provide test data for a wide variety of Unit Testing requirements. Whether you use NUnit,
XUnit, MbUnit or MSTest, you can now lay your hands on as much data as you need.
[Test]
[ExecuteSql(ExecuteWhen = TestSequence.Before, //Taken from Testing Times Ahead:
Extending NUnit
Script = "DELETE FROM [Order Details]; DELETE FROM [Orders]; DELETE FROM
CustomerCustomerDemo; DELETE FROM Customers",
Connection = "Server=(local);Database=Northwind;Integrated
Security=True")]
public void GetAllCustomers_EmptyDatabase_ZeroRows()
{
DataTable dt = DataAccess.GetAllCustomers();
Assert.AreEqual(0, dt.Rows.Count);
}
The test uses the ExecuteSql attribute, which will execute SQL statements against the server
NOTE:
I showed how to create the ExecuteSql attribute in my previous chapter in thie book
Extending NUnit article (Testing Times Ahead: Extending NUnit) so you can get the
code from there.
This is a great example of how using attributes can improve the readability of code: the logic to setup
the test has been moved into an attribute, leaving the actual test code just as test code.
To pass the test, we need to implement our DataAccess class. The DataAccess.GetAllCustomers()
method is shown below and it simply executes a SELECT * to return all the customers in the table.
return dt;
}
The test passes successfully. Next we want to test that data is being returned correctly, this is where
SQL Data Generator can be beneficial.
In this next test, we want to ensure that the DataAccess object does actually return a populated
DataTable with the contents from the database. First, we need to setup our TestFixture class in
order to be able to use the new attributes. All we need to do is add a SDGAttribute to the
TestFixture and inherit from SDGContext.
Using SQL Data Generator with your Unit Tests 133
These attributes are located in the SDG.UnitTesting assembly; the SDGAttribute tells our
extension that the class contains a method which we should be interested in as it should contain test
methods that requires SDG to execute. The SDGContext class allows the extension to hook into the
class and intercept the method calls, if the method has the attribute SDGExecute, see below, then we
know to execute SQL Data Generator.
[TestFixture]
[SDG]
//[SDG("D:\Red Gate\SQL Data Generator\SQLDataGenerator.exe")] //Overload to set
console application location.
public class DataAccessTests : SDGContext
On SDGAttribute, we have an overload where you can specify a different installed location for the
command line application.
With this in place, we can execute SDG as part of our tests. The following test ensures that the
DataTable is populated with 1000 rows and we can be confident that this will always pass because we
are executing SDG before the test. Before the test is executed, the SDGContext intercepts the call to
GetAllCustomers_SDGProject_1000Rows(), as the method has a SDGExecute attribute, the
framework knows to execute SDG. The attribute has an argument for a filename, this is the project
file created using the UI which we want to use. Under the covers, the command line application is
executed using the project name as a parameter.
[Test]
[SDGExecute("Database\\NorthwindCustomers_Orders.sqlgen")]
public void GetAllCustomers_SDGProject_1000Rows()
{
DataTable dt = DataAccess.GetAllCustomers();
Assert.AreEqual(1000, dt.Rows.Count);
}
I find this approach very clean and very effective. We no longer have to worry about what or how the
data is generated.
Another advantage of SDG is that it uses seedable data. The means that the same data will be
produced each time, making it good to test against. The next test below ensures that the
GetCustomerByID() method works correctly; we pass in an ID for a customer generated by SDG
and verify that the data is being returned as we expect.
[Test]
[SDGExecute("Database\\NorthwindCustomers_Orders.sqlgen")]
public void GetCustomerByID_SDGProject_1000Rows()
{
DataTable dt = DataAccess.GetCustomerByID("00040");
Assert.AreEqual("00040", dt.Rows[0]["CustomerID"]);
Assert.AreEqual("Suppebentor Holdings ", dt.Rows[0]["CompanyName"]);
Assert.AreEqual("Bobbi Yates", dt.Rows[0]["ContactName"]);
Assert.AreEqual("Web", dt.Rows[0]["ContactTitle"]);
Assert.AreEqual("564 White Old Freeway", dt.Rows[0]["Address"]);
}
Again, the readability of the test is not harmed by the fact we are generating data, unlike other
techniques such as inserting data manually using ADO.net
By using attributes, we have an easy and flexible way to generate different data for each test, simply by
supplying SDG with a different project file to execute.
However, sometimes we might want to generate data once for the entire fixture.
134 by Ben Hall
[TestFixtureSetUp]
public void TestFixtureSetup()
{
SDGConsoleWrapper sdgConsoleWrapper = new
SDGConsoleWrapper(@"Database\NorthwindEmployees_Orders.sqlgen");
sdgConsoleWrapper.Execute();
}
This runs the data generation before any tests are executed within DataAccessTestsV2. The result is
that all of my tests have the data required, and I only execute the generation once, speeding up the
tests.
NOTE:
During the trail period for SDG you will have a trail screen displayed for each test, as
each test executes the console application. You will need to purchase a license to be able
to execute without this screen.
generate the data to meet these combinations and rules quickly and effectively. We can then export
this data to use as part of our unit and integration tests.
For the next test, we need a method that will verify that an order is valid. A valid order must have an
OrderID, CustomerID and an OrderDate. In normal test cases, we would have to create a series of
valid and invalid Order objects and give them to a ValidateOrder method to see if it worked
correctly for different combinations. For example, we would have a series of tests like this:
[Test]
public void ValidateOrder_ValidOrder_ReturnsTrue()
{
Order o = new Order();
o.CustomerID = 1;
o.OrderID = 1;
o.OrderDate = DateTime.Now.Add(new TimeSpan(0, 1, 0));
Assert.IsTrue(BusinessLayer.ValidateOrder(o));
}
However, after a number of different scenarios this would become difficult to manage and difficult to
understand what is being tested, especially if we have a number of different tests with only slightly
different requirements. Making a change to the code being tested would then break all of our tests,
resulting in increased maintenance of the test suite.
One alternative is to use SQL Data Generator and combine it with some of the advanced testing
frameworks already available. Both MbUnit (http://blog.benhall.me.uk/2007/04/mbunit-
datafixture-data-driven-unit.html) and XUnit (http://blog.benhall.me.uk/2008/01/introduction-to-
xunitnet-extensions.html) can use SQL Tables, CSV and XML files as the source of test data. We can
then use the data SDG produces as the source of the test data for the unit tests allowing for large
amounts of real data to be used in a maintainable fashion.
However, there is still a problem. How do we actually get data into the format we need in order to
use the data for testing. Currently, SDG can only insert data into SQL Server 2000 and 2005
databases. However once the data is in there it doesn’t mean it has to stay there. SQL Server includes
a tool called Bulk Copy Program (BCP) which can export data to a flat file, for example CSV or
XML.
Executing the following command will export data from the table ValidateOrder in the database,
TestData and write the data to the CSV file TestData_ValidateOrder.csv, using a comma as the
delimiter. We can then include this file as an item within our solution and have it copied to the output
directory when we build.
Exporting as XML is a little bit more difficult. Instead of giving a database and table to use as the
source, we provide a query. This query selects all the data, uses the built in FOR XML AUTO clause
to convert the data into XML elements and then wrap the results in a XML root element.
After executing this, all of our data (1000 rows) is created as XML elements within the XML file.
There is no limit to the amount of data we can use. The data produced will look like this:
136 by Ben Hall
We can then use this data together with the MbUnit XmlDataProvider
(http://blog.benhall.me.uk/2007/04/mbunit-datafixture-data-driven-unit.html) feature to introduce a
great deal of flexibility into our unit tests. We can create a series of different xml files with different
data scenarios for tests. We can then store them within a source control to share with the data. As
they are not accessing the database, they will run faster and we don’t have to worry about having
access to the database with the data inserted.
In the following test, we use the DataFixture Attribute with the filename for the xml and an XPath
expression defining how to find the data items (we only have one root so we just use that). We
stipulate that the test should be executed for each Xml Element of type TestData.dbo.ValidateOrder,
passing in the current XML Node for the test.
We can then use the XMLNode to access the data we want to test against.
[DataFixture]
[XmlDataProvider(@"TestData\TestData_Valid_ValidateOrder.xml", "root")]
public class BusinessLayerTests_MbUnit_Valid
{
[ForEachTest("TestData.dbo.ValidateOrder")]
public void x(XmlNode node)
{
Order o = new Order();
//Not nice, but need to get around limitations of Xml and the Xml Serialiser
o.CustomerID = Convert.ToInt32(node.Attributes["CustomerID"] != null ?
node.Attributes["CustomerID"].InnerText : "-1");
o.OrderID = Convert.ToInt32(node.Attributes["OrderID"] != null ?
node.Attributes["OrderID"].InnerText : "-1");
o.OrderDate = Convert.ToDateTime(node.Attributes["OrderDate"] != null ?
node.Attributes["OrderDate"].InnerText : "01/01/1970");
Assert.IsTrue(BusinessLayer.ValidateOrder(o));
}
}
As a result, loading this test will produce 1000 different tests, each using data produced using SDG.
This is excellent for integration and boundary tests as it is so quick and easy to produce huge amounts
of data to test against.
Summary
Hopefully this article has demonstrated some of the possibilities when using SQL Data Generator
with your unit testing. Download the wrapper from Codeplex (http://www.codeplex.com-
/SDGGenerators and experiment to see what kind of difference it makes to your unit tests. Also,
keep in mind that the data produced doesn’t have to be used just with databases, it can be used as any
kind of test data. When you combine this with the more powerful features of the unit testing
frameworks, you can test code faster and more effectively.
Finally, we would love to hear your feedback about the approach taken in this article and SQL Data
Generator in general.
The code for this article can be downladed from here,( http://www.simple-
talk.com/iwritefor/articlefiles/493-RedGate.SDG.Code.zip )
137