Thoughts on Software by Andrew Davey
| | Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|
| 29 | 30 | 1 | 2 | 3 | 4 | 5 | | 6 | 7 | 8 | 9 | 10 | 11 | 12 | | 13 | 14 | 15 | 16 | 17 | 18 | 19 | | 20 | 21 | 22 | 23 | 24 | 25 | 26 | | 27 | 28 | 29 | 30 | 31 | 1 | 2 | | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
Search
Navigation
Categories
Blogroll
|

Wednesday, September 05, 2007
Assertions via Linq Expressions
When writing assertions it is annoying to write a string that basically mirrors what the code your testing says. For example:
Debug.Assert(input != null, "input != null");
Similar statements appear when unit testing with tools like NUnit.
With Linq it is now possible to avoid this by using expression trees. The basic idea is to take a boolean assertion function as an expression tree so that we can call ToString() to get the message for the assert.
void Assert<T>(T obj, Expression<Func<T, bool>> test)
{
System.Diagnostics.Debug.Assert(test.Compile().Invoke(obj), test.ToString());
}
This is then called like:
Assert(input, i => i != null);
Given this idea, we can play with the syntax a bit. Using an extension method:
static class Exts
{
public static void MustSatisfy<T>(this T obj, Expression<Func<T, bool>> test)
{
Debug.Assert(test.Compile().Invoke(obj), test.ToString());
}
}
We then have:
input.MustSatisfy(i => i != null)
Another syntax option would be something like:
Assert.That(foo).Satisfies(
i => i > 0,
i => i < 100);
Where we are now passing an array of assertions (using a params arg in the Satisfies method).
Wednesday, September 05, 2007 11:46:23 AM (GMT Standard Time, UTC+00:00)
.net | c# | linq | programming | syntax

Monday, August 27, 2007
Data Access Syntax Using Anonymous Methods
I have so far managed to avoid needing to use any fancy ORM tools. However, this doesn't mean I like writing all the standard ADO.NET code by hand. Using C# 2.0 anonymous methods I am able to write code like the following. (I'm not sure all of this is truly original work; if you have already done this then sweet! I just want to share the ideas with everyone.)
Customer c = With.Database<Customer>(delegate(Database db)
{
return db.ExecuteReader<Customer>(
"select Id, FirstName, Surname from Customer where Id = @id", // The SQL to execute
delegate(IDbCommand cmd) // This anonymous function is called before executing so we can add params.
{
db.AddParameter(cmd, "@id", DbType.Int32, id);
},
delegate(IDataReader reader) // The IDataReader returned from ExecuteReader is passed here.
{
if (reader.Read())
return new Customer(
reader.GetInt32(0),
reader.GetString(1),
reader.GetString(2));
else
return null;
});
});
My Database class manages the creation of a connection and optionally a transaction. It then exposes methods to invoke SQL commands (Reader, Scalar and NonQuery). A transaction can be automatically provided by calling With.DatabaseInTransaction( ... ) instead. This then wraps the inputted action in a try-catch-finally block, commit and rolling back in the usual places.
("With" is a static class that provides convenient access to the Database object)
public static T DatabaseInTransaction<T>(Function<Database, T> function)
{
using (Database db = new Database())
{
db.BeginTransaction();
try
{
T result = function(db);
db.CommitTransaction();
return result;
}
catch
{
db.RollbackTransaction();
throw;
}
}
}
There are generic and non-generic versions of the functions, depending on if we want to return a value.
For example, here is the non-generic ExecuteReader method from Database:
public void ExecuteReader(string sql, Action<IDbCommand> addParameters, Action<IDataReader> action)
{
Debug.Assert(sql != null, "sql cannot be null.");
Debug.Assert(action != null, "action cannot be null.");
using (IDbCommand cmd = CreateCommand(sql))
{
if (addParameters != null)
{
addParameters(cmd);
}
using (IDataReader reader = cmd.ExecuteReader())
{
action(reader);
}
}
}
The IDbCommand is created using the following method. Notice that we also handle assigning the transaction if we're in one.
IDbCommand CreateCommand(string sql)
{
Debug.Assert(_connection != null && _connection.State == ConnectionState.Open);
IDbCommand cmd = _connection.CreateCommand();
cmd.CommandText = sql;
cmd.CommandType = CommandType.Text;
if (_transaction != null)
{
cmd.Transaction = _transaction;
}
return cmd;
}
The Database class implements IDisposable, thus allowing the "using" syntax in the With class methods. In Dispose() I close the connection, if the transaction is still open I rollback first.
I anyone wants the full Database and With classes drop me a line. I'm looking forward to C# 3.0 since the improved type inference and lambda syntax will slim down the amount of keyboard time even more!
Monday, August 27, 2007 9:41:09 AM (GMT Standard Time, UTC+00:00)
.net | c# | data_access | programming | syntax