Interface Programming: Entity Wrapper to Handle Dynamic Source Object

Background

During working with legacy code, I have found that many people used DataTable/DataSet instead of strongly typed objects. They are using some code like
string id = row["id"].ToString();
instead of
string id = request.Id;

It is becoming a maintenance hell because of several reasons:
  • I do not know the data type from database, so I need to debug into database procedure
  • I do not know whether the data is nullable or not, again I need to debug into database
  • When I need to change the data type, I need to search for every implementation, change it and make sure it does not break 
And last but not least, if I want to make enhancements or modifications, I have been faced with 2 options:
  • Keep  the same programming style, using the DataTable, with the risk that you add another more maintenance hell object
  • Refactor it, with the risk of breaking is higher
I want to use Dependency Injection (DI) for my further development. Lack of strongly typed entities are of course prevent me from using DI. So I need to change the DataTable to strongly typed object before using the DI implementation.

Traditional Way: Converter

Yes, it can be done using a defined strongly-typed entity, and using a converter to convert it. Example of code is:

public class Converter{
  public Request Convert(DataRow row){
    Request result = new Request();
    result.Id = row["id"].ToString();
    //rest of conversion here
  }
}

Then it is very annoying during the usage:

public void UseConverter(DataRow row){
  Converter converter = new Converter();
  Request = converter.Convert(row);
}

Of course, you can use static method, but once again you just create another trap for maintenance problem later on. Short tips, static method are hard to test, and cannot be mock/injected.

Traditional Way: Constructor Converter

Another way to handle this case is to add a constructor to the entity, making it accept DataRow and convert it as an entity. Example:
public class Request{
  public Request(DataRow row){
    this.Id = row["id"].ToString();
  }

  public string Id{get;set;}
} 

But using this design, you are risking of:
1. Having your entity class has many constructors, whether they are used or not. Making it dirty
2. Create a dependency of your entity class with DataRow. It will be confusing later on.

Using Interface to Wrap the Implementation

In order to solve constructor converter problem, we can use the interface to represent the entity, making it temporal dependent with the DataRow.

public interface IRequest{
  string Id{ get; } //up to the developer to add set accessor
}
public DataRowRequest:IRequest{
  public DataRowRequest(DataRow row){
    this._id = row["id"].ToString();
  }

  private string _id;
  public string Id{ get{ return id; } }
}

And the usage will be:

public void RequestFromDataRow(DataRow row){
  DataRowRequest request = new DataRowRequest(row);
  RequestConsumer(request);
}
// it is better if the consumer is in different class
public void RequestConsumer(IRequest request){
  string id = request.Id;
}

This way, you can get some benefits:
1. The consumer does not dependent at all with DataRow
2. DataRowRequest cannot be made without DataRow
3. The purpose of DataRowRequest is clear, having only 1 constructor accepting DataRow

This technique can also be used to handle static objects. I have asked a question at stack exchange here.

Conclusion

Using Interface correctly can decouple your consumer with other object, which in practical are not used at all by the consumer (in this case, the DataRow). You can implement the interface in many ways, and the consumer does not need to know about it.

More of it, using the wrapper with interface leaves clean design for the application. The purpose of the interface are clear, and the implementation are also clear. In the future, when the DataTable/DataRow is not used anymore, and changed to ORM, the implementation can be easily disposed, and the consumer will not need any changes at all.
Post a Comment