Thursday, October 25, 2007

Javascript Random Text

Following javascript is to create some mock contacts for UI testing purpose:
    function createMockContacts(contactNumber) {
        var contacts = new Array();
        for (var i = 0; i < contactNumber; i++) {
            var contact = new Object();
            contact.Name = randomText(8);
            contact.Company = randomText(16);
            contact.Phone = randomText(9, "phone");
            contact.Email = randomText(9, "email");
            contact.Address = randomText(20);
            contacts.push(contact);
        }
        return contacts;
    }
    function randomText(length, type) {
        if ( type == "phone") 
            var chars = "1234567890";
        else 
            var chars = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
        var text = "";
        for (var i = 0; i < length; i++) {
            text += chars.charAt(Math.floor(Math.random() * chars.length));
        }
        if (type == "email" && text.length > 3)
            text = text.slice(0, 3) + "@" + text.slice(3) + ".com";

        return text;
    }
Return is an array of javascript objects:
 contacts[0].Name = "7k15zlQQ"
 contacts[0].Company = "sTOX2EVFhsBXJFIn"
 contacts[0].Phone = "881414055"
 contacts[0].Email = "3Ic@Adu2N3.com"
 contacts[0].Address = "lN3kSwJCJC3m7gC8zdZy" 
 contacts[1].Name = "IZYkS3gI"
 ...

Thursday, October 11, 2007

.NET Repository Pattern

Domain Driven Design (DDD) attracts quite a lot of attentions in recent years. Repository pattern is one important pattern in DDD. It mediates between the domain and data mapping layers, or a simple delegation of responsibility away from the domain objects.

Consider reading GeoCode from database, we may have following code:
public class GeoCode
{
public double Latitude;
public double Longitude;
public GeoCode(double lat, double lon)
{
Latitude = lat;
Longitude = lon;
}

public GeoCode GetByPostalCode(string postalCode)
{
GeoCode gc = null;
string connectionString = "....";
string sqlQuery = "SELECT Latitude, Longitude FROM GeoCode WHERE PostalCode = @PostalCode";
try
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
SqlCommand DBCommand = new SqlCommand(sqlQuery, connection);
DBCommand.Parameters.AddWithValue("@PostalCode", postalCode);
using (SqlDataReader reader = DBCommand.ExecuteReader())
{
if (reader.HasRows)
{
gc = new GeoCode(Convert.ToDouble(reader["Latitude"]), Convert.ToDouble(reader["Longitude"]));
}
}
}
}
catch (Exception ex)
{
Logger.LogException(ex);
}
return gc;
}
}
Such code works but it's not flexible and it has direct dependency to the backend store. With repository pattern we need to declare an interface and have a separate implementation:
public class GeoCode
{
public double Latitude;
public double Longitude;
public GeoCode(double lat, double lon)
{
Latitude = lat;
Longitude = lon;
}
}

public interface IGeoCodeRepository
{
GeoCode GetByPostalCode(string postalCode);
}

public class SQLServerGeoCodeRepository : IGeoCodeRepository
{
private string ConnectionString;
public SQLServerGeoCodeRepository()
{
ConnectionString = ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString;
}
public SQLServerGeoCodeRepository(string connectionString)
{
ConnectionString = connectionString;
}

public GeoCode GetByPostalCode(string postalCode)
{
GeoCode gc = null;
string sqlQuery = "SELECT Latitude, Longitude FROM GeoCode WHERE PostalCode = @PostalCode";
try
{
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
connection.Open();
SqlCommand DBCommand = new SqlCommand(sqlQuery, connection);
DBCommand.Parameters.AddWithValue("@PostalCode", postalCode);
using (SqlDataReader reader = DBCommand.ExecuteReader())
{
if (reader.HasRows)
{
gc = new GeoCode(Convert.ToDouble(reader["Latitude"]), Convert.ToDouble(reader["Longitude"]));
}
}
}
}
catch (Exception ex)
{
Logger.LogException(ex);
}
return gc;
}
}
A wrapper class in service layer exposes all related functions:
public class GeoCodeService
{
private IGeoCodeRepository _repository;
public GeoCodeService(IGeoCodeRepository repository)
{
_repository = repository;
}

public GeoCode GetByPostalCode(string postalCode)
{
return _repository.GetByPostalCode(postalCode);
}
}

class Program
{
static void Main(string[] args)
{
IGeoCodeRepository repository = new SQLServerGeoCodeRepository();
GeoCodeService service = new GeoCodeService(repository);
GeoCode gc = service.GetByPostalCode("A1A1A1");
}
}
With repository pattern we can easily extend current implementation. For example, if we need to use web service to get the geo code, we can simply add a new implementation without changing any existing code:
public class WebServiceGeoCodeRepository : IGeoCodeRepository
{
private string WebServiceUrl;
public WebServiceGeoCodeRepository()
{
WebServiceUrl = ConfigurationManager.AppSettings["WebServiceUrl"];
}
public WebServiceGeoCodeRepository(string webServiceUrl)
{
WebServiceUrl = webServiceUrl;
}

public GeoCode GetByPostalCode(string postalCode)
{
GeoCode gc = null;
//Get GeoCode by web service
return gc;
}
}

public class Program
{
static void Main(string[] args)
{
IGeoCodeRepository repository = new WebServiceGeoCodeRepository();
GeoCodeService service = new GeoCodeService(repository);
GeoCode gc = service.GetByPostalCode("A1A1A1");
}
}
The other advantage of using repository pattern is the testability. For example we can create a fake data repository:
public class FakeGeoCodeRepository : IGeoCodeRepository
{
public GeoCode GetByPostalCode(string postalCode)
{
GeoCode gc = null;
double fakeCode = 111.111;
if (postalCode.StartsWith("A", StringComparison.InvariantCultureIgnoreCase))
fakeCode = 123.123;
gc = new GeoCode(fakeCode, fakeCode);
return gc;
}
}
The unit test becomes simple:
 [TestMethod()]
public void GetByPostalCodeTest()
{
IGeoCodeRepository repository = new FakeGeoCodeRepository();
GeoCodeService target = new GeoCodeService(repository);
string postalCode = "M1M1M1";
double expected = 111.111;
GeoCode actual;
actual = target.GetByPostalCode(postalCode);
Assert.AreEqual(expected, actual.Latitude);
Assert.AreEqual(expected, actual.Longitude);

postalCode = "A1A1A1";
expected = 123.123;
actual = target.GetByPostalCode(postalCode);
Assert.AreEqual(expected, actual.Latitude);
Assert.AreEqual(expected, actual.Longitude);
}
Related topics:
http://martinfowler.com/eaaCatalog/repository.html
http://martinfowler.com/articles/injection.html