Ошибка приведения в SQLDataReader - PullRequest
5 голосов
/ 20 мая 2010

Мой сайт использует корпоративную библиотеку v 5.0. В основном DAAB. Некоторые функции, такие как executetescalar, executeataset, работают должным образом. Проблемы появляются, когда я начинаю использовать Readers

У меня есть эта функция в моем классе включений:

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

Это вызывается из моего aspx.vb примерно так:

reader = includes.AssignedDepartmentDetail(Did)
If reader.HasRows Then
    TheModule = reader("templatefilename")
    PageID = reader("id")
Else
    TheModule = "#"
End If

Это приводит к следующей ошибке в строке db.ExecuteReader:

Невозможно привести объект типа 'Microsoft.Practices.EnterpriseLibrary.Data.RefCountingDataReader' к типу System.Data.SqlClient.SqlDataReader '.

Может ли кто-нибудь пролить свет на то, как я могу заставить это работать. Буду ли я всегда сталкиваться с проблемами при работе с читателями через entlib?

Ответы [ 6 ]

3 голосов
/ 15 июня 2010

Я был бы осторожен с этой реализацией. На сайте Enterprise Library Codeplex есть ветка, объясняющая фон для этого: http://entlib.codeplex.com/Thread/View.aspx?ThreadId=212973

Крис Таварес объясняет, что возвращать .InnerReader нехорошо, потому что тогда отслеживание соединений с помощью Enterprise Library сбрасывается (его ответ от 20 мая, 17:39): «Такой подход полностью испортит ваше управление соединениями. Вся причина в обёртке заключается в том, что мы можем выполнить дополнительный код, чтобы убрать вещи во время утилизации. Захват внутреннего считывателя и выбрасывание внешнего утечки соединений! «

Так что да, с этим немного больно, мы в такой же ситуации.

С уважением, Mike

1 голос
/ 29 июля 2013

Я учел комментарии и код, размещенный ctavars на http://entlib.codeplex.com/discussions/212973 и http://entlib.codeplex.com/discussions/211288,, что привело к следующему универсальному подходу к получению чтения данных SQL.

Как правило, вы используете IDataReader в операторе использования, а затем используйте эту ссылку напрямую, когда можете. Вызовите AsSqlDataReader, когда вам нужно что-то специфичное для SQL.

Добавьте этот класс расширения куда-нибудь:

/// <summary>
/// Obtains an <see cref="SqlDataReader"/> from less derived data readers in Enterprise Library
/// </summary>
/// <remarks>
/// See http://entlib.codeplex.com/discussions/212973 and http://entlib.codeplex.com/discussions/211288
/// for a discussion of why this is necessary
/// </remarks>
public static class SqlDataReaderExtension
{
    /// <summary>
    /// Allows the internal <see cref="SqlDataReader"/> of a <see cref="RefCountingDataReader"/> to be accessed safely
    /// </summary>
    /// <remarks>
    /// To ensure correct use, the returned reference must not be retained and used outside the scope of the input
    /// reference. This is so that the reference counting does not get broken. In practice this means calling this method
    /// on the base reader every time a reference to it is required.
    /// </remarks>
    public static SqlDataReader AsSqlDataReader(this RefCountingDataReader reader)
    {
        return (SqlDataReader)(reader.InnerReader);
    }

    /// <summary>
    /// Allows the internal <see cref="SqlDataReader"/> of a <see cref="IDataReader"/> to be accessed safely
    /// </summary>
    /// <remarks>
    /// To ensure correct use, the returned reference must not be retained and used outside the scope of the input
    /// reference. This is so that the reference counting does not get broken. In practice this means calling this method
    /// on the base reader every time a reference to it is required.
    /// </remarks>
    public static SqlDataReader AsSqlDataReader(this IDataReader reader)
    {
        return (SqlDataReader)(((RefCountingDataReader)(reader)).InnerReader);
    }
}

... затем для чтения данных с помощью SQLReader сделайте что-то вроде этого:

using (IDataReader reader = db.ExecuteReader(command))
{
    while (reader.Read())
    {
        reader.GetInt32(reader.GetOrdinal("SomeColumn")),
        reader.GetInt32(reader.GetOrdinal("SomeOtherColumn")),
        reader.GetInt32(reader.GetOrdinal("SomeFurtherColumn")),
        // Obtain the SQL data reader each time it is used
        // (Note that GetDateTimeOffset is not on the standard IDataReader)
        reader.AsSqlDataReader().GetDateTimeOffset(reader.GetOrdinal("SQLSpecificColumn"))
        reader.AsSqlDataReader().GetDateTimeOffset(reader.GetOrdinal("AnotherSQLSpecificColumn"))
        reader.GetString(reader.GetOrdinal("SomeAdditionalColumn"))
    }
}
1 голос
/ 13 августа 2010

У меня утечка соединений, потому что все мои методы DA требуют SqlDataReader. Теперь я должен вернуть внутренний RefCountingDataReader и никогда не могу закрыть внешний ридер. Старая библиотека Enterprise работала нормально с возвратом SqlDataReader.

1 голос
/ 20 мая 2010

ExecuteReader в Enterprise Library включает IDataReader в RefCountingDataReader, который, как SqlDataReader, реализует интерфейс IDataReader.

RefCountingDataReader имеет свойство InnerReader, которое можно преобразовать в SqlDataReader. Пример ниже на C #, но вы можете легко преобразовать его в VB.NET.

SqlDataReader reader;
reader = ((RefCountingDataReader)db.ExecuteReader(command)).InnerReader as SqlDataReader;
if (reader != null)
    reader.Read();
return reader;

Надеюсь, это поможет

0 голосов
/ 31 июля 2012

Вы должны использовать интерфейс, а не конкретный класс.

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)
    reader.Read()
    Return reader
End Function

и использование. Лично я бы никогда не использовал устройство чтения данных на странице уровня презентации, но, думаю, каждому из них свой.

Private Const TemplateFileName_Select_Column_Ordinal As Integer = 0
Private Const Id_Select_Column_Ordinal As Integer = 1

Private Sub DoSomething()
dim reader as IDataReader
reader = includes.AssignedDepartmentDetail(Did)
If reader.HasRows Then
    TheModule = reader(TemplateFileName_Select_Column_Ordinal)
    PageID = reader(Id_Select_Column_Ordinal)
Else
    TheModule = "#"

    reader.Close()  ''Dude, close your reader(s)

End If
0 голосов
/ 16 августа 2010

Мне кажется, у меня есть рабочее решение.

enter code here

    ' 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)
...