Thursday, 24 July 2008

Designing an Enumerator for Business Objects

Following is my elegant enumerator design which can be used for enumerating business objects from the database. Designing an enumerator with C#, .Net 3.5 will be much more elegant simply because of LINQ and LINQ to SQL. I will write about it in my future posts. LINQ has made it extremely easy to do such things, and i will prove my point in the future posts. This is also mostly based on Enterprise patterns (Check out the most famous Martin Fowler's book) . I've used a Table Gateway and a normal Iterator pattern.

BusinessObjectEnumerator

 

Product - Business Object

Product.cs

public class Product
{
int id;

public int Id {
get {
return id;
}
set {
id = value;
}
}
string name;

public string Name {
get {
return name;
}
set {
name = value;
}
}
double price;

public double Price {
get {
return price;
}
set {
price = value;
}
}

public override bool Equals(object obj) {
if (null==obj)
return false;
else
{
Product p = obj as Product;
return (p.id == this.id);
}
}

public override int GetHashCode() {
return id.GetHashCode();
}

public override string ToString() {
return name.ToString();
}



 



DBEnumerator.cs



    public abstract class DBEnumerator<T>:IEnumerator,IEnumerable
{
private int position;
protected List<T> list;
protected TableGateway tablegateway;
protected abstract void Read(string sql);
public virtual void Add(T item) {
if (!list.Contains(item))
{
list.Add(item);
}
}

#region IEnumerable Members

public IEnumerator GetEnumerator() {
return (IEnumerator)this;
}

#endregion

#region IEnumerator Members

public object Current {
get {
return (object)list[position];
}
}

public bool MoveNext() {
position++;
return (position < list.Count);
}

public void Reset() {
position = -1;
}

#endregion
}



ProductEnumerator.cs



    public class ProductEnumerator:DBEnumerator<Product>
{
public ProductEnumerator(string sql) {
this.tablegateway = new TableGateway();
this.list = new List<Product>();
Read(sql);
this.Reset();
}

internal TableGateway TableGateway {
get {
return tablegateway;
}
}

protected override void Read(string sql) {
DataTable dt = new DataTable();
dt = tablegateway.ExecuteNonQuery(sql);
foreach (DataRow row in dt.Rows)
{
Product p = new Product();
p.Id = Convert.ToInt32(row["Id"].ToString());
p.Name = row["Name"].ToString();
p.Price = Convert.ToDouble(row["Price"].ToString());
Add(p);
}
}
}



TableGateway.cs



   public class TableGateway
{
private string connstring;
private IDbCommand command;
private Collection<IDataParameter> parameters;

public TableGateway() {
connstring = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\Projects\\C#Projects"+
"\\BusinessObjectEnumerator\\Inventory.mdb;";
parameters = new Collection<IDataParameter>();
}

public void AddParameters(string parameterName, string parameterValue) {
IDataParameter param = command.CreateParameter();
param.ParameterName = parameterName;
param.Value = parameterValue;
}


public string TableName{
get {return "Tablegateway";}
}

public DataTable ExecuteNonQuery(string sql)
{
using (IDbConnection conn = GetConnection())
{
command = conn.CreateCommand();
foreach (IDataParameter param in parameters){
command.Parameters.Add(param);
}

OleDbDataAdapter oledbdataadapter = new OleDbDataAdapter(sql,(OleDbConnection) conn);
DataSet dataset = new DataSet();
try
{
oledbdataadapter.Fill(dataset, this.TableName);
}
catch
{
throw;

}
finally
{
conn.Close();
}
return dataset.Tables[TableName];
}
}

private IDbConnection GetConnection() {
return new OleDbConnection(connstring);
}

}



Putting the ProductEnumerator to test



  static void Main(string[] args) {

ProductEnumerator myproducts = new ProductEnumerator ("select * from products");

Console.WriteLine("Enumerating products..\n");

foreach (Product p in myproducts){
Console.WriteLine("ID: ,"+p.Id+
"Name: ,"+ p.Name+
"Price: ,"+p.Price);
}

Console.WriteLine("\nPress any key to exit..");
Console.ReadKey();
}



Notice, Since we have implemented the IEnumerable and IEnumerator interfaces, we can use the ProductEnumerator in a foreach statement. This will be the cool part of Enumerators. The MSIL generation for the foreach statement would do something like the following:



IEnumerator ienum = myproducts.GetEnumerator();



while(ienum.MoveNext()) {



...



}



Also, We can  provide mechanisms to add stuff into the List<T> of the DBEnumerator. By doing this we will need to have some mechanisms to persist the newly added object to the DB. In other words instantaneously serialize the object. This will require some kind of an EventHandler to track the List<T> added event. Hmm... reminds me of a 1000 patterns from Martin Fowler , just to do this.. :) (Oh, my brave attempt to write a DataMapper in 3 days, i paid the price in the ACW..Thanks to Dr.Grey for enlightening me about my deed)



Output



Enumerating products..



ID: ,1Name: ,Product APrice: ,10.99

ID: ,2Name: ,Product BPrice: ,12.99


ID: ,3Name: ,Product CPrice: ,10.99


ID: ,4Name: ,Product DPrice: ,14.99


ID: ,5Name: ,Product EPrice: ,19


ID: ,6Name: ,Product FPrice: ,19.99


ID: ,7Name: ,Product GPrice: ,7.99


ID: ,8Name: ,Product HPrice: ,5.45


ID: ,9Name: ,Product IPrice: ,3.44


ID: ,10Name: ,Product JPrice: ,0.99



Press any key to exit..



The DB (for proof)



image



No comments:

Post a Comment