430 lines
17 KiB
C#
430 lines
17 KiB
C#
/************************************************************************************
|
|
*
|
|
* Umbraco Data Layer
|
|
* MIT Licensed work
|
|
* ©2008 Ruben Verborgh
|
|
*
|
|
***********************************************************************************/
|
|
|
|
using System;
|
|
using System.Data;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
|
|
namespace umbraco.DataLayer
|
|
{
|
|
/// <summary>
|
|
/// Class that adapts a generic data reader to an IRecordsReader.
|
|
/// </summary>
|
|
/// <typeparam name="D">The data reader class</typeparam>
|
|
public abstract class RecordsReaderAdapter<D> : IRecordsReader
|
|
where D : IDataReader, IDataRecord, IEnumerable
|
|
{
|
|
#region Private Fields
|
|
|
|
/// <summary> Wrapped data reader. </summary>
|
|
private readonly D m_DataReader;
|
|
|
|
/* Use the DebugDataLayer compile constant to get information about the opened RecordsReaderAdapters. */
|
|
#if DEBUG && DebugDataLayer
|
|
|
|
/// <summary>Application unique identifier of this RecordsReaderAdapter.</summary>
|
|
/// <remarks>Used to track different instantiations for debug purposes.</remarks>
|
|
private readonly int m_Id;
|
|
|
|
/// <summary>Application unique identifier that will be assigned to the next new RecordsReaderAdapter.</summary>
|
|
/// <remarks>Used to track different instantiations for debug purposes.</remarks>
|
|
private static int m_GlobalId = 0;
|
|
|
|
/// <summary>List of identifiers of open RecordsReaderAdapters.</summary>
|
|
/// <remarks>Used to track different instantiations for debug purposes.</remarks>
|
|
private static List<int> m_OpenDataReaderIds = new List<int>();
|
|
|
|
#endif
|
|
|
|
#endregion
|
|
|
|
#region Public Properties
|
|
/// <summary>Gets the internal data reader.</summary>
|
|
/// <value>The data reader.</value>
|
|
public D DataReader
|
|
{
|
|
get { return m_DataReader; }
|
|
}
|
|
|
|
/// <summary>Gets the internal data reader.</summary>
|
|
/// <value>The data reader.</value>
|
|
/// <remarks>Obsolete. You should NOT try to close or dispose the RawDataReader,
|
|
/// but instead close or dispose this RecordsReaderAdapter.
|
|
/// Inheriting classes can call the protected DataReader property.</remarks>
|
|
[Obsolete("Only for backwards compatibility.", false)]
|
|
public D RawDataReader
|
|
{
|
|
get { return m_DataReader; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Constructors and destructors
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="RecordsReaderAdapter<D>"/> class.
|
|
/// </summary>
|
|
/// <param name="dataReader">The data reader.</param>
|
|
public RecordsReaderAdapter(D dataReader)
|
|
{
|
|
m_DataReader = dataReader;
|
|
|
|
#if DEBUG && DebugDataLayer
|
|
lock (m_OpenDataReaderIds)
|
|
{
|
|
// Get application unique identifier
|
|
m_Id = m_GlobalId++;
|
|
// Signal the creation of this new RecordsReaderAdapter
|
|
m_OpenDataReaderIds.Add(m_Id);
|
|
StackFrame stackFrame = new StackFrame(4);
|
|
string caller = stackFrame.GetMethod().ReflectedType.Name + "." + stackFrame.GetMethod().Name;
|
|
Trace.TraceInformation(m_Id + ". RecordsReader created by " + caller + ". "
|
|
+ "Open Data Readers: " + m_OpenDataReaderIds.Count);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// Releases unmanaged resources and performs other cleanup operations before the
|
|
/// <see cref="RecordsReaderAdapter<D>"/> is reclaimed by garbage collection.
|
|
/// </summary>
|
|
~RecordsReaderAdapter()
|
|
{
|
|
Dispose(false);
|
|
}
|
|
#endregion
|
|
|
|
#region IRecordsReader Members
|
|
|
|
/// <summary>
|
|
/// Closes the data reader.
|
|
/// </summary>
|
|
public virtual void Close()
|
|
{
|
|
try
|
|
{
|
|
m_DataReader.Close();
|
|
}
|
|
catch { }
|
|
|
|
#if DEBUG && DebugDataLayer
|
|
// Log closing
|
|
lock (m_OpenDataReaderIds)
|
|
{
|
|
m_OpenDataReaderIds.Remove(m_Id);
|
|
Trace.TraceInformation(m_Id + ". RecordsReader closed. "
|
|
+ "Open Data Readers: " + m_OpenDataReaderIds.Count);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the depth of nesting of the current row.
|
|
/// </summary>
|
|
/// <value>The depth of nesting of the current row.</value>
|
|
public int Depth
|
|
{
|
|
get { return m_DataReader.Depth; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether this instance is closed.
|
|
/// </summary>
|
|
/// <value><c>true</c> if this instance is closed; otherwise, <c>false</c>.</value>
|
|
public bool IsClosed
|
|
{
|
|
get { return m_DataReader.IsClosed; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Advances to the next record.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// <c>true</c> if there are more records; otherwise, <c>false</c>.
|
|
/// </returns>
|
|
public bool Read()
|
|
{
|
|
return m_DataReader.Read();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether this instance has records.
|
|
/// </summary>
|
|
/// <value>
|
|
/// <c>true</c> if this instance has records; otherwise, <c>false</c>.
|
|
/// </value>
|
|
public abstract bool HasRecords { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the value of the specified field.
|
|
/// </summary>
|
|
/// <typeparam name="FieldType">The field type.</typeparam>
|
|
/// <param name="fieldName">Name of the field.</param>
|
|
/// <returns>The value of the specified field</returns>
|
|
public FieldType Get<FieldType>(string fieldName)
|
|
{
|
|
switch (typeof(FieldType).FullName)
|
|
{
|
|
case "System.Boolean": return (FieldType)(object)GetBoolean(fieldName);
|
|
case "System.Byte": return (FieldType)(object)GetByte(fieldName);
|
|
case "System.DateTime": return (FieldType)(object)GetDateTime(fieldName);
|
|
case "System.Decimal": return (FieldType)(object)GetDecimal(fieldName);
|
|
case "System.Double": return (FieldType)(object)GetDouble(fieldName);
|
|
case "System.Single": return (FieldType)(object)GetFloat(fieldName);
|
|
case "System.Guid": return (FieldType)(object)GetGuid(fieldName);
|
|
case "System.Int16": return (FieldType)(object)GetShort(fieldName);
|
|
case "System.Int32": return (FieldType)(object)GetInt(fieldName);
|
|
case "System.Int64": return (FieldType)(object)GetLong(fieldName);
|
|
case "System.String": return (FieldType)(object)GetString(fieldName);
|
|
default: return (FieldType)GetObject(fieldName);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the value of the specified field as a bool.
|
|
/// </summary>
|
|
/// <param name="fieldName">Name of the field.</param>
|
|
/// <returns>The value of the field.</returns>
|
|
/// <exception cref="System.IndexOutOfRangeException">No column with the specified name was found.</exception>
|
|
public bool GetBoolean(string fieldName)
|
|
{
|
|
return m_DataReader.GetBoolean(GetOrdinal(fieldName));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the value of the specified field as a byte.
|
|
/// </summary>
|
|
/// <param name="fieldName">Name of the field.</param>
|
|
/// <returns>The value of the field.</returns>
|
|
/// <exception cref="System.IndexOutOfRangeException">No column with the specified name was found.</exception>
|
|
public byte GetByte(string fieldName)
|
|
{
|
|
return m_DataReader.GetByte(GetOrdinal(fieldName));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the value of the specified field as a DateTime.
|
|
/// </summary>
|
|
/// <param name="fieldName">Name of the field.</param>
|
|
/// <returns>The value of the field.</returns>
|
|
/// <exception cref="System.IndexOutOfRangeException">No column with the specified name was found.</exception>
|
|
public DateTime GetDateTime(string fieldName)
|
|
{
|
|
return m_DataReader.GetDateTime(GetOrdinal(fieldName));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the value of the specified field as a decimal.
|
|
/// </summary>
|
|
/// <param name="fieldName">Name of the field.</param>
|
|
/// <returns>The value of the field.</returns>
|
|
/// <exception cref="System.IndexOutOfRangeException">No column with the specified name was found.</exception>
|
|
public decimal GetDecimal(string fieldName)
|
|
{
|
|
return m_DataReader.GetDecimal(GetOrdinal(fieldName));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the value of the specified field as a double.
|
|
/// </summary>
|
|
/// <param name="fieldName">Name of the field.</param>
|
|
/// <returns>The value of the field.</returns>
|
|
/// <exception cref="System.IndexOutOfRangeException">No column with the specified name was found.</exception>
|
|
public double GetDouble(string fieldName)
|
|
{
|
|
return m_DataReader.GetDouble(GetOrdinal(fieldName));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the value of the specified field as a float.
|
|
/// </summary>
|
|
/// <param name="fieldName">Name of the field.</param>
|
|
/// <returns>The value of the field.</returns>
|
|
/// <exception cref="System.IndexOutOfRangeException">No column with the specified name was found.</exception>
|
|
public float GetFloat(string fieldName)
|
|
{
|
|
return m_DataReader.GetFloat(GetOrdinal(fieldName));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the value of the specified field as a Guid.
|
|
/// </summary>
|
|
/// <param name="fieldName">Name of the field.</param>
|
|
/// <returns>The value of the field.</returns>
|
|
/// <exception cref="System.IndexOutOfRangeException">No column with the specified name was found.</exception>
|
|
public Guid GetGuid(string fieldName)
|
|
{
|
|
return m_DataReader.GetGuid(GetOrdinal(fieldName));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the value of the specified field as a short.
|
|
/// </summary>
|
|
/// <param name="fieldName">Name of the field.</param>
|
|
/// <returns>The value of the field.</returns>
|
|
/// <exception cref="System.IndexOutOfRangeException">No column with the specified name was found.</exception>
|
|
public short GetShort(string fieldName)
|
|
{
|
|
return m_DataReader.GetInt16(GetOrdinal(fieldName));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the value of the specified field as an int.
|
|
/// </summary>
|
|
/// <param name="fieldName">Name of the field.</param>
|
|
/// <returns>The value of the field.</returns>
|
|
/// <exception cref="System.IndexOutOfRangeException">No column with the specified name was found.</exception>
|
|
public int GetInt(string fieldName)
|
|
{
|
|
return m_DataReader.GetInt32(GetOrdinal(fieldName));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the value of the specified field as a long.
|
|
/// </summary>
|
|
/// <param name="fieldName">Name of the field.</param>
|
|
/// <returns>The value of the field.</returns>
|
|
/// <exception cref="System.IndexOutOfRangeException">No column with the specified name was found.</exception>
|
|
public long GetLong(string fieldName)
|
|
{
|
|
return m_DataReader.GetInt64(GetOrdinal(fieldName));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the value of the specified field as a string.
|
|
/// </summary>
|
|
/// <param name="fieldName">Name of the field.</param>
|
|
/// <returns>The value of the field.</returns>
|
|
/// <exception cref="System.IndexOutOfRangeException">No column with the specified name was found.</exception>
|
|
public string GetString(string fieldName)
|
|
{
|
|
int ordinal = GetOrdinal(fieldName);
|
|
return m_DataReader.IsDBNull(ordinal) ? null : m_DataReader.GetString(ordinal);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the value of the specified field.
|
|
/// </summary>
|
|
/// <param name="fieldName">Name of the field.</param>
|
|
/// <returns>The value of the field.</returns>
|
|
/// <exception cref="System.IndexOutOfRangeException">No column with the specified name was found.</exception>
|
|
public object GetObject(string fieldName)
|
|
{
|
|
int ordinal = GetOrdinal(fieldName);
|
|
return m_DataReader.IsDBNull(ordinal) ? null : m_DataReader.GetValue(ordinal);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines whether the specified field is null.
|
|
/// </summary>
|
|
/// <param name="fieldName">Name of the field.</param>
|
|
/// <returns>
|
|
/// <c>true</c> if the specified field is null; otherwise, <c>false</c>.
|
|
/// </returns>
|
|
public bool IsNull(string fieldName)
|
|
{
|
|
return m_DataReader.IsDBNull(GetOrdinal(fieldName));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines whether a field with the specified field name exists in the record.
|
|
/// The field can still contain a null value.
|
|
/// </summary>
|
|
/// <param name="fieldName">Name of the field.</param>
|
|
/// <returns>
|
|
/// <c>true</c> if the specified field exists; otherwise, <c>false</c>.
|
|
/// </returns>
|
|
public bool ContainsField(string fieldName)
|
|
{
|
|
bool fieldExists;
|
|
try
|
|
{
|
|
GetOrdinal(fieldName);
|
|
fieldExists = true;
|
|
}
|
|
catch
|
|
{
|
|
// GetOrdinal failed, field was not found.
|
|
fieldExists = false;
|
|
}
|
|
return fieldExists;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the index of the field with the specified name.
|
|
/// </summary>
|
|
/// <param name="fieldName">The name of the field.</param>
|
|
/// <returns>The index of the field.</returns>
|
|
protected virtual int GetOrdinal(string fieldName)
|
|
{
|
|
return m_DataReader.GetOrdinal(fieldName);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IEnumerable Members
|
|
|
|
/// <summary>
|
|
/// Returns an enumerator that iterates through the records.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// An <see cref="T:System.Collections.IEnumerator"></see> object that can be used to iterate through records.
|
|
/// </returns>
|
|
public IEnumerator GetEnumerator()
|
|
{
|
|
return m_DataReader.GetEnumerator();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IDisposable Members
|
|
|
|
/// <summary>
|
|
/// Performs application-defined tasks associated with freeing, releasing,
|
|
/// or resetting unmanaged resources.
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Releases unmanaged and - optionally - managed resources.
|
|
/// </summary>
|
|
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources;
|
|
/// <c>false</c> to release only unmanaged resources.</param>
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
// Try to close the data reader if it's still open
|
|
try
|
|
{
|
|
if (m_DataReader != null && !m_DataReader.IsClosed)
|
|
Close();
|
|
}
|
|
finally
|
|
{
|
|
// Try to dispose the data reader
|
|
try
|
|
{
|
|
if (m_DataReader != null)
|
|
m_DataReader.Dispose();
|
|
}
|
|
catch { }
|
|
// Dispose methods should call SuppressFinalize
|
|
if (disposing)
|
|
{
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|