The IDataReader Interface Providing a solid DataReader is core to providing a reliable .NET managed data provider. DataReaders provide forward-only access over the ResultSets obtained by executing database commands. They also provide access to values for the fields in each DataRow through strongly typed accessor methods. Examples of strongly typed accessors are GetInt32, GetString, and so on. Additionally, DataAdapters use DataReaders to fill the DataTables in a DataSet. The Data Access Application Block returns a class that implements the IDataReader interface when the ExecuteReader method is called on a database provider. The XmlFileDataReader is the implementation of the IDataReader interface for the .NET managed data provider for XML files. However, before showing you some of the code for this DataReader, I must provide a disclaimer: I have not made this DataReader as efficient as it should be. It uses a private DataSet member variable to read and write data from and to XML files, and uses the DataSet internally to return the values for column fields. While it is not optimized for performance, this DataReader will serve the purpose for providing a valid implementation for the IDataReader interface so a database provider can be created and used. The DataReader could be enhanced to perform better. To do so, it would need to use the same inference process that .NET uses to determine the relational structure of the data when loading from XML. The inference process first determines, from the XML document, which elements will be inferred as tables. From the remaining XML, the inference process determines the columns for those tables. For nested tables, the inference process generates nested DataRelation and ForeignKeyConstraint objects. The following assumptions must be made during this process. Elements that have attributes are inferred as tables. Elements that have child elements are inferred as tables. Elements that repeat are inferred as a single table. If the document (root) element has no attributes, and no child elements that would be inferred as columns, it is inferred as a DataSet. Otherwise, the document element is inferred as a table. Attributes are inferred as columns. Elements that have no attributes or child elements and do not repeat are inferred as columns. For elements that are inferred as tables that are nested in other elements also inferred as tables, a nested DataRelation is created between the two tables. A new, primary key column named TableName_Id is added to both tables and used by the DataRelation. A ForeignKeyConstraint is created between the two tables using the TableName_Id column. For elements that are inferred as tables and that contain text but have no child elements, a new column named TableName_Text is created for the text of each of the elements. If an element is inferred as a table and has text as well as child elements, the text is ignored. Listing B.3 provides an excerpt of the XmlFileDataReader. Listing B.3. XmlFileDataReader Implements IDataReader [C#] public class XmlFileDataReader : IDataReader { ... public bool NextResult() { return (tableNum < dataSet.Tables.Count); } public void Close() { closed = true; if (xmlFileConnection != null) xmlFileConnection.Close(); } internal void GetXmlFile(string strCommand) { fileName = strCommand; try { dataSet.ReadXml(strCommand); rowNum = -1; closed = false; } catch (Exception ex) { throw new ApplicationException (SR.ExceptionLoadXMLFile(command, ex.Message)); } } internal XmlReader XmlReader { get { try { XmlReader retVal = new XmlTextReader (new System.IO.StringReader (dataSet.GetXml())); return retVal; } catch (Exception ex) { throw new ApplicationException( SR.ExceptionCreateReader(ex.Message)); } } } public bool Read() { bool bRetVal = false; if (closed) return bRetVal; if (rowNum < (dataSet.Tables[tableNum].Rows.Count -1)) { bRetVal = true; rowNum++; } else { tableNum++; rowNum = -1; } return bRetVal; } public int GetInt32(int i) { return (Int32)GetValue(i); } public byte GetByte(int i) { return (Byte)GetValue(i); } ... } [Visual Basic] Public Class XmlFileDataReader : Implements IDataReader ... Public Function NextResult() As Boolean _ Implements IDataReader.NextResult Return (tableNum < dataSet.Tables.Count) End Function Public Sub Close() Implements IDataReader.Close closed = True If Not xmlFileConnection Is Nothing Then xmlFileConnection.Close() End If End Sub Friend Sub GetXmlFile(ByVal strCommand As String) fileName = strCommand Try dataSet.ReadXml(strCommand) rowNum = -1 closed = False Catch ex As Exception Throw New ApplicationException _ (SR.ExceptionLoadXMLFile(command, ex.Message)) End Try End Sub Friend ReadOnly Property XmlReader() As XmlReader Get Try Dim retVal As XmlReader = New XmlTextReader _ (New _ System.IO.StringReader(dataSet.GetXml())) Return retVal Catch ex As Exception Throw New _ ApplicationException _ (SR.ExceptionCreateReader(ex.Message)) End Try End Get End Property Public Function Read() As Boolean Implements IDataReader.Read Dim bRetVal As Boolean = False If closed Then Return bRetVal End If If rowNum < (dataSet.Tables(tableNum).Rows.Count -1) Then bRetVal = True rowNum += 1 Else tableNum += 1 rowNum = -1 End If Return bRetVal End Function Public Function GetInt32(ByVal i As Integer) As Integer Return CInt(GetValue(i)) End Function Public Function GetByte(ByVal i As Integer) As Byte Return CType(GetValue(i), Byte) End Function ... End Class | |