Problems trying to config the DAAB in entlib 5.0

Topics: Data Access Application Block, Enterprise Library Core
May 18, 2010 at 6:37 AM

I am trying to get up and running with the enterprise library 3.5. I am using Visual studio 2008 sp1 and asp,.net framework 3.5 sp1. I've followed what I believe (may be very wrong here ;) are the setup steps....

) Download / install enterprise library

2) Add references to the blocks I need (common / data)

3) Imports

Imports Microsoft.Practices.EnterpriseLibrary.Common 
Imports Microsoft.Practices.EnterpriseLibrary.Data 

4) Through the enterprise library config software. I open up the web.config from my site. I then click Blocks, then Add data settings... fill in my details and save / close

5) I then (thinking setup is complete) try to get an instance of the database via

 Dim db As Database = DatabaseFactory.CreateDatabase()  

6) I compile and receive the following error:

Could not load file or assembly 'Microsoft.Practices.EnterpriseLibrary.Data, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040) (C:\site\web.config line 4)

Line 4 off my web.config was generated by the config tool and is:

<section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" /> 

Am I missing a required step? Have I done the steps in the wrong order? Have I made a mistake?

I have also posted a question over on stack overflow regarding this... if you would like to answer it is here: http://bit.ly/ci7mhx

Thanks very much in advance for your time.

Phil Sando
www.phildoes.net

May 18, 2010 at 7:13 AM

DAAB only works if you target the full .net framework.  Go to project properties and change the Target Framework to .NET Framework 4.0.

 

Sarah Urmeneta
Global Technology & Solutions
Avanade, Inc.
entlib.support@Avanade.com

May 18, 2010 at 7:24 AM

Is it impossible to use .net framework 3.5 with vs 2008 sp1?

May 18, 2010 at 7:32 AM

No, sorry, it is possible.  Targeting .NET Framework 3.5 is fine.  This is a different issue.  Based on your error, it seems like you're using 2 different versions of DAAB.  From what location did you reference the entlib assemblies?  

Sarah Urmeneta
Global Technology & Solutions
Avanade, Inc.
entlib.support@Avanade.com

 

May 18, 2010 at 7:36 AM

I will remove / uninstall the daab / enterprise library, Then try the steps again to make sure I am working with a consistant version.

May 18, 2010 at 7:42 AM

You probably used the assemblies from the entlib source EntLib50Src\bin folder, these are the unsigned versions (PublicKeyToken=null).  You should be adding reference from Program Files\Microsoft Enterprise Library 5.0\Bin folder.

 

Sarah Urmeneta
Global Technology & Solutions
Avanade, Inc.
entlib.support@Avanade.com

May 18, 2010 at 8:09 AM

Thanks that seems to have fixed the problem

May 20, 2010 at 7:13 AM

OK, the DAAB is now working, but I am running into further problems;

I have this function:

    Public Function AssignedDepartmentDetail(ByVal Did As Integer) As SqlDataReader
        Dim reader As SqlDataReader
        Dim Command As SqlCommand = db.GetSqlStringCommand("select seomthing from somewhere where something = @did")
        db.AddInParameter(Command, "@did", Data.DbType.Int32, Did)
        reader = db.ExecuteReader(Command)
        reader.Read()
        Return reader
    End Function

This gives the following error on db.ExecuteReader line:

Unable to cast object of type 'Microsoft.Practices.EnterpriseLibrary.Data.RefCountingDataReader' to type 'System.Data.SqlClient.SqlDataReader'.

How do I go about getting this working. Will I always run into problems when dealing with the reader? If so I may have to remove the entlib as my proj uses these heavily.

May 20, 2010 at 7:28 AM
Edited May 20, 2010 at 7:48 AM

This is a result of a bug fix in 3.0.  The workaround is to use the .InnerReader property of the RefCountingDataReader.

See this thread for Chris Tavares' explanation - http://entlib.codeplex.com/Thread/View.aspx?ThreadId=211288

 

Sarah Urmeneta
Global Technology & Solutions
Avanade, Inc.
entlib.support@Avanade.com

May 20, 2010 at 8:11 AM

How can that be applied to my code? I'm new to using the daab and find the workaround a bit messy / confusing. Will this issue be fixed in the future?

 

May 20, 2010 at 8:20 AM
Edited May 20, 2010 at 8:23 AM

As Chris has suggested, create a wrapper method to make it a bit nicer, avoiding the need to scatter this code in places where you need an sqldatareader.  I'm not used to writing vb.net codes so I'm using C# here:

public SqlDataReader ExecuteReader(Database db)
{
         RefCountingDataReader reader = (RefCountingDataReader)db.ExecuteReader();
         return (SqlDataReader)reader.InnerReader;
}


I'm not sure if it will be fixed, better log it in the issue tracker.

 

Sarah Urmeneta
Global Technology & Solutions
Avanade, Inc.
entlib.support@Avanade.com

May 20, 2010 at 8:50 AM

In vb it looks something like this;

 

Public Function ExecuteReader(ByVal db As Database) As SqlDataReader
        Dim reader As RefCountingDataReader = DirectCast(db.ExecuteReader(), RefCountingDataReader)
        Return DirectCast(reader.InnerReader, SqlDataReader)
    End Function

but gives an error here:

 

DirectCast(db.ExecuteReader(), RefCountingDataReader)

Overload resolution failed because no accessible 'ExecuteReader' accepts this number of arguements.

 

 Please can you show me an example of how to get this function:

 

Public Function AssignedDepartmentDetail(ByVal Did As Integer) As SqlDataReader
        Dim reader As SqlDataReader
        Dim Command As SqlCommand = db.GetSqlStringCommand("select seomthing from somewhere where something = @did")
        db.AddInParameter(Command, "@did", Data.DbType.Int32, Did)
        reader = db.ExecuteReader(Command)
        reader.Read()
        Return reader
    End Function

 

Working with this workaround.

 

 

The function is inside my includes.vb class, being called from my default.aspx.vb page like this;

 

Dim reader As SqlDataReader = includes.AssignedDepartmentDetail(Did)

If reader.HasRows Then
                TheModule = reader("templatefilename")
                PageID = reader("id")
end if
Thanks.
May 20, 2010 at 8:57 AM
Edited May 20, 2010 at 9:02 AM

Oh, I forgot you need to add the parameter DbCommand to the wrapper method and pass that to the ExecuteReader method of the Database object. That was the cause of the error.

Sarah Urmeneta
Global Technology & Solutions
Avanade, Inc.
entlib.support@Avanade.com

May 20, 2010 at 9:02 AM

Please can you post the complete wrapper method.

Thanks a lot for the help!!

May 20, 2010 at 9:03 AM
Public Function ExecuteReader(ByVal db As Database, ByVal command As DbCommand) As SqlDataReader
        Dim reader As RefCountingDataReader = DirectCast(db.ExecuteReader(command), RefCountingDataReader)
        Return DirectCast(reader.InnerReader, SqlDataReader)
    End Function
May 20, 2010 at 9:06 AM

This should be placed in my includes class?

May 20, 2010 at 9:07 AM
Edited May 20, 2010 at 9:08 AM

Anywhere as long as it can be access by other classes which might need it.

Sarah Urmeneta
Global Technology & Solutions
Avanade, Inc.
entlib.support@Avanade.com

May 20, 2010 at 9:39 PM

That approach will completely screw up your connection management. The whole reason for the wrapper is so that we could execute extra code to clean stuff up at dispose time. Grabbing the inner reader and throwing out the outer will leak connections!

The original sample:

Public Function AssignedDepartmentDetail(ByVal Did As Integer) As SqlDataReader
        Dim reader As SqlDataReader
        Dim Command As SqlCommand = db.GetSqlStringCommand("select seomthing from somewhere where something = @did")
        db.AddInParameter(Command, "@did", Data.DbType.Int32, Did)
        reader = db.ExecuteReader(Command)
        reader.Read()
        Return reader
    End Function

Is bad for this reason as well - where does the reader get closed?

Also, there's nothing here that depends on SqlDataReader - why not just return IDataReader and work with to the interface? Failing that, a quick extension method:

public static SqlDataReader AsSqlReader(this IDataReader outer)
{
    return (SqlDataReader)(((RefCountingDataReader)outer).InnerReader);
}

Then, you could just pass around IDataReader, and any time you need the SqlDataReader explicitly, you could do:

using(IDataReader genericReader = GimmeAReader())
{
    genericReader.AsSqlReader(). /* whatever */();
}

May 21, 2010 at 6:27 AM

This is not very useful to me. I started using the enterprise library to save time. now im back to square one.

Can someone show me in code, in vb how to make this exact function here;

Public Function AssignedDepartmentDetail(ByVal Did As Integer) As SqlDataReader
        Dim reader As SqlDataReader
        Dim Command As SqlCommand = db.GetSqlStringCommand("select seomthing from somewhere where something = @did")
        db.AddInParameter(Command, "@did", Data.DbType.Int32, Did)
        reader = db.ExecuteReader(Command)
        reader.Read()
        Return reader
    End Function


Work with the enterprise library DAAB. 

 

May 21, 2010 at 8:55 AM
Edited May 21, 2010 at 9:05 AM

Sorry for that, I was focused on the casting thing and forgot about connection management.  Following on Chris' approach, your AssignedDepartmentDetail method would now return an IDataReader

Public Function AssignedDepartmentDetail(ByVal Did As Integer) As IDataReader
        Dim reader As IDataReader
        Dim Command As SqlCommand = db.GetSqlStringCommand("select seomthing from somewhere where something = @did")
        db.AddInParameter(Command, "@did", Data.DbType.Int32, Did)
        reader = db.ExecuteReader(Command)
        Return reader
    End Function

You would create then this extension method:

 <System.Runtime.CompilerServices.Extension()> _
 Public Function AsSqlReader(ByVal reader As IDataReader) As SqlDataReader
        Return DirectCast(DirectCast(reader, RefCountingDataReader).InnerReader, SqlDataReader)
End Function

 

You would then use this code when executing a datareader:

Using reader  AssignedDepartmentDetail(id) 

      SqlDataReader sqlReader = reader.AsSqlReader()

      'do the processing here

End Using

The purpose of this is to be able to properly close the actual IDataReader object created from the call to database.ExecuteReader . 

The previous one I posted as Chris said would cause connection leaks since it will only close the inner reader.

 

Sarah Urmeneta
Global Technology & Solutions
Avanade, Inc.
entlib.support@Avanade.com

Aug 2, 2010 at 7:18 AM
ctavares wrote:

That approach will completely screw up your connection management. The whole reason for the wrapper is so that we could execute extra code to clean stuff up at dispose time. Grabbing the inner reader and throwing out the outer will leak connections!

The original sample:

 

Public Function AssignedDepartmentDetail(ByVal Did As Integer) As SqlDataReader
        Dim reader As SqlDataReader
        Dim Command As SqlCommand = db.GetSqlStringCommand("select seomthing from somewhere where something = @did")
        db.AddInParameter(Command, "@did", Data.DbType.Int32, Did)
        reader = db.ExecuteReader(Command)
        reader.Read()
        Return reader
    End Function

 

Is bad for this reason as well - where does the reader get closed?

Also, there's nothing here that depends on SqlDataReader - why not just return IDataReader and work with to the interface? Failing that, a quick extension method:

 

public static SqlDataReader AsSqlReader(this IDataReader outer)
{
    return (SqlDataReader)(((RefCountingDataReader)outer).InnerReader);
}

 

Then, you could just pass around IDataReader, and any time you need the SqlDataReader explicitly, you could do:

 

using(IDataReader genericReader = GimmeAReader())
{
    genericReader.AsSqlReader(). /* whatever */();
}

 

Hi,

 

I'm facing the same problem. Can you please explain how to to extend the IDataReader. In our data access application how do we extend by using this method

public static SqlDataReader AsSqlReader(this IDataReader outer)

 

Aug 2, 2010 at 7:31 AM

I'm not sure what do you mean, the code was already posted by Chris Tavares.

The AsSqlReader is the extension method which allows you to call that method against any instance of IDataReader

using(IDataReader genericReader = GimmeAReader())
{
    SqlDataReader sqlReader = genericReader.AsSqlReader();
}

Sarah Urmeneta
Global Technology & Solutions
Avanade, Inc.
entlib.support@Avanade.com

Aug 13, 2010 at 3:48 AM

So are you saying every da method that was using SqlDataReader that use to do this:

using reader = da.GetSomeData()
end using

has to now do this:

using reader = da.GetSomeData()
     SqlDataReader sqlReader = reader.AsSqlReader()
end using

Just to kill the outer reader to have clean connections?  That is not the way it has been for the other DAAB's.
I don't want to have to re-write all the da's just because the DAAB is returning a RefCountingDataReader.

Please tell me someone has a better solution.

 

Aug 13, 2010 at 6:50 PM

I think I have a working solution.

        ' Create the Database object, using the default database service. The
        ' default database service is determined through configuration.
        Dim db As Microsoft.Practices.EnterpriseLibrary.Data.Database = EnterpriseLibraryContainer.Current.GetInstance(Of Microsoft.Practices.EnterpriseLibrary.Data.Database)(DatabaseName)

        Dim dbCommand As DbCommand
        dbCommand = db.GetStoredProcCommand(StoredProcedureName)

        'create a new database connection based on the enterprise library database connection
        Dim dbConnection As System.Data.Common.DbConnection
        dbConnection = db.CreateConnection
        dbConnection.Open()

        'set the dbCommand equal to the open dbConnection
        dbCommand.Connection = dbConnection

        'return a ADO sqlDatareader but still managed by the EnterpriseLibrary
        Return dbCommand.ExecuteReader(CommandBehavior.CloseConnection)