Introduction
This article explores the power of the DataSet in a way that you might not be familiar with. Two important (and very helpful) aspects of the DataSet are uncovered here:
- A DataSet can be used to write to Excel and read from Excel using an OleDbConnection very easily.
- A DataSet, in conjunction with an agnostic Xml Schema (not a DataSet Xml Schema) can be used to shape xml data
Background
I remember the DAO (Data Access Objects) days and the ADO Recordset days. I also remember being skeptical when Microsoft introduced yet another library to manipulate data. That was when .NET initially came out. My skepticism waned away very quickly when I started to delve under the covers of the DataSet.
Going back a few years ago, I had designed/developed an Enterprise Management Reporting system for a global company that required MSMQ driven server-side automation of Office. That was an unadvisable task, according to Microsoft - but it was achieved nonetheless (with macros and the like).
Then, I discovered the beauty of writing to and reading from Excel using an OleDbConnection. This meant I could limit the time that I was running macros to just the manipulation of data (I didn't need Excel to write to Excel anymore). That greatly improved the efficiency and reduced the risk of problems because the Excel instance was used for a much shorter time.
I also remember a time when I had to use an XmlWriter or the FOR XML clause in SQL Server to build XML Data. I also remember using the Relations collection of ADO.NET to build formatted XML. But those were my younger days, I rejoiced when I found that I could read XML Schemas into a DataSet and out came formatted XML!
Now you might say that .NET already provides you the ability with Strongly-Typed DataSets. But the problem is that you don't necessarily want .NET DataSet Xml Schemas. I have been on a few projects where the tools used to develop Xml Schema were not VS2005. Admit it, most times you need to use agnostic Xml Schemas. It is, after all, best practice to do so - isolate the presentation of the data from the retrieval of data.
By relaxing the constraints we can use the Xml Schema to build the data, then use the Xml Schema to validate the data. Kill two birds with one stone - so to speak. Enough of the sales pitch - let's take a look at what I am talking about.
Using the code
There are two main modules in this project.
| Name |
Description |
DataSetMethods |
This class has two methods:
gatherMetaData
setupDataSet
gatherMetaDat is used to gather Table/Column information and Relations within the DataSet
setupDataSet takes an agnostic Xml Schema and makes it DataSet-ready. |
ExcelFunctions |
This class contains two overloads to ReadFromExcel to a DataSet and two overloads to WriteToExcel from a DataSet. |
DataSetMethods code:
Collapse
public static DataSet gatherMetaData(DataSet ds)
{
XmlDocument xd = new XmlDocument();
xd.Load(@"Metadata Xsd\MetaData.xsd");
DataSet metaData = setupDataSet(xd, new DataSet());
for (int i = 0; i < ds.Tables.Count; i++)
{
for (int j = 0; j < ds.Tables[i].Columns.Count; j++)
{
DataRow dr = metaData.Tables["Table"].NewRow();
dr["Name"] = ds.Tables[i].TableName;
dr["Column"] = ds.Tables[i].Columns[j].ColumnName;
metaData.Tables["Table"].Rows.Add(dr);
}
}
for (int i = 0; i < ds.Relations.Count; i++)
{
for (int j = 0; j < ds.Relations[i].ParentColumns.Length; j++)
{
for (int k = 0; k < ds.Relations[i].ChildColumns.Length; k++)
{
DataRow dr = metaData.Tables["Relations"].NewRow();
dr["ParentTable"] = ds.Relations[i].ParentTable;
dr["ParentColumn"] = ds.Relations[i].ParentColumns[j].ColumnName;
dr["ChildTable"] = ds.Relations[i].ChildTable;
dr["ChildColumn"] = ds.Relations[i].ChildColumns[j].ColumnName;
metaData.Tables["Relations"].Rows.Add(dr);
}
}
}
return metaData;
}
public static DataSet setupDataSet(XmlDocument xmlSchema, DataSet ds)
{
XmlAttribute xa = xmlSchema.CreateAttribute("xmlns:msdata", "http://www.w3.org/2000/xmlns/");
xa.Value = "urn:schemas-microsoft-com:xml-msdata";
XmlNode schemaNode = xmlSchema.SelectSingleNode("descendant::*[local-name() = 'schema']");
schemaNode.Attributes.Append(xa);
xa = xmlSchema.CreateAttribute("msdata:IsDataSet", "urn:schemas-microsoft-com:xml-msdata");
schemaNode.SelectSingleNode("*[local-name() = 'element']").Attributes.Append(xa);
StringReader sr = new StringReader(xmlSchema.OuterXml);
XmlReader xr = XmlReader.Create(sr);
ds.ReadXmlSchema(xr);
ds.EnforceConstraints = false;
return ds;
}
ExcelFunctions code:
Collapse
#region Constants
private const string _excelConnectionString =
"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=\"{0}\";User ID=" +
"Admin;Password=;Extended Properties=\"Excel 8.0;HDR=YES\";";
private const string _excelSelect = "select * from [{0}$]";
private const string _tableName = "TABLE_NAME";
private const string _tableCreate = "CREATE TABLE [{0}] (";
private const string _tableColumn = "[{0}] {1}{2}";
#endregion
#region Private Methods
private static string getColumnType(DataColumn dc)
{
string columnType = "TEXT";
switch (dc.DataType.ToString())
{
case "System.Int64" :
case "System.Double":
case "System.Int32" :
columnType = "NUMERIC";
break;
default:
columnType = "TEXT";
break;
}
return columnType;
}
#endregion
#region Public Methods
#region WriteToExcel(DataSet ds)
public static void WriteToExcel(DataSet ds)
{
WriteToExcel(ds,ds.DataSetName + ".xls",false);
}
#endregion
#region WriteToExcel(DataSet ds, String fileName, bool append)
public static void WriteToExcel(DataSet ds, String fileName, bool append)
{
string excelConnectionString = string.Format(_excelConnectionString, fileName);
OleDbConnection excelFile = null;
OleDbCommand excelCmd = null;
OleDbDataAdapter excelDataAdapter = null;
OleDbCommandBuilder excelCommandBuilder = null;
StringBuilder sb = null;
try
{
if (File.Exists(fileName) && !append) File.Delete(fileName);
excelFile = new OleDbConnection(excelConnectionString);
excelFile.Open();
foreach (DataTable dt in ds.Tables)
{
if (!File.Exists(fileName) || !append)
{
sb = new StringBuilder();
sb.AppendFormat(_tableCreate, dt.TableName);
foreach (DataColumn dc in ds.Tables[dt.TableName].Columns)
{
sb.AppendFormat(_tableColumn, dc.ColumnName,
getColumnType(dc)
, (dc.Ordinal == dt.Columns.Count - 1 ?
")" : ","));
}
excelCmd = new OleDbCommand(sb.ToString(), excelFile);
excelCmd.ExecuteNonQuery();
}
excelDataAdapter = new OleDbDataAdapter(string.Format(_excelSelect,dt.TableName), excelFile);
excelCommandBuilder = new OleDbCommandBuilder(excelDataAdapter);
excelCommandBuilder.QuotePrefix = "[";
excelCommandBuilder.QuoteSuffix = "]";
excelDataAdapter.InsertCommand = excelCommandBuilder.GetInsertCommand();
excelDataAdapter.Update(ds, dt.TableName);
}
}
finally
{
sb = null;
excelDataAdapter = null;
excelCommandBuilder = null;
excelCmd = null;
excelFile.Close();
excelFile.Dispose();
excelFile = null;
}
}
#endregion
#region ReadFromExcel(string fileName)
public static DataSet ReadFromExcel(string fileName)
{
return ReadFromExcel(fileName, new DataSet());
}
#endregion
#region ReadFromExcel(string fileName, DataSet ds)
public static DataSet ReadFromExcel(string fileName, DataSet ds)
{
string excelConnectionString = string.Format(_excelConnectionString, fileName);
OleDbConnection excelFile;
DataTable schemaTable;
OleDbDataAdapter excelDataAdapter;
try
{
excelFile = new OleDbConnection(excelConnectionString);
excelFile.Open();
schemaTable = excelFile.GetOleDbSchemaTable(OleDbSchemaGuid.Tables,
new object[] { null, null, null, "TABLE" });
foreach (DataRow dr in schemaTable.Rows)
{
excelDataAdapter = new OleDbDataAdapter(dr[_tableName].ToString(), excelFile);
excelDataAdapter.SelectCommand.CommandType = CommandType.TableDirect;
excelDataAdapter.AcceptChangesDuringFill = false;
if (dr[_tableName].ToString().EndsWith("$"))
excelDataAdapter.Fill(ds, dr[_tableName].ToString().Replace("$", string.Empty));
}
excelFile.Close();
}
finally
{
excelDataAdapter = null;
schemaTable = null;
excelFile = null;
}
return ds;
}
#endregion
#endregion