Data sources customization in ActiveReports is one of the most popular demands. Our Oracle Data Provider sample demonstrates the custom data providers implementation details.
To support a custom data provider in Web, you need to perform some additional steps:
A custom data provider allows you to use non-traditional data sources in your Page reports, both at run time and at design time. Let us learn how to create a solution with projects that create a custom data provider and demonstrate how it pulls data from a comma separated values (CSV) file.
When you complete these steps, you will have a designer pre-loaded with a report that pulls data from a CSV file and looks like the following.
Text. Paste in the Text property of the RichTextBox. |
Copy Code
|
---|---|
1. In the Report Explorer, right-click the Data Sources node and select Add Data Source. 2. In the Report Data Source dialog that appears, on the General tab, drop down the Type box, select CSV Data Provider, and click OK. 3. In the Report Explorer, right-click DataSource1 and select Add Data Set. 4. In the dialog that appears, select the Query page. 5. Drop down the Query String box to display the custom query editor. 6. Click the Select CSV File button and open the Categories.csv file in this project. 7. Click OK to save the changes and close the dialog. 8. Click Preview to see the report with data from the csv file. |
Visual Basic.NET code. Paste above the class. |
Copy Code
|
---|---|
Imports System.Xml Imports System.IO Imports GrapeCity.ActiveReports.Design |
C# code. Paste above the class. |
Copy Code
|
---|---|
using System.Xml; using System.IO; using GrapeCity.ActiveReports.Design; |
Visual Basic.NET code. Paste inside the form Load event. |
Copy Code
|
---|---|
Using reportStream = File.OpenRead("DemoReport.rdlx") Using reader = XmlReader.Create(reportStream) Designer1.LoadReport(reader, DesignerReportType.Page) End Using End Using |
C# code. Paste inside the form Load event. |
Copy Code
|
---|---|
using (var reportStream = File.OpenRead("DemoReport.rdlx")) { using (var reader = XmlReader.Create(reportStream)) { designer1.LoadReport(reader, DesignerReportType.Page); } } |
Paste into the config file. |
Copy Code
|
---|---|
<?xml version="1.0" encoding="utf-8" ?> <Configuration> <Extensions> <Data> <Extension Name="CSV" DisplayName="CSV Data Provider" Type="CustomDataProvider.CsvDataProvider.CsvDataProviderFactory, CustomDataProvider" CommandTextEditorType="CustomDataProvider.CSVDataProvider.QueryEditor, CustomDataProvider"/> </Data> </Extensions> </Configuration> |
Paste into the text file. |
Copy Code
|
---|---|
EmployeeID(int32),LastName,FirstName,Role,City 1,James,Yolanda,Owner,Columbus 7,Reed,Marvin,Manager,Newton 9,Figg,Murray,Cashier,Columbus 12,Snead,Lance,Store Keeper,Columbus 15,Halm,Jeffry,Store Keeper,Columbus 17,Hames,Alma,Store Keeper,Oak Bay 18,Nicki,Aubrey,Store Keeper,Columbus 24,Cliett,Vikki,Store Keeper,Newton |
TextBox Name | Value Property | BackgroundColor Property |
---|---|---|
TextBox1 | Name | MediumSlateBlue |
TextBox2 | Role | MediumSlateBlue |
TextBox3 | City | MediumSlateBlue |
TextBox4 | =Fields!FirstName.Value & " " & Fields!LastName.Value | |
TextBox5 | =Fields!Role.Value | |
TextBox6 | =Fields!City.Value |
Visual Basic.NET code. Paste it to replace the default stub in the class. |
Copy Code
|
---|---|
Namespace CSVDataProvider ' Represents information about fields in the data source. Friend Structure CsvColumn Private ReadOnly _fieldName As String Private ReadOnly _dataType As Type ' Creates a new instance of the CsvColumn class. ' The fieldName parameter is the name of the field represented by this instance of the CsvColumn. ' The dataType parameter is the Type of the field represented by this instance of the CsvColumn. Public Sub New(fieldName As String, dataType As Type) If fieldName Is Nothing Then Throw New ArgumentNullException("fieldName") End If If dataType Is Nothing Then Throw New ArgumentNullException("dataType") End If _fieldName = fieldName _dataType = dataType End Sub ' Gets the name of the field represented by this instance of the CsvColumn. Public ReadOnly Property FieldName() As String Get Return _fieldName End Get End Property ' Gets the the Type of the field represented by this instance of the CsvColumn. Public ReadOnly Property DataType() As Type Get Return _dataType End Get End Property ' Returns a String that represents this instance of the CsvColumn. Public Overrides Function ToString() As String Return [String].Concat(New String() {FieldName, "(", DataType.ToString(), ")"}) End Function ' Determines whether two CsvColumn instances are equal. ' The obj represents the CsvColumn to compare with the current CsvColumn. ' Returns True if the specified CsvColumn is equal to the current CsvColumn; otherwise, False. Public Overrides Function Equals(obj As Object) As Boolean Dim flag As Boolean If TypeOf obj Is CsvColumn Then flag = Equals(CType(obj, CsvColumn)) Else flag = False End If Return flag End Function Private Overloads Function Equals(column As CsvColumn) As Boolean Return column.FieldName = FieldName End Function ' Serves as a hash function for a CsvColumn, suitable for use in hashing algorithms and data structures like a hash table. ' Returns a hash code for the current CsvColumn instance. Public Overrides Function GetHashCode() As Integer Return (FieldName.GetHashCode() + DataType.GetHashCode()) End Function End Structure End Namespace |
C# code. Paste it to replace the default stub in the class. |
Copy Code
|
---|---|
using System; namespace CustomDataProvider.CSVDataProvider { // Represents information about fields in the data source. internal struct CsvColumn { private readonly string _fieldName; private readonly Type _dataType; // Creates a new instance of the CsvColumn class. // The fieldName parameter is the name of the field represented by this instance of the CsvColumn. // The dataType parameter is the Type of the field represented by this instance of the CsvColumn. public CsvColumn(string fieldName, Type dataType) { if (fieldName == null) throw new ArgumentNullException("fieldName"); if (dataType == null) throw new ArgumentNullException("dataType"); _fieldName = fieldName; _dataType = dataType; } // Gets the name of the field represented by this instance of the CsvColumn. public string FieldName { get { return _fieldName; } } // Gets the the Type of the field represented by this instance of the CsvColumn. public Type DataType { get { return _dataType; } } // Returns a String that represents this instance of the CsvColumn. public override string ToString() { return String.Concat(new string[] {FieldName, "(", DataType.ToString(), ")"}); } // Determines whether two CsvColumn instances are equal. // The obj represents the CsvColumn to compare with the current CsvColumn. // Returns True if the specified CsvColumn is equal to the current CsvColumn; otherwise, False. public override bool Equals(object obj) { bool flag; if (obj is CsvColumn) { flag = Equals((CsvColumn) obj); } else { flag = false; } return flag; } private bool Equals(CsvColumn column) { return column.FieldName == FieldName; } // Serves as a hash function for a CsvColumn, suitable for use in hashing algorithms and data structures like a hash table. // Returns a hash code for the current CsvColumn instance. public override int GetHashCode() { return (FieldName.GetHashCode() + DataType.GetHashCode()); } } } |
Visual Basic.NET code. Paste it to replace the default stub in the class. |
Copy Code
|
---|---|
Imports System Imports System.Collections Imports System.Globalization Imports System.IO Imports System.Text.RegularExpressions Imports GrapeCity.ActiveReports.Extensibility.Data Namespace CSVDataProvider ' Provides an implementation of IDataReader for the .NET Framework CSV Data Provider. Friend Class CsvDataReader Implements IDataReader 'NOTE: HashcodeProvider and Comparer need to be case-insensitive since TypeNames are capitalized differently in places. 'Otherwise data types end up as strings when using Int32 vs int32. Private _typeLookup As New Hashtable(StringComparer.Create(CultureInfo.InvariantCulture, False)) Private _columnLookup As New Hashtable() Private _columns As Object() Private _textReader As TextReader Private _currentRow As Object() 'The regular expressions are set to be pre-compiled to make it faster. Since we were concerned about 'multi-threading, we made the properties read-only so no one can change any properties on these objects. Private Shared ReadOnly _rxDataRow As New Regex(",(?=(?:[^""]*""[^""]*"")*(?![^""]*""))", RegexOptions.Compiled) 'Used to parse the data rows. Private Shared ReadOnly _rxHeaderRow As New Regex("(?<fieldName>(\w*\s*)*)\((?<fieldType>\w*)\)", RegexOptions.Compiled) 'Used to parse the header rows. ' Creates a new instance of the CsvDataReader class. ' The textReader parameter represents the TextReader to use to read the data. Public Sub New(textReader As TextReader) _textReader = textReader ParseCommandText() End Sub ' Parses the passed-in command text. Private Sub ParseCommandText() If _textReader.Peek() = -1 Then Return End If 'Command text is empty or at the end already. FillTypeLookup() Dim header As String = _textReader.ReadLine() header = AddDefaultTypeToHeader(header) If Not ParseHeader(header) Then Throw New InvalidOperationException( _ "Field names and types are not defined. " & _ "The first line in the CommandText must contain the field names and data types. e.g FirstName(string)") End If End Sub 'A hashtable is used to return a type for the string value used in the header text. Private Sub FillTypeLookup() _typeLookup.Add("string", GetType([String])) _typeLookup.Add("byte", GetType([Byte])) _typeLookup.Add("boolean", GetType([Boolean])) _typeLookup.Add("datetime", GetType(DateTime)) _typeLookup.Add("decimal", GetType([Decimal])) _typeLookup.Add("double", GetType([Double])) _typeLookup.Add("int16", GetType(Int16)) _typeLookup.Add("int32", GetType(Int32)) _typeLookup.Add("int", GetType(Int32)) _typeLookup.Add("integer", GetType(Int32)) _typeLookup.Add("int64", GetType(Int64)) _typeLookup.Add("sbyte", GetType([SByte])) _typeLookup.Add("single", GetType([Single])) _typeLookup.Add("time", GetType(DateTime)) _typeLookup.Add("date", GetType(DateTime)) _typeLookup.Add("uint16", GetType(UInt16)) _typeLookup.Add("uint32", GetType(UInt32)) _typeLookup.Add("uint64", GetType(UInt64)) End Sub ' Returns a type based on the string value passed in from the header text string. If no match is found, ' a string type is returned. ' The fieldType parameter represents the String value from the header command text string. Private Function GetFieldTypeFromString(fieldType As String) As Type If _typeLookup.Contains(fieldType) Then Return TryCast(_typeLookup(fieldType), Type) End If Return GetType([String]) End Function ' Parses the first line in the passed-in command text string to create the field names and field data types. ' The field information is stored in a CsvColumn struct, and these column info items are stored ' in an ArrayList. The column name is also added to a hashtable for easy lookup later. ' The header parameter represents the header string that contains all the fields. ' Returns True if it can parse the header string; otherwise False. Private Function ParseHeader(header As String) As Boolean Dim fieldName As String Dim index As Integer = 0 If header.IndexOf("(") = -1 Then Return False End If Dim matches As MatchCollection = _rxHeaderRow.Matches(header) _columns = New Object(matches.Count - 1) {} For Each match As Match In matches fieldName = match.Groups("fieldName").Value Dim fieldType As Type = GetFieldTypeFromString(match.Groups("fieldType").Value) _columns.SetValue(New CsvColumn(fieldName, fieldType), index) _columnLookup.Add(fieldName, index) index += 1 Next Return True End Function ' Ensures that the header contains columns in the form of name(type) ' The line parameter represents the raw header line from the file to fix up. ' Returns a modified header with default types appended to column names. Private Shared Function AddDefaultTypeToHeader(line As String) As String Const ColumnWithDataTypeRegex As String = "[""]?\w+[\""]?\(.+\)" Dim columns As String() = line.Split(New String() {","}, StringSplitOptions.None) Dim ret As String = Nothing For Each column As String In columns If Not String.IsNullOrEmpty(ret) Then ret += "," End If If Not Regex.Match(column, ColumnWithDataTypeRegex).Success Then ret += column + "(string)" Else ret += column End If Next Return ret End Function ' Parses a row of data using a regular expression and stores the information inside an object ' array that is the current row of data. ' If the row does not have the correct number of fields, an exception is raised. ' The dataRow parameter represents the String value representing a comma delimited data row. ' Returns True if it can parse the data string; otherwise False. Private Function ParseDataRow(dataRow As String) As Boolean Dim index As Integer = 0 Dim tempData As String() = _rxDataRow.Split(dataRow) _currentRow = New Object(tempData.Length - 1) {} If tempData.Length <> _columns.Length Then Dim [error] As String = String.Format(CultureInfo.InvariantCulture, _ "Invalid row ""{0}"". The row does not contain the same number of data columns as the table header definition.", dataRow) Throw New InvalidOperationException([error]) End If For i As Integer = 0 To tempData.Length - 1 Dim value As String = tempData(i) If value.Length > 1 Then If value.IndexOf(""""c, 0) = 0 AndAlso value.IndexOf(""""c, 1) = value.Length - 1 Then value = value.Substring(1, value.Length - 2) End If End If _currentRow.SetValue(ConvertValue(GetFieldType(index), value), index) index += 1 Next Return True End Function ' Coverts the string value coming from the command text to the appropriate data type, based on the field's type. ' This also checks a few string value rules to decide if a String.Empty of System.Data.DBNull needs to be returned. ' The type parameter represents the Type of the current column the data belongs to. ' The originalValue parameter represents the String value coming from the command text. ' Returns the object resulting from the converted string, based on the type. Private Function ConvertValue(type As Type, originalValue As String) As Object Dim fieldType As Type = type Dim invariantCulture As CultureInfo = CultureInfo.InvariantCulture Try If originalValue = """""" OrElse originalValue = " " Then Return String.Empty End If If originalValue = "" Then Return DBNull.Value End If If originalValue = "DBNull" Then Return DBNull.Value End If If fieldType.Equals(GetType([String])) Then Return originalValue.Trim() End If If fieldType.Equals(GetType(Int32)) Then Return Convert.ToInt32(originalValue, invariantCulture) End If If fieldType.Equals(GetType([Boolean])) Then Return Convert.ToBoolean(originalValue, invariantCulture) End If If fieldType.Equals(GetType(DateTime)) Then Return Convert.ToDateTime(originalValue, invariantCulture) End If If fieldType.Equals(GetType([Decimal])) Then Return Convert.ToDecimal(originalValue, invariantCulture) End If If fieldType.Equals(GetType([Double])) Then Return Convert.ToDouble(originalValue, invariantCulture) End If If fieldType.Equals(GetType(Int16)) Then Return Convert.ToInt16(originalValue, invariantCulture) End If If fieldType.Equals(GetType(Int64)) Then Return Convert.ToInt64(originalValue, invariantCulture) End If If fieldType.Equals(GetType([Single])) Then Return Convert.ToSingle(originalValue, invariantCulture) End If If fieldType.Equals(GetType([Byte])) Then Return Convert.ToByte(originalValue, invariantCulture) End If If fieldType.Equals(GetType([SByte])) Then Return Convert.ToSByte(originalValue, invariantCulture) End If If fieldType.Equals(GetType(UInt16)) Then Return Convert.ToUInt16(originalValue, invariantCulture) End If If fieldType.Equals(GetType(UInt32)) Then Return Convert.ToUInt32(originalValue, invariantCulture) End If If fieldType.Equals(GetType(UInt64)) Then Return Convert.ToUInt64(originalValue, invariantCulture) End If Catch e As Exception Throw New InvalidOperationException(String.Format("Input value '{0}' could not be converted to the type '{1}'.", originalValue, type), e) End Try 'If no match is found return DBNull instead. Return DBNull.Value End Function #Region "IDataReader Members" ' Advances the CsvDataReader to the next record. ' Returns True if there are more rows; otherwise, False. Public Function Read() As Boolean Implements IDataReader.Read If _textReader.Peek() > -1 Then ParseDataRow(_textReader.ReadLine()) Else Return False End If Return True End Function #End Region #Region "IDisposable Members" ' Releases the resources used by the CsvDataReader. Public Sub Dispose() Implements IDisposable.Dispose Dispose(True) GC.SuppressFinalize(Me) End Sub Private Sub Dispose(disposing As Boolean) If disposing Then If _textReader IsNot Nothing Then _textReader.Close() End If End If _typeLookup = Nothing _columnLookup = Nothing _columns = Nothing _currentRow = Nothing End Sub ' Allows an Object to attempt to free resources and perform ' other cleanup operations before the Object is reclaimed by garbage collection. Protected Overrides Sub Finalize() Try Dispose(False) Finally MyBase.Finalize() End Try End Sub #End Region #Region "IDataRecord Members" ' Gets the number of columns in the current row. Public ReadOnly Property FieldCount() As Integer Implements IDataRecord.FieldCount Get Return _columns.Length End Get End Property ' The i parameter represents the index of the field to find. ' Returns the Type information corresponding to the type of Object that would be returned from GetValue. Public Function GetFieldType(i As Integer) As Type Implements IDataReader.GetFieldType If i > _columns.Length - 1 Then Return Nothing End If Return DirectCast(_columns.GetValue(i), CsvColumn).DataType End Function ' Gets the name for the field to find. ' The i parameter represents the index of the field to find. ' Returns the name of the field or an empty string (""), if there is no value to return. Public Function GetName(i As Integer) As String Implements IDataRecord.GetName If i > _columns.Length - 1 Then Return String.Empty End If Return DirectCast(_columns.GetValue(i), CsvColumn).FieldName End Function ' The name parameter represents the name of the field to find. ' Returns the index of the named field. Public Function GetOrdinal(name As String) As Integer Implements IDataRecord.GetOrdinal Dim value As Object = _columnLookup(name) If value Is Nothing Then Throw New IndexOutOfRangeException("name") End If Return CInt(value) End Function ' The i parameter represents the index of the field to find. ' Returns the Object which contains the value of the specified field. Public Function GetValue(i As Integer) As Object Implements IDataRecord.GetValue If i > _columns.Length - 1 Then Return Nothing End If Return _currentRow.GetValue(i) End Function Public Overridable Function GetData(fieldIndex As Integer) As IDataReader Implements IDataReader.GetData Throw New NotSupportedException() End Function #End Region End Class End Namespace |
C# code. Paste it to replace the default stub in the class. |
Copy Code
|
---|---|
using System; using System.Collections; using System.Globalization; using System.IO; using System.Text.RegularExpressions; using GrapeCity.ActiveReports.Extensibility.Data; namespace CustomDataProvider.CSVDataProvider { // Provides an implementation of IDataReader for the .NET Framework CSV Data Provider. internal class CsvDataReader : IDataReader { //NOTE: HashcodeProvider and Comparer need to be case-insensitive since TypeNames are capitalized differently in places. //Otherwise data types end up as strings when using Int32 vs int32. private Hashtable _typeLookup = new Hashtable(StringComparer.Create(CultureInfo.InvariantCulture, false)); private Hashtable _columnLookup = new Hashtable(); private object[] _columns; private TextReader _textReader; private object[] _currentRow; //The regular expressions are set to be pre-compiled to make it faster. Since we were concerned about //multi-threading, we made the properties read-only so no one can change any properties on these objects. private static readonly Regex _rxDataRow = new Regex(@",(?=(?:[^""]*""[^""]*"")*(?![^""]*""))", RegexOptions.Compiled); //Used to parse the data rows. private static readonly Regex _rxHeaderRow = new Regex(@"(?<fieldName>(\w*\s*)*)\((?<fieldType>\w*)\)", RegexOptions.Compiled); //Used to parse the header rows. // Creates a new instance of the CsvDataReader class. // The textReader parameter represents the TextReader to use to read the data. public CsvDataReader(TextReader textReader) { _textReader = textReader; ParseCommandText(); } // Parses the passed-in command text. private void ParseCommandText() { if (_textReader.Peek() == -1) return; //Command text is empty or at the end already. FillTypeLookup(); string header = _textReader.ReadLine(); header = AddDefaultTypeToHeader(header); if (!ParseHeader(header)) throw new InvalidOperationException( "Field names and types are not defined. The first line in the CommandText must contain the field names and data types. e.g FirstName(string)"); } //A hashtable is used to return a type for the string value used in the header text. private void FillTypeLookup() { _typeLookup.Add("string", typeof (String)); _typeLookup.Add("byte", typeof (Byte)); _typeLookup.Add("boolean", typeof (Boolean)); _typeLookup.Add("datetime", typeof (DateTime)); _typeLookup.Add("decimal", typeof (Decimal)); _typeLookup.Add("double", typeof (Double)); _typeLookup.Add("int16", typeof (Int16)); _typeLookup.Add("int32", typeof (Int32)); _typeLookup.Add("int", typeof (Int32)); _typeLookup.Add("integer", typeof (Int32)); _typeLookup.Add("int64", typeof (Int64)); _typeLookup.Add("sbyte", typeof (SByte)); _typeLookup.Add("single", typeof (Single)); _typeLookup.Add("time", typeof (DateTime)); _typeLookup.Add("date", typeof (DateTime)); _typeLookup.Add("uint16", typeof (UInt16)); _typeLookup.Add("uint32", typeof (UInt32)); _typeLookup.Add("uint64", typeof (UInt64)); } // Returns a type based on the string value passed in from the header text string. If no match is found, a string type is returned. // The fieldType parameter represents the String value from the header command text string. private Type GetFieldTypeFromString(string fieldType) { if (_typeLookup.Contains(fieldType)) return _typeLookup[fieldType] as Type; return typeof (String); } // Parses the first line in the passed-in command text string to create the field names and field data types. The field information // is stored in a CsvColumn struct, and these column info items are stored in an ArrayList. The column name is also added // to a hashtable for easy lookup later. // The header parameter represents the header string that contains all the fields. // Returns True if it can parse the header string; otherwise False. private bool ParseHeader(string header) { string fieldName; int index = 0; if (header.IndexOf("(") == -1) return false; MatchCollection matches = _rxHeaderRow.Matches(header); _columns = new object[matches.Count]; foreach (Match match in matches) { fieldName = match.Groups["fieldName"].Value; Type fieldType = GetFieldTypeFromString(match.Groups["fieldType"].Value); _columns.SetValue(new CsvColumn(fieldName, fieldType), index); _columnLookup.Add(fieldName, index); index++; } return true; } // Ensures that the header contains columns in the form of name(type) // The line parameter represents the raw header line from the file to fix up. // Returns a modified header with default types appended to column names. private static string AddDefaultTypeToHeader(string line) { const string ColumnWithDataTypeRegex = @"[""]?\w+[\""]?\(.+\)"; string[] columns = line.Split(new string[] { "," }, StringSplitOptions.None); string ret = null; foreach (string column in columns) { if (!string.IsNullOrEmpty(ret)) ret += ","; if (!Regex.Match(column, ColumnWithDataTypeRegex).Success) { ret += column + "(string)"; } else { ret += column; } } return ret; } // Parses a row of data using a regular expression and stores the information inside an object array that is the current row of data. // If the row does not have the correct number of fields, an exception is raised. // The dataRow parameter represents the String value representing a comma delimited data row. // Returns True if it can parse the data string; otherwise False. private bool ParseDataRow(string dataRow) { int index = 0; string[] tempData = _rxDataRow.Split(dataRow); _currentRow = new object[tempData.Length]; if (tempData.Length != _columns.Length) { string error = string.Format(CultureInfo.InvariantCulture, "Invalid row \"{0}\". The row does not contain the same number of data columns as the table header definition.", dataRow); throw new InvalidOperationException(error); } for (int i = 0; i < tempData.Length; i++) { string value = tempData[i]; if (value.Length > 1) { if (value.IndexOf('"', 0) == 0 && value.IndexOf('"', 1) == value.Length - 1) value = value.Substring(1, value.Length - 2); } _currentRow.SetValue(ConvertValue(GetFieldType(index), value), index); index++; } return true; } // Coverts the string value coming from the command text to the appropriate data type, based on the field's type. // This also checks a few string value rules to decide if a String.Empty of System.Data.DBNull needs to be returned. // The type parameter represents the Type of the current column the data belongs to. // The originalValue parameter represents the String value coming from the command text. // Returns the object resulting from the converted string, based on the type. private object ConvertValue(Type type, string originalValue) { Type fieldType = type; CultureInfo invariantCulture = CultureInfo.InvariantCulture; try { if (originalValue == "\"\"" || originalValue == " ") return string.Empty; if (originalValue == "") return DBNull.Value; if (originalValue == "DBNull") return DBNull.Value; if (fieldType.Equals(typeof (String))) return originalValue.Trim(); if (fieldType.Equals(typeof (Int32))) return Convert.ToInt32(originalValue, invariantCulture); if (fieldType.Equals(typeof (Boolean))) return Convert.ToBoolean(originalValue, invariantCulture); if (fieldType.Equals(typeof (DateTime))) return Convert.ToDateTime(originalValue, invariantCulture); if (fieldType.Equals(typeof (Decimal))) return Convert.ToDecimal(originalValue, invariantCulture); if (fieldType.Equals(typeof (Double))) return Convert.ToDouble(originalValue, invariantCulture); if (fieldType.Equals(typeof (Int16))) return Convert.ToInt16(originalValue, invariantCulture); if (fieldType.Equals(typeof (Int64))) return Convert.ToInt64(originalValue, invariantCulture); if (fieldType.Equals(typeof (Single))) return Convert.ToSingle(originalValue, invariantCulture); if (fieldType.Equals(typeof (Byte))) return Convert.ToByte(originalValue, invariantCulture); if (fieldType.Equals(typeof (SByte))) return Convert.ToSByte(originalValue, invariantCulture); if (fieldType.Equals(typeof (UInt16))) return Convert.ToUInt16(originalValue, invariantCulture); if (fieldType.Equals(typeof (UInt32))) return Convert.ToUInt32(originalValue, invariantCulture); if (fieldType.Equals(typeof (UInt64))) return Convert.ToUInt64(originalValue, invariantCulture); } catch (Exception e) { throw new InvalidOperationException( string.Format("Input value '{0}' could not be converted to the type '{1}'.", originalValue, type), e); } //If no match is found return DBNull instead. return DBNull.Value; } #region IDataReader Members // Advances the CsvDataReader to the next record. // Returns True if there are more rows; otherwise, False. public bool Read() { if (_textReader.Peek() > -1) ParseDataRow(_textReader.ReadLine()); else return false; return true; } #endregion #region IDisposable Members // Releases the resources used by the CsvDataReader. public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (disposing) { if (_textReader != null) _textReader.Close(); } _typeLookup = null; _columnLookup = null; _columns = null; _currentRow = null; } // Allows an Object to attempt to free resources and perform other cleanup operations before the Object is reclaimed by garbage collection. ~CsvDataReader() { Dispose(false); } #endregion #region IDataRecord Members // Gets the number of columns in the current row. public int FieldCount { get { return _columns.Length; } } // The i parameter represents the index of the field to find. // Returns the Type information corresponding to the type of Object that would be returned from GetValue. public Type GetFieldType(int i) { if (i > _columns.Length - 1) return null; return ((CsvColumn) _columns.GetValue(i)).DataType; } // Gets the name for the field to find. // The i parameter represents the index of the field to find. // Returns the name of the field or an empty string (""), if there is no value to return. public string GetName(int i) { if (i > _columns.Length - 1) return string.Empty; return ((CsvColumn) _columns.GetValue(i)).FieldName; } // The name parameter represents the name of the field to find. // Returns the index of the named field. public int GetOrdinal(string name) { object value = _columnLookup[name]; if (value == null) throw new IndexOutOfRangeException("name"); return (int) value; } // The i parameter represents the index of the field to find. // Returns the Object which contains the value of the specified field. public object GetValue(int i) { if (i > _columns.Length - 1) return null; return _currentRow.GetValue(i); } public virtual IDataReader GetData(int fieldIndex) { throw new NotSupportedException(); } #endregion } } |
Visual Basic.NET code. Paste it to replace the default stub in the class. |
Copy Code
|
---|---|
Imports System Imports System.IO Imports GrapeCity.ActiveReports.Extensibility.Data Namespace CSVDataProvider ' Provides the IDbCommand implementation for the .NET Framework CSV Data Provider. Public NotInheritable Class CsvCommand Implements IDbCommand Private _commandText As String Private _connection As IDbConnection Private _commandTimeout As Integer Private _commandType As CommandType ' Creates a new instance of the CsvCommand class. Public Sub New() Me.New(String.Empty) End Sub ' Creates a new instance of the CsvCommand class with command text. ' The commandText parameter represents the command text. Public Sub New(commandText As String) Me.New(commandText, Nothing) End Sub ' Creates a new instance of the CsvCommand class with command text and a CsvConnection. ' The commandText parameter represents the command text. ' The connection parameter represents a CsvConnection to a data source. Public Sub New(commandText As String, connection As CsvConnection) _commandText = commandText _connection = connection End Sub ' Gets or sets the command to execute at the data source. Public Property CommandText() As String Implements IDbCommand.CommandText Get Return _commandText End Get Set(value As String) _commandText = value End Set End Property ' Gets or sets the wait time before terminating an attempt to execute the command and generating an error. Public Property CommandTimeout() As Integer Implements IDbCommand.CommandTimeout Get Return _commandTimeout End Get Set(value As Integer) _commandTimeout = value End Set End Property ' Gets or sets a value indicating how the CommandText property is interpreted. ' Remarks: We don't use this one for the Csv Data Provider. Public Property CommandType() As CommandType Implements IDbCommand.CommandType Get Return _commandType End Get Set(value As CommandType) _commandType = value End Set End Property ' Gets or sets the CsvConnection used by this instance of the CsvCommand. Public Property Connection() As IDbConnection Get Return _connection End Get Set(value As IDbConnection) _connection = value End Set End Property ' Sends the CommandText to the CsvConnection, and builds a CsvDataReader using one of the CommandBehavior values. ' The behavior parameter represents a CommandBehavior value. ' Returns a CsvDataReader object. Public Function ExecuteReader(behavior As CommandBehavior) As IDataReader Implements IDbCommand.ExecuteReader Return New CsvDataReader(New StringReader(_commandText)) End Function ' Returns a string that represents the command text with the parameters expanded into constants. Public Function GenerateRewrittenCommandText() As String Implements IDbCommand.GenerateRewrittenCommandText Return _commandText End Function ' Sends the CommandText to the CsvConnection and builds a CsvDataReader. ' Returns a CsvDataReader object. Public Function ExecuteReader() As IDataReader Implements IDbCommand.ExecuteReader Return ExecuteReader(CommandBehavior.SchemaOnly) End Function #Region "Non implemented IDbCommand Members" Public ReadOnly Property Parameters() As IDataParameterCollection Implements IDbCommand.Parameters Get Throw New NotImplementedException() End Get End Property Public Property Transaction() As IDbTransaction Implements IDbCommand.Transaction Get Throw New NotImplementedException() End Get Set(value As IDbTransaction) Throw New NotImplementedException() End Set End Property Public Sub Cancel() Implements IDbCommand.Cancel End Sub Public Function CreateParameter() As IDataParameter Implements IDbCommand.CreateParameter Throw New NotImplementedException() End Function #End Region #Region "IDisposable Members" ' Releases the resources used by the CsvCommand. Public Sub Dispose() Implements IDisposable.Dispose Dispose(True) GC.SuppressFinalize(Me) End Sub Private Sub Dispose(disposing As Boolean) If disposing Then If _connection IsNot Nothing Then _connection.Dispose() _connection = Nothing End If End If End Sub #End Region End Class End Namespace |
C# code. Paste it to replace the default stub in the class |
Copy Code
|
---|---|
using System; using System.IO; using GrapeCity.ActiveReports.Extensibility.Data; namespace CustomDataProvider.CSVDataProvider { // Provides the IDbCommand implementation for the .NET Framework CSV Data Provider. public sealed class CsvCommand : IDbCommand { private string _commandText; private IDbConnection _connection; private int _commandTimeout; private CommandType _commandType; /// Creates a new instance of the CsvCommand class. public CsvCommand() : this(string.Empty) { } // Creates a new instance of the CsvCommand class with command text. // The commandText parameter represents the command text. public CsvCommand(string commandText) : this(commandText, null) { } // Creates a new instance of the CsvCommand class with command text and a CsvConnection. // The commandText parameter represents the command text. // The connection parameter represents a CsvConnection to a data source.? public CsvCommand(string commandText, CsvConnection connection) { _commandText = commandText; _connection = connection; } // Gets or sets the command to execute at the data source. public string CommandText { get { return _commandText; } set { _commandText = value; } } // Gets or sets the wait time before terminating an attempt to execute the command and generating an error. public int CommandTimeout { get { return _commandTimeout; } set { _commandTimeout = value; } } // Gets or sets a value indicating how the CommandText property is interpreted. // Remarks: We don't use this one for the Csv Data Provider. public CommandType CommandType { get { return _commandType; } set { _commandType = value; } } // Gets or sets the CsvConnection used by this instance of the CsvCommand. public IDbConnection Connection { get { return _connection; } set { _connection = value; } } // Sends the CommandText to the CsvConnection, and builds a CsvDataReader using one of the CommandBehavior values. // The behavior parameter represents a CommandBehavior value. // Returns a CsvDataReader object. public IDataReader ExecuteReader(CommandBehavior behavior) { return new CsvDataReader(new StringReader(_commandText)); } // Returns a string that represents the command text with the parameters expanded into constants. public string GenerateRewrittenCommandText() { return _commandText; } // Sends the CommandText to the CsvConnection and builds a CsvDataReader. // Returns a CsvDataReader object. public IDataReader ExecuteReader() { return ExecuteReader(CommandBehavior.SchemaOnly); } #region Non implemented IDbCommand Members public IDataParameterCollection Parameters { get { throw new NotImplementedException(); } } public IDbTransaction Transaction { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } public void Cancel() { } public IDataParameter CreateParameter() { throw new NotImplementedException(); } #endregion #region IDisposable Members // Releases the resources used by the CsvCommand. public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (disposing) { if (_connection != null) { _connection.Dispose(); _connection = null; } } } #endregion } } |
Visual Basic.NET code. Paste it to replace the default stub in the class. |
Copy Code
|
---|---|
Imports System Imports System.Collections.Specialized Imports GrapeCity.ActiveReports.Extensibility.Data Namespace CSVDataProvider ' Provides an implementation of IDbConnection for the .NET Framework CSV Data Provider. Public NotInheritable Class CsvConnection Implements IDbConnection Private _localizedName As String ' Creates a new instance of the CsvConnection class. Public Sub New() _localizedName = "Csv" End Sub ' Creates a new instance of the CsvConnection class. ' The localizedName parameter represents the localized name for the CsvConnection instance. Public Sub New(localizeName As String) _localizedName = localizeName End Sub #Region "IDbConnection Members" ' Gets or sets the string used to open the connection to the data source. ' Remarks: We don't use this one for the Csv Data Provider. Public Property ConnectionString() As String Implements IDbConnection.ConnectionString Get Return String.Empty End Get Set(value As String) End Set End Property ' Gets the amount of time to wait while trying to establish a connection before terminating ' the attempt and generating an error. ' Remarks: We don't use this one for the Csv Data Provider. Public ReadOnly Property ConnectionTimeout() As Integer Implements IDbConnection.ConnectionTimeout Get Throw New NotImplementedException() End Get End Property ' Begins a data source transaction. ' Returns an object representing the new transaction. ' Remarks: We don't use this one for the Csv Data Provider. Public Function BeginTransaction() As IDbTransaction Implements IDbConnection.BeginTransaction Return Nothing End Function ' Opens a data source connection. ' Remarks: We don't use this one for the Csv Data Provider. Public Sub Open() Implements IDbConnection.Open End Sub ' Closes the connection to the data source. This is the preferred method of closing any open connection. Public Sub Close() Implements IDbConnection.Close Dispose() End Sub ' Creates and returns a CsvCommand object associated with the CsvConnection. Public Function CreateCommand() As IDbCommand Implements IDbConnection.CreateCommand Return New CsvCommand(String.Empty) End Function Public Property DataProviderService() As IDataProviderService Implements IDbConnection.DataProviderService Get Return Nothing End Get Set(value As IDataProviderService) End Set End Property #End Region #Region "IDisposable Members" ' Releases the resources used by the CsvConnection. Public Sub Dispose() Implements IDisposable.Dispose Dispose(True) GC.SuppressFinalize(Me) End Sub Private Sub Dispose(disposing As Boolean) End Sub ' Allows an Object to attempt to free resources and perform other cleanup operations ' before the Object is reclaimed by garbage collection. Protected Overrides Sub Finalize() Try Dispose(False) Finally MyBase.Finalize() End Try End Sub #End Region #Region "IExtension Members" ' Gets the localized name of the CsvConnection. Public ReadOnly Property LocalizedName() As String Implements IDbConnection.LocalizedName Get Return _localizedName End Get End Property ' Specifies any configuration information for this extension. ' The configurationSettings parameter represents a NameValueCollection of the settings. Public Sub SetConfiguration(configurationSettings As NameValueCollection) Implements IDbConnection.SetConfiguration End Sub #End Region End Class End Namespace |
C# code. Paste it to replace the default stub in the class. |
Copy Code
|
---|---|
using System; using System.Collections.Specialized; using GrapeCity.ActiveReports.Extensibility.Data; namespace CustomDataProvider.CSVDataProvider { // Provides an implementation of IDbConnection for the .NET Framework CSV Data Provider. public sealed class CsvConnection : IDbConnection { private string _localizedName; // Creates a new instance of the CsvConnection class. public CsvConnection() { _localizedName = "Csv"; } // Creates a new instance of the CsvConnection class. // The localizedName parameter represents the localized name for the CsvConnection instance. public CsvConnection(string localizeName) { _localizedName = localizeName; } #region IDbConnection Members // Gets or sets the string used to open the connection to the data source. // Remarks: We don't use this one for the Csv Data Provider. public string ConnectionString { get { return string.Empty; } set { ; } } // Gets the amount of time to wait while trying to establish a connection before terminating the attempt and generating an error. // Remarks: We don't use this one for the Csv Data Provider. public int ConnectionTimeout { get { throw new NotImplementedException(); } } // Begins a data source transaction. // Returns an object representing the new transaction. // Remarks: We don't use this one for the Csv Data Provider. public IDbTransaction BeginTransaction() { return null; } // Opens a data source connection. // Remarks: We don't use this one for the Csv Data Provider. public void Open() { ; } // Closes the connection to the data source. This is the preferred method of closing any open connection. public void Close() { Dispose(); } // Creates and returns a CsvCommand object associated with the CsvConnection. public IDbCommand CreateCommand() { return new CsvCommand(string.Empty); } public IDataProviderService DataProviderService { get { return null; } set { } } #endregion #region IDisposable Members // Releases the resources used by the CsvConnection. public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { } // Allows an Object to attempt to free resources and perform other cleanup operations before the Object is reclaimed by garbage collection. ~CsvConnection() { Dispose(false); } #endregion #region IExtension Members // Gets the localized name of the CsvConnection. public string LocalizedName { get { return _localizedName; } } // Specifies any configuration information for this extension. // The configurationSettings parameter represents a NameValueCollection of the settings. public void SetConfiguration(NameValueCollection configurationSettings) { } #endregion } } |
Visual Basic.NET code. Paste it to replace the default stub in the class. |
Copy Code
|
---|---|
Imports GrapeCity.ActiveReports.Extensibility.Data Imports GrapeCity.BI.Data.DataProviders Namespace CSVDataProvider ' Implements the DataProviderFactory for .NET Framework CSV Data Provider. Public Class CsvDataProviderFactory Inherits DataProviderFactory ' Creates new instance of the CsvDataProviderFactory class. Public Sub New() End Sub ' Returns a new instance of the the CsvCommand. Public Overrides Function CreateCommand() As IDbCommand Return New CsvCommand() End Function ' Returns a new instance of the the CsvConnection. Public Overrides Function CreateConnection() As IDbConnection Return New CsvConnection() End Function End Class End Namespace |
C# code. Paste it to replace the default stub in the class. |
Copy Code
|
---|---|
using GrapeCity.ActiveReports.Extensibility.Data; using GrapeCity.BI.Data.DataProviders; namespace CustomDataProvider.CSVDataProvider { // Implements the DataProviderFactory for .NET Framework CSV Data Provider. public class CsvDataProviderFactory : DataProviderFactory { // Creates new instance of the CsvDataProviderFactory class. public CsvDataProviderFactory() { } // Returns a new instance of the the CsvCommand. public override IDbCommand CreateCommand() { return new CsvCommand(); } // Returns a new instance of the the CsvConnection. public override IDbConnection CreateConnection() { return new CsvConnection(); } } } |
Visual Basic.NET code. Paste it to replace the default stub in the class. |
Copy Code
|
---|---|
Imports System.Collections.Generic Imports System.Drawing.Design Imports System.IO Imports System.Linq Imports System.Text Imports System.Text.RegularExpressions Imports System.Windows.Forms Imports System.Windows.Forms.Design Namespace CustomDataProvider.CSVDataProvider Public NotInheritable Class QueryEditor Inherits UITypeEditor Public Overrides Function GetEditStyle(context As System.ComponentModel.ITypeDescriptorContext) As UITypeEditorEditStyle Return UITypeEditorEditStyle.DropDown End Function Public Overrides Function EditValue(context As System.ComponentModel.ITypeDescriptorContext, provider As System.IServiceProvider, value As Object) As Object Dim edSvc As IWindowsFormsEditorService = DirectCast(provider.GetService(GetType(IWindowsFormsEditorService)), IWindowsFormsEditorService) Dim path = "" Dim btn = New Button() btn.Text = "Select CSV File..." Dim pdg = btn.Padding pdg.Bottom += 2 btn.Padding = pdg btn.Click += Sub() Using openDlg = New OpenFileDialog() openDlg.Filter = "CSV Files (*.csv)|*.csv|All Files (*.*)|*.*" If openDlg.ShowDialog() <> DialogResult.OK Then path = "" Else path = openDlg.FileName End If End Using edSvc.DropDownControl(btn) If String.IsNullOrEmpty(path) Then Return String.Empty End If If Not File.Exists(path) Then Return String.Empty End If Return GetCSVQuery(path) End Function Private Function GetCSVQuery(path As String) As Object Dim sr As StreamReader = Nothing Try sr = New StreamReader(path) Dim ret As String = String.Empty Dim currentLine As String Dim line As Integer = 0 While (InlineAssignHelper(currentLine, sr.ReadLine())) IsNot Nothing If line = 0 Then ret += ProcessColumnsDefinition(currentLine) & Convert.ToString(vbCr & vbLf) Else ret += currentLine & Convert.ToString(vbCr & vbLf) End If line += 1 End While Return ret Catch generatedExceptionName As IOException Return String.Empty Finally If sr IsNot Nothing Then sr.Close() End If End Try End Function Private Function ProcessColumnsDefinition(currentLine As String) As String Const ColumnWithDataTypeRegex As String = "[""]?\w+[\""]?\(.+\)" Dim columns As String() = currentLine.Split(New String() {","}, StringSplitOptions.None) Dim ret As String = Nothing For Each column As String In columns If Not String.IsNullOrEmpty(ret) Then ret += "," End If If Not Regex.Match(column, ColumnWithDataTypeRegex).Success Then ret += column & Convert.ToString("(string)") Else ret += column End If Next Return ret End Function Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, value As T) As T target = value Return value End Function End Class End Namespace |
C# code. Paste it to replace the default stub in the class. |
Copy Code
|
---|---|
using System; using System.Collections.Generic; using System.Drawing.Design; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Windows.Forms; using System.Windows.Forms.Design; namespace CustomDataProvider.CSVDataProvider { public sealed class QueryEditor : UITypeEditor { public override UITypeEditorEditStyle GetEditStyle(System.ComponentModel.ITypeDescriptorContext context) { return UITypeEditorEditStyle.DropDown; } public override object EditValue(System.ComponentModel.ITypeDescriptorContext context, System.IServiceProvider provider, object value) { IWindowsFormsEditorService edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService)); var path = ""; var btn = new Button(); btn.Text = "Select CSV File..."; var pdg = btn.Padding; pdg.Bottom += 2; btn.Padding = pdg; btn.Click += delegate { using (var openDlg = new OpenFileDialog()) { openDlg.Filter = "CSV Files (*.csv)|*.csv|All Files (*.*)|*.*"; if (openDlg.ShowDialog() != DialogResult.OK) path = ""; else path = openDlg.FileName; } }; edSvc.DropDownControl(btn); if (string.IsNullOrEmpty(path)) return string.Empty; if (!File.Exists(path)) return string.Empty; return GetCSVQuery(path); } private object GetCSVQuery(string path) { StreamReader sr = null; try { sr = new StreamReader(path); string ret = string.Empty; string currentLine; int line = 0; while ((currentLine = sr.ReadLine()) != null) { if (line == 0) ret += ProcessColumnsDefinition(currentLine) + "\r\n"; else ret += currentLine + "\r\n"; line++; } return ret; } catch (IOException) { return string.Empty; } finally { if (sr != null) sr.Close(); } } private string ProcessColumnsDefinition(string currentLine) { const string ColumnWithDataTypeRegex = @"[""]?\w+[\""]?\(.+\)"; string[] columns = currentLine.Split(new string[] { "," }, StringSplitOptions.None); string ret = null; foreach (string column in columns) { if (!string.IsNullOrEmpty(ret)) ret += ","; if (!Regex.Match(column, ColumnWithDataTypeRegex).Success) { ret += column + "(string)"; } else { ret += column; } } return ret; } } } |