I’ve spent a good deal of the past couple of years pondering ORM’s and data access technologies for the dotnet platform. Needless to say, it can be quite involved. Before you cite Ayende Rahien and tell me that “Data Access is a solved problem”, I ask you to bear with me for a minute.
A common pattern for data access is to create a domain model with classes for the objects your application needs. Here we are going to use POCO’s (Plain Old Csharp objects):
using System;
namespace Domain
{
public class Organization
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string ZipCode { get; set; }
public string City { get; set; }
public string Homepage { get; set; }
public string Phone { get; set; }
}
public class Contact
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public Organization Organization { get; set; }
}
}
A hand-coded data access layer might start out like this if you have no intention of code reuse:
namespace Infrastructure
{
public class OrganizationRepository
{
private const string CONNECTION_STRING_NAME = "Datastore";
public List<Organization> FindAll()
{
var items = new List<Organization>();
string connectionString = ConfigurationManager
.ConnectionStrings[CONNECTION_STRING_NAME].ConnectionString;
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
string query =
"SELECT Id,Name,Address,ZipCode,City,Homepage,Phone" +
" FROM Organizations";
using (var command = new SqlCommand(query, connection))
{
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
items.Add(new Organization {
Id = Convert.ToInt32(reader["Id"]),
Name = Convert.ToString(reader["Name"]),
Address = Convert.ToString(reader["Address"]),
ZipCode = Convert.ToString(reader["ZipCode"]),
City = Convert.ToString(reader["City"]),
Homepage = Convert.ToString(reader["Homepage"]),
Phone = Convert.ToString(reader["Phone"])
});
}
}
}
}
return items;
}
public List<Organization> FindByName(string name)
{
var items = new List<Organization>();
string connectionString = ConfigurationManager
.ConnectionStrings[CONNECTION_STRING_NAME].ConnectionString;
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
string query =
"SELECT Id,Name,Address,ZipCode,City,Homepage,Phone" +
" FROM Organizations" +
" WHERE Name LIKE '%" + name + "%'";
using (var command = new SqlCommand(query, connection))
{
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
items.Add(new Organization {
Id = Convert.ToInt32(reader["Id"]),
Name = Convert.ToString(reader["Name"]),
Address = Convert.ToString(reader["Address"]),
ZipCode = Convert.ToString(reader["ZipCode"]),
City = Convert.ToString(reader["City"]),
Homepage = Convert.ToString(reader["Homepage"]),
Phone = Convert.ToString(reader["Phone"])
});
}
}
}
}
return items;
}
public Organization GetById(int id)
{
string connectionString = ConfigurationManager
.ConnectionStrings[CONNECTION_STRING_NAME].ConnectionString;
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
string query =
"SELECT Id,Name,Address,ZipCode,City,Homepage,Phone" +
" FROM Organizations" +
" WHERE Id=" + Id;
using (var command = new SqlCommand(query, connection))
{
using (var reader = command.ExecuteReader())
{
if (reader.Read())
{
return new Organization {
Id = Convert.ToInt32(reader["Id"]),
Name = Convert.ToString(reader["Name"]),
Address = Convert.ToString(reader["Address"]),
ZipCode = Convert.ToString(reader["ZipCode"]),
City = Convert.ToString(reader["City"]),
Homepage = Convert.ToString(reader["Homepage"]),
Phone = Convert.ToString(reader["Phone"])
};
}
}
}
throw new KeyNotFoundException(query);
}
}
}
}
Using this (admittedly contrived) example, you can begin to understand the amount of time required to write even the simplest applications.
Next, we will look at the first iteration for refactoring this thing into something more manageable.