Wednesday, October 25, 2006

// // 2 comments

stumbling over the EntLib blocks

just when i had thought that 1.1 to 2.0 migration was going to be done within a week, I stumble upon the Enterprise Library application blocks migration from the June 2005 (.Net 1.1) to January 2006 (2.0).

The first approach i took was to replace all the assemblies with their 2006 version ans get rid of any obsolete ones ( e.g Configuration.dll ) - This completely wrecked the build. So now, I'm going to take up the migration assembly by assembly... slooow and careful, just as recommended

Microsoft.Practices.EnterpriseLibrary.Common
replaced the 1.1 version with the 2.0 version, Cleaned solution, Re-built;

Errors-
The name 'ArgumentValidation' does not exist in the current context

Fire up Lutz's reflector and notice that not only ArgumentValidation, but thenamespace it used to reide is gone in this release.




Fine, i'm not going to do any argumentvalidation. It was a sort of "nice to have" in any case . No complaints. Just commented out the offending statements. Build succeeded.

Microsoft.Practices.EnterpriseLibrary.Data.dll
replaced the 1.1 version with the 2.0 version, Cleaned solution, Re-built;

Errors -
The type or namespace name 'DBCommandWrapper' could not be found (are you missing a using directive or an assembly reference?

Ahh, so thats gone too ?

As per documentation, the replacement for this is System.Data.Common.DbCommand. And here is the example

Original Code

Database db = DatabaseFactory.CreateDatabase();
DBCommandWrapper dbCommand = db.GetStoredProcCommandWrapper("GetProductsByCategory");
dbCommand.AddInParameter("CategoryID", DbType.Int32, Category);
DataSet productDataSet = db.ExecuteDataSet(dbCommand);

Modify to

Database db = DatabaseFactory.CreateDatabase();
DbCommand dbCommand = db.GetStoredProcCommand("GetProductsByCategory");
db.AddInParameter(dbCommand , "CategoryID", DbType.Int32, Category);
DataSet productDataSet = db.ExecuteDataSet(dbCommand);


Take a close look - yes, you got it - Now to add in parameters you require both Database + DbCommand. So it is not going to be a straightforward replacement.

The application i'm migrating has literally thousands of calls to DbCommandWrapper.AddInParamater. Worse still these calls are made through another static Helper class, that does some business validations before actually calling AddInParameter on the DbcommandWrapper. And obviously, the Database object is not passed into it.

A typical signature for the helper function looks like

public static void AddStringInParameter( DBCommandWrapper cw, string parameterName, string parameterValue)
{
if ( null == cw )
return;

cw.AddInParameter( parameterName, DbType.String);

if ( null != parameterValue && parameterValue.Length>0 && NotAvailable.sNA != parameterValue)
cw.SetParameterValue( parameterName, parameterValue);
}

So this is a huge problem now. With this static helper class being used liberally all through the application, method parameter modification is not feasible. Manually repairing the code to pass in the Database object into such functions is also not an options just beacuse of the sheer number of times it is used ( >5K calls ).

At this point i realize that my basic need to Add parameters without needing Database object is essential to a painless migration.

Luckily, the internal implementation of DbCommand still allows the addition of parameters using the Parameters collection and without the need for a Database object. In addtion  DbCommandWrapper was very useful in hiding the complexity of CreatePararamter and Parameters.Add() by exposing the AddInParameter function family.

Now the functionality is exposed on the Database class; which is puzzling; as it could have been very easily implemented in the DbCommand class. Maybe a question for Tom.

So anyway, I decided to nick the code for DbCommandWrapper from the original Entlib (1.1) code. This would solve 3 problems- 1) obviously it would re-instate the class and make all the references valid again; 2) It would allow me to add parameters the way i was used to - without the database object. 3) at the same time keep the migration issues internal to the Wrapper class.

Hopefully, this would work with a few well-aimed hacks. And it did - just beautifully.

I copied the class code files exactly as they were into my namespace. This got rid of the original DbcommandWrapper not found errors. I then applied a few hacks on these Wrappers -

  • The original OracleCommandWrapper (OCW) is a concrete implementation of the (abstract) DbCommandWrapper (DbCW) ; In addition DbCW has a private mustoverride member IDbCommand, which OCW overrrides with OracleCommand . Changed IDbCommandrresponding DbCommand in System.Data 2.0.
  • Created a new constructor that accepted DbCommand, and got rid of the other constructors. This means that the new wrappers now are "Real" wrappers, in the sense that they don't have ability to create the DbCommand internally. Retaining the code to create the command, would have meant pulling in more source from the EntLib. I decided to avoid this.

so far so good - The DbcommandWrapper has re-incarnated and the AddParamter family of references are also now available. The code fit back into the jigsaw again.

unfortunately, I could not protect all of my code, but at least the further changes could be done by intelligent search and replace operations
  • System.Data.Database in the 2.0 versions replaces the EntLib.Database and it has no awareness of a DbCommandWrapper. So all the references to Database.Get[]Wrapper functions are no longer valid. These are all Database.Get[]Command functions now. Well, with what i consider a stroke of pre-science, we had decided to wrap the Database class with a custom DatabaseWrapper class that would hide (almost) all functionality of the Database class. Facading paid off ! In fact, the code broke exactly where we had been to lazy to facade some workings of the EntLib - Lesson learnt.
  • All the IDbConnection, IDbTransaction, and IDbCommand reference had to be updated to the new classes from System.Data.Common namespace equivalents - DbConnection, DbTransaction, DbCommand.i>
  • Database.GetConnection() has been replaced by Database.CreateConnection() . Search and replace.
The search and replace operation took about 15 mins.
>
Ahhh - no compile errors . The code is fitting in snug and tight -no ugly patches required. Life is good again .


... to be continued. Next migrating the Configuration block

kick it on DotNetKicks.com

2 comments:

  1. Anonymous11:33 PM

    Great read! You may want to follow up to this topic!?

    -Thank You
    Curt

    ReplyDelete
  2. Anonymous2:28 AM

    Hi I think that your blog is very nice!

    ReplyDelete