So, I was growing a little weary of all of the hype surrounding LINQ. I mean, could it really be that great? Well, I've had a chance to use it in a real world situation and I'm still neutral on it. For me, the best part is the other language features that even make LINQ possible. I'm talking extension methods, lambda expressions, and anonymous types.
I think I'd much rather type:
var specialPeople = myPeopleCollection.Where(p=>p.LastName=="Bush");
than this:
var specialPeople = from p in myPeopleCollection where p.lastName == "Bush" select p;
I just really dig the extension methods that System.Linq brings.
A co-worker and I had a conversation about extension methods today. Somehow this triggered a thought that I needed to see what could be done about the database monotony. Here's the result.
/*
* DbExtensions.cs
* Author: Josh Bush (digitalbush.com)
*/
using System;
using System.Data;
using System.Collections.Generic;
namespace Bush.Data{
public static class DbExtensions{
private delegate T DbAction(IDbCommand cmd);
///
/// Helper method that does the connection and paramter setup.
///
private static T ExecuteDbAction(IDbConnection conn, string commandText, IDbDataParameter[] parameters,DbAction dba){
if (conn.State != ConnectionState.Open)
conn.Open();
using (IDbCommand cmd = conn.CreateCommand()){
cmd.CommandText = commandText;
cmd.Connection = conn;
if (parameters != null){
foreach (IDbDataParameter p in parameters)
cmd.Parameters.Add(p);
}
return dba(cmd);
}
}
///
/// Extension method to execute a query and return the resulting records
///
public static IEnumerable Query(this IDbConnection conn, string commandText, params IDbDataParameter[] parameters){
return ExecuteDbAction>(
conn,
commandText,
parameters,
queryHelper //ugh, can't use yield inside of an anonymous method
);
}
public static IEnumerable Query(this IDbConnection conn, string commandText){
return conn.Query(commandText, null);
}
private static IEnumerable queryHelper(IDbCommand cmd){
using (IDataReader r = cmd.ExecuteReader()){
while (r.Read())
yield return r;
}
}
///
/// Extension method to execute scalar query.
///
public static object QueryValue(this IDbConnection conn, string commandText, params IDbDataParameter[] parameters){
return ExecuteDbAction
I haven't had a chance to really run this code through it's paces, but I did the following test. I used the MySql Connector, but the methods above should work for any database provider as long as it implements the IDbConnection interface.
using (MySqlConnection mConn = new MySqlConnection(ConnectionString)){
foreach(IDataRecord r in mConn.Query("select post_title from posts"))
Console.WriteLine(r["post_title"].ToString());
}
Compare that to the craptacular way of doing it without the extension methods.
using (MySqlConnection mConn = new MySqlConnection(ConnectionString)){
mConn.Open();
using (MySqlCommand mCmd = new MySqlCommand("select post_title from posts", mConn)){
using (MySqlDataReader r = mCmd.ExecuteReader()){
while (r.Read())
Console.WriteLine(r["post_title"].ToString());
}
}
}
I like the improved look. I haven't had a chance to test this with transactions or much of anything else for that matter, so just consider this a proof of concept at the moment.
You can download the extension methods to see for yourself: DbExtensions.cs
I can't wait to start using extension methods in production. I've already thought of a few great uses for this. For a while I've been jealous of other languages and their seamless integration of regular expressions. Regular expressions are executed against strings, and so I feel it's appropriate to have them married together. With .NET 3.5, I can get a little bit closer with the following class:
public static class StringExtensions
{
private static Dictionary cache = new Dictionary();
private static Regex cacheRegex(string r)
{
if (!cache.ContainsKey(r))
cache[r] = new Regex(r, RegexOptions.Compiled);
return cache[r];
}
public static bool IsMatch(this string s, string regex)
{
Regex r = cacheRegex(regex);
return r.IsMatch(s);
}
public static MatchCollection Matches(this string s,string regex){
Regex r = cacheRegex(regex);
return r.Matches(s);
}
public static Match Match(this string s, string regex)
{
Regex r = cacheRegex(regex);
return r.Match(s);
}
public static string[] Split(this string s, string regex)
{
Regex r = cacheRegex(regex);
return r.Split(s);
}
public static string Replace(this string s, string regex,string replacement)
{
Regex r = cacheRegex(regex);
return r.Replace(s, replacement);
}
}
Sure, the caching could actually do something more useful, but the idea is there. Now I can do something like this: