Bite Size C# – XLS Creator

Today we will look at my XLS Creator Class. It is very similar to my CSV Creator class discussed in an earlier post (BITE SIZE C# – CSV FILE CREATOR), except that it creates a well formed XLS file instead of a CSV file.

It is a static class contained in the Core.XLS namespace and contains 1 static method ExportToExcel. Like the CSV Creator class it uses a generic list of objects to construct a XLS file. The method also takes 2 string values xlsNameWithExt and sheetName.

xlsNameWithExt which is used to define the name and location to save the created XLS file and sheetName which is used to set the sheet name the data will be inserted on.

Here is the code:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Core.XLS
{
  public static class XlsCreator
  {
  public static void ExportToExcel<T>(List<T> list, string xlsNameWithExt, string sheetName)
  {
  var columnCount = 0;

  var StartTime = DateTime.Now;

  var rowData = new StringBuilder();

  var properties = typeof (T).GetProperties(BindingFlags.Public | BindingFlags.Instance);

  rowData.Append("<Row ss:StyleID=\"s62\">");
  foreach (var p in properties)
  {
  if (p.Name.ToUpper() == "ENTITYSTATE" || p.Name.ToUpper() == "ENTITYKEY")
    continue;

  if (p.PropertyType.Name != "EntityCollection`1" && p.PropertyType.Name != "EntityReference`1")
  {
    var type = "String";
    columnCount++;
    rowData.Append("<Cell><Data ss:Type=\"" + type + "\">" + p.Name + "</Data></Cell>");
  }
  else
    break;
  }
  rowData.Append("</Row>");

  foreach (var item in list)
  {
  rowData.Append("<Row>");
  for (var x = 0; x < columnCount; x++) //each (PropertyInfo p in properties)
  {
    var o = properties[x].GetValue(item, null);
    var value = o == null ? "" : o.ToString();
    rowData.Append("<Cell><Data ss:Type=\"String\">" + value + "</Data></Cell>");
  }
  rowData.Append("</Row>");
  }

  var sheet = @"<?xml version=""1.0""?>
    <?mso-application progid=""Excel.Sheet""?>
    <Workbook xmlns=""urn:schemas-microsoft-com:office:spreadsheet""
    xmlns:o=""urn:schemas-microsoft-com:office:office""
    xmlns:x=""urn:schemas-microsoft-com:office:excel""
    xmlns:ss=""urn:schemas-microsoft-com:office:spreadsheet""
    xmlns:html=""http://www.w3.org/TR/REC-html40"">
    <DocumentProperties xmlns=""urn:schemas-microsoft-com:office:office"">
    <Author>MSADMIN</Author>
    <LastAuthor>MSADMIN</LastAuthor>
    <Created>2011-07-12T23:40:11Z</Created>
    <Company>Microsoft</Company>
    <Version>12.00</Version>
    </DocumentProperties>
    <ExcelWorkbook xmlns=""urn:schemas-microsoft-com:office:excel"">
    <WindowHeight>6600</WindowHeight>
    <WindowWidth>12255</WindowWidth>
    <WindowTopX>0</WindowTopX>
    <WindowTopY>60</WindowTopY>
    <ProtectStructure>False</ProtectStructure>
    <ProtectWindows>False</ProtectWindows>
    </ExcelWorkbook>
    <Styles>
    <Style ss:ID=""Default"" ss:Name=""Normal"">
    <Alignment ss:Vertical=""Bottom""/>
    <Borders/>
    <Font ss:FontName=""Calibri"" x:Family=""Swiss"" ss:Size=""11"" ss:Color=""#000000""/>
    <Interior/>
    <NumberFormat/>
    <Protection/>
    </Style>
    <Style ss:ID=""s62"">
    <Font ss:FontName=""Calibri"" x:Family=""Swiss"" ss:Size=""11"" ss:Color=""#000000""
    ss:Bold=""1""/>
    </Style>
    </Styles>
    <Worksheet ss:Name=""" + sheetName + @""">
    <Table ss:ExpandedColumnCount=""" + (properties.Count() + 1) + @""" ss:ExpandedRowCount=""" +
    (list.Count() + 1) + @""" x:FullColumns=""1""
    x:FullRows=""1"" ss:DefaultRowHeight=""15"">
    " + rowData + @"
    </Table>
    <WorksheetOptions xmlns=""urn:schemas-microsoft-com:office:excel"">
    <PageSetup>
    <Header x:Margin=""0.3""/>
    <Footer x:Margin=""0.3""/>
    <PageMargins x:Bottom=""0.75"" x:Left=""0.7"" x:Right=""0.7"" x:Top=""0.75""/>
    </PageSetup>
    <Print>
    <ValidPrinterInfo/>
    <HorizontalResolution>300</HorizontalResolution>
    <VerticalResolution>300</VerticalResolution>
    </Print>
    <Selected/>
    <Panes>
    <Pane>
      <Number>3</Number>
      <ActiveCol>2</ActiveCol>
    </Pane>
    </Panes>
    <ProtectObjects>False</ProtectObjects>
    <ProtectScenarios>False</ProtectScenarios>
    </WorksheetOptions>
    </Worksheet>
    <Worksheet ss:Name=""Sheet2"">
    <Table ss:ExpandedColumnCount=""1"" ss:ExpandedRowCount=""1"" x:FullColumns=""1""
    x:FullRows=""1"" ss:DefaultRowHeight=""15"">
    </Table>
    <WorksheetOptions xmlns=""urn:schemas-microsoft-com:office:excel"">
    <PageSetup>
    <Header x:Margin=""0.3""/>
    <Footer x:Margin=""0.3""/>
    <PageMargins x:Bottom=""0.75"" x:Left=""0.7"" x:Right=""0.7"" x:Top=""0.75""/>
    </PageSetup>
    <ProtectObjects>False</ProtectObjects>
    <ProtectScenarios>False</ProtectScenarios>
    </WorksheetOptions>
    </Worksheet>
    <Worksheet ss:Name=""Sheet3"">
    <Table ss:ExpandedColumnCount=""1"" ss:ExpandedRowCount=""1"" x:FullColumns=""1""
    x:FullRows=""1"" ss:DefaultRowHeight=""15"">
    </Table>
    <WorksheetOptions xmlns=""urn:schemas-microsoft-com:office:excel"">
    <PageSetup>
    <Header x:Margin=""0.3""/>
    <Footer x:Margin=""0.3""/>
    <PageMargins x:Bottom=""0.75"" x:Left=""0.7"" x:Right=""0.7"" x:Top=""0.75""/>
    </PageSetup>
    <ProtectObjects>False</ProtectObjects>
    <ProtectScenarios>False</ProtectScenarios>
    </WorksheetOptions>
    </Worksheet>
    </Workbook>";

  using (var sw = new StreamWriter(xlsNameWithExt))
  {
  sw.Write(sheet);
  }
  }
  }
}
Bite Size C# – XLS Creator

Bite Size C# – XLS to CSV Converter

Let us have a look at my XLS to CSV Converter class today.

It is a static class, which is located in the Core.CSV namespace. It contains one static method CovertExcelToCsv which takes 3 parameters excelFilePath, csvOutputFile and worksheetNumber. The excelFilePath parameter is used to input the source Excel file, csvOutputFile is used to input the destination CSV file and lastly worksheetNumber is used to define which worksheet to convert, as CSV files do not have worksheets only one worksheet can be converted to CSV file at a time. The worksheetNumber parameter has a default value of 1 in the event that no value is passed in.

using System;
using System.Data;
using System.Data.OleDb;
using System.IO;

namespace Core.CSV
{
 public static class XlsToCsvConverter
 {
  public static void CovertExcelToCsv(string excelFilePath, string csvOutputFile, int worksheetNumber = 1)
  {
   if (!File.Exists(excelFilePath)) throw new FileNotFoundException(excelFilePath);
   if (File.Exists(csvOutputFile)) throw new ArgumentException("File exists: " + csvOutputFile);

   // Connection string
   var connectionStr =
    String.Format(
     "Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Extended Properties=\"Excel 8.0;IMEX=1;HDR=NO\"",
     excelFilePath);
   var cnn = new OleDbConnection(connectionStr);

   // Get schema information and then data in spreadsheet
   var dt = new DataTable();
   try
   {
    cnn.Open();
    var schemaTable = cnn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
    if (schemaTable != null && schemaTable.Rows.Count < worksheetNumber)
     throw new ArgumentException("The worksheet number requested does not exist.");
    if (schemaTable != null)
    {
     var worksheet = schemaTable.Rows[worksheetNumber - 1]["table_name"].ToString().Replace("'", "");
     var sql = String.Format("select * from [{0}]", worksheet);
     var da = new OleDbDataAdapter(sql, cnn);
     da.Fill(dt);
    }
   }
   catch (Exception ex)
   {
    //Insert logging of exception here
    throw;
   }
   finally
   {
    // Close connection to the spreadsheet
    cnn.Close();
   }

   // Write out CSV data
   using (var wtr = new StreamWriter(csvOutputFile))
   {
    foreach (DataRow row in dt.Rows)
    {
     var firstLine = true;
     foreach (DataColumn col in dt.Columns)
     {
      if (!firstLine)
      {
       wtr.Write(",");
      }
      else
      {
       firstLine = false;
      }
      var data = row[col.ColumnName].ToString().Replace("\"", "");
      wtr.Write(String.Format("{0}", data));
     }
     wtr.WriteLine();
    }
   }
  }
 }
}
Bite Size C# – XLS to CSV Converter

Bite Size SQL – Basic Query Optimisation

How SQL queries are written can have a huge impact on how quickly and efficiently they execute. In this post I will explain some very basic methods that can be used to optimise SQL queries.

  • When writing select queries, only select the columns you need. In general using “SELECT *” is very bad practice.
  • Avoid using sub queries, rather use joins. What is meant by this is instead of using:
        SELECT COLUMNA FROM TABLEA WHERE COLUMNB IS IN (SELECT COLUMNB FROM TABLEB)
        Rather use:
        SELECT COLUMNA FROM TABLEA AS TA INNER JOIN TABLEB AS TB ON TA.COLUMNB = TB.COLUMNB
  • Use WHERE to limit the result set to only what you need. There is no point in pulling 10000 records if you are only looking for records, for example in a certain date range.
  • Use WITH NOLOCK with SELECT queries when feasible, this can prevent deadlocks which can cause serious performance issues. However note that using WITH NOLOCK can result in a non-accurate result set being returned which contains non committed records, so keep this in mind when using. But when querying a production system database this risk might be outweighed by the risk of deadlocks occuring.

One last point on stored procedures, this is not really an optimisation point as much as a best practice point. When developing stored procedures always write them to return result sets of the same dimensions. I have worked on numerous legacy systems were this was not the case and it results in a complete nightmare, especially with projects that utilise an ORM.

Bite Size SQL – Basic Query Optimisation