Suggestion : GenericDataReader

Suggestion : GenericDataReader

Old forum URL: forums.lhotka.net/forums/t/4524.aspx


RubensFarias posted on Wednesday, March 19, 2008

Hi All,

I'm trying to use SafeDataReader concept into a non CSLA project, basically due lack of GetXXX(string) methods in SqlDataReader. Currently I have a generic implementation I think could specially useful to CSLA:

// main load
using(GenericDataReader reader = new GenericDataReader(cm.ExecuteReader()))
{
    while (reader.Read())
    {
        orders.Add(reader.Get<Order>()); // "GetList" pattern
        //return new Order(reader);    // "Get" pattern
    }
}

// generic reader; interface implementation removed
public class GenericDataReader : IDataReader
{
    // ... \\

    public T Get<T>(string name)
    {
        int i = _dataReader.GetOrdinal(name);
        if (_dataReader.IsDBNull(i))
            return default(T);
        else
            return (T)_dataReader.GetValue(i);
    }

    public T Get<T>() where T : ILoadable, new()
    {
        T entity = new T();
        entity.Load(this, false);
        return entity;
    }
}

// All business objects must inherits this interface (@BusinessBase?)
public interface ILoadable
{
    void Load(GenericDataReader reader, bool deep);
}

// Sample BO 1
class Order : ILoadable
{
    int id;
    Customer customer; // Note
    DateTime orderDate;
    DateTime requiredDate;
    DateTime? shippedDate;
    List<OrderDetail> orderDetails;

    public Order(){ }

    public Order(GenericDataReader reader)
    {
        Load(reader, true);
    }

    #region ILoadable Members

    public void Load(GenericDataReader reader, bool deep)
    {
        id = reader.Get<int>("OrderID");
        customer = reader.Get<Customer>(); // Note

        orderDate = reader.Get<DateTime>("OrderDate");
        requiredDate = reader.Get<DateTime>("OrderRequiredDate");
        shippedDate = reader.Get<DateTime?>("OrderShippedDate");
        if (deep && reader.NextResult())
        {
            orderDetails = new List<OrderDetail>();
            while (reader.Read())
            {
                orderDetails.Add(reader.Get<OrderDetail>());
            }
        }
    }

    #endregion
}

// Sample BO 2
class Customer : ILoadable
{
    string customerID;
    List<Order> orders;

    public Customer(){ }

    internal Customer(GenericDataReader reader)
    {
        Load(reader, true);
    }

    #region ILoadable Members

    public void Load(GenericDataReader reader, bool deep)
    {
        customerID = reader.Get<string>("CustomerID");
        if (deep && reader.NextResult())
        {
            orders = new List<Order>();
            while (reader.Read())
            {
                orders.Add(reader.Get<Order>());
            }
        }
    }

    #endregion
}

What do you think? Are there any major drawback with this model I'm missing?

Best regards,

Rubens

nermin replied on Wednesday, March 19, 2008

Hi Rubens,

On a first look it looks like an elegant solution.  And this is completely OK if you are sure that this app is going to be built only with ADO.NET.

However if this is going to be an ongoing project, that perhaps in the future might migrate the Data Acces Layer code to some ORM Technology, like NHiberbate, Linq or Entity Framework, then the tight coupling of your business objects with the IDataReader (or GenericDataReader) might prove as a migration bottleneck.  In other words you have to change the signature of each of your BOs to migrate to another storage technology.

There is a reason why Rocky has not implemented something like that in Csla as much as it appears more readable and simpler than what we have now.

Those are my 2c

Nermin

RubensFarias:

Hi All,

I'm trying to use SafeDataReader concept into a non CSLA project, basically due lack of GetXXX(string) methods in SqlDataReader. Currently I have a generic implementation I think could specially useful to CSLA:

// main load
using(GenericDataReader reader = new GenericDataReader(cm.ExecuteReader()))
{
    while (reader.Read())
    {
        orders.Add(reader.Get<Order>()); // "GetList" pattern
        //return new Order(reader);    // "Get" pattern
    }
}

// generic reader; interface implementation removed
public class GenericDataReader : IDataReader
{
    // ... \\

    public T Get<T>(string name)
    {
        int i = _dataReader.GetOrdinal(name);
        if (_dataReader.IsDBNull(i))
            return default(T);
        else
            return (T)_dataReader.GetValue(i);
    }

    public T Get<T>() where T : ILoadable, new()
    {
        T entity = new T();
        entity.Load(this, false);
        return entity;
    }
}

// All business objects must inherits this interface (@BusinessBase?)
public interface ILoadable
{
    void Load(GenericDataReader reader, bool deep);
}

// Sample BO 1
class Order : ILoadable
{
    int id;
    Customer customer; // Note
    DateTime orderDate;
    DateTime requiredDate;
    DateTime? shippedDate;
    List<OrderDetail> orderDetails;

    public Order(){ }

    public Order(GenericDataReader reader)
    {
        Load(reader, true);
    }

    #region ILoadable Members

    public void Load(GenericDataReader reader, bool deep)
    {
        id = reader.Get<int>("OrderID");
        customer = reader.Get<Customer>(); // Note

        orderDate = reader.Get<DateTime>("OrderDate");
        requiredDate = reader.Get<DateTime>("OrderRequiredDate");
        shippedDate = reader.Get<DateTime?>("OrderShippedDate");
        if (deep && reader.NextResult())
        {
            orderDetails = new List<OrderDetail>();
            while (reader.Read())
            {
                orderDetails.Add(reader.Get<OrderDetail>());
            }
        }
    }

    #endregion
}

// Sample BO 2
class Customer : ILoadable
{
    string customerID;
    List<Order> orders;

    public Customer(){ }

    internal Customer(GenericDataReader reader)
    {
        Load(reader, true);
    }

    #region ILoadable Members

    public void Load(GenericDataReader reader, bool deep)
    {
        customerID = reader.Get<string>("CustomerID");
        if (deep && reader.NextResult())
        {
            orders = new List<Order>();
            while (reader.Read())
            {
                orders.Add(reader.Get<Order>());
            }
        }
    }

    #endregion
}

What do you think? Are there any major drawback with this model I'm missing?

Best regards,

Rubens

RubensFarias replied on Thursday, March 20, 2008

Hi Nermin,

In my specific case, there are no signal about ORM mapping in a closer future.

But you're right. I'll think about that and try to work around it.

Thank you for your time,

Rubens

Copyright (c) Marimer LLC