Excel Tool to compare two Excel Sheets - c#

This is my current code. Open to receive any comments to improve the memory optimization.
When I am taking a sample of 1000000 * 8 with 1000000*8 data its resulting into out of memory exception. Would love to have advice on optimizing memory usage.
Compare The two tables in a data set named "Before" and "After" and fill all result tables.
private bool CompareAndFillResultTable(DataSet ds)
{
Stopwatch stopWatch = new Stopwatch(); stopWatch.Start();
System.Data.DataTable dt_copy;
dt_copy = new System.Data.DataTable();
dt_copy = ds.Tables["Before"].Copy();
dt_copy.TableName = "BeforeBackup";
ds.Tables.Add(dt_copy);
dt_copy = new System.Data.DataTable();
dt_copy = ds.Tables["After"].Copy();
dt_copy.TableName = "AfterBackup";
ds.Tables.Add(dt_copy);
dt_copy = new System.Data.DataTable();
dt_copy = ds.Tables["Before"].Clone();
dt_copy.TableName = "BeforeSingular";
ds.Tables.Add(dt_copy);
dt_copy = new System.Data.DataTable();
dt_copy = ds.Tables["Before"].Clone();
dt_copy.TableName = "AfterSingular";
ds.Tables.Add(dt_copy);
dt_copy = new System.Data.DataTable();
dt_copy = ds.Tables["Before"].Clone();
dt_copy.TableName = "Duplicates";
ds.Tables.Add(dt_copy);
dt_copy = new System.Data.DataTable();
dt_copy = ds.Tables["Before"].Clone();
dt_copy.TableName = "Mismatch";
ds.Tables.Add(dt_copy);
foreach (System.Data.DataTable table in ds.Tables)
{
table.Columns.Add("Source_Label");
}
//Remove identical from before, then after
for (int i = 0; i < ds.Tables["Before"].Rows.Count; i++)
{
string BeforeCompareKeyVal = ds.Tables["Before"].Rows[i][Inputs.SortColumn].ToString();
if (ds.Tables["After"].Rows.Count > 0)
{
for (int j = 0; j < ds.Tables["After"].Rows.Count; j++)
{
string AfterCompareKeyVal = ds.Tables["After"].Rows[j][Inputs.SortColumn].ToString();
if (ds.Tables["Before"].Rows[i].ItemArray.SequenceEqual(ds.Tables["After"].Rows[j].ItemArray))
{
//copy Aftter row to duplicate Table and Remove row from After
DataRow rw = ds.Tables["After"].Rows[j];
rw[ds.Tables["After"].Columns.Count - 1] = "NA";
ds.Tables["Duplicates"].ImportRow(rw);
ds.Tables["After"].Rows.RemoveAt(j);
j--;
break;
}
if (Int64.Parse(BeforeCompareKeyVal) > Int64.Parse(AfterCompareKeyVal))// Review - 7
{
if (true)//all dup after + a before - set logic
{
//Copy After row to AfterSingular Table and Remove row from After
DataRow rw = ds.Tables["After"].Rows[j];
rw[ds.Tables["After"].Columns.Count - 1] = "After";
ds.Tables["AfterSingular"].ImportRow(rw);
ds.Tables["After"].Rows.RemoveAt(j);
j--;
if (ds.Tables["After"].Rows.Count == 0)
{
rw = ds.Tables["Before"].Rows[i];
rw[ds.Tables["Before"].Columns.Count - 1] = "Before";
ds.Tables["BeforeSingular"].ImportRow(rw);
}
continue;
}
}
if (Int64.Parse(BeforeCompareKeyVal) < Int64.Parse(AfterCompareKeyVal))// Review - 7
{
if (true)//all dup after and a before set logic
{
//Copy Before row to BeforeSingular Table
DataRow rw = ds.Tables["Before"].Rows[i];
rw[ds.Tables["Before"].Columns.Count - 1] = "Before";
ds.Tables["BeforeSingular"].ImportRow(rw);
break;
}
}
if (Int64.Parse(BeforeCompareKeyVal) == Int64.Parse(AfterCompareKeyVal))// Review - 7
{
//Copy Before, After row to Mismatch Table and Remove row from After
if (true)//all dup after and a before set logic
{
DataRow rwB = ds.Tables["Before"].Rows[i];
rwB[ds.Tables["Before"].Columns.Count - 1] = "Before";
DataRow rwA = ds.Tables["After"].Rows[j];
rwA[ds.Tables["After"].Columns.Count - 1] = "After";
ds.Tables["Mismatch"].ImportRow(rwB);
ds.Tables["Mismatch"].ImportRow(rwA);
ds.Tables["After"].Rows.RemoveAt(j);
j--;
break;
}
}
}
}
else
{
DataRow rw = ds.Tables["Before"].Rows[i];
rw[ds.Tables["Before"].Columns.Count - 1] = "Before";
ds.Tables["BeforeSingular"].ImportRow(rw);
continue;
}
}
//Add remaining after table rows to AfterSingular table
ds.Tables["AfterSingular"].Merge(ds.Tables["After"]);
//ds.Tables["AfterSingular"].Columns.Add("Source_Label", System.Type.GetType("System.String"), "After_Singular");
//ds.Tables["BeforeSingular"].Columns.Add("Source_Label", System.Type.GetType("System.String"), "Before_Singular");
//foreach (System.Data.DataTable table in ds.Tables)
//{
// DataRow colNames = table.NewRow();
// //foreach (var col in table.Columns)
// //{
// //}
// for (int i = 0; i < table.Columns.Count; i++)
// colNames[i] = table.Columns[i].ColumnName;
// table.Rows.InsertAt(colNames, 0);
//}
foreach (System.Data.DataTable table in ds.Tables)
{
table.Columns.Remove(Inputs.SortColumn);
table.AcceptChanges();
}
stopWatch.Stop(); lbAlert.Text = lbAlert.Text + "\n\n" + "Total Comparison time for B: " + Inputs.RowNoBeforeTable + " x " + Inputs.ColumnNoBeforeTable + " A: " + Inputs.RowNoAfterTable + " x " + Inputs.ColumnNoAfterTable + " is " + stopWatch.ElapsedMilliseconds + " ms, " + stopWatch.ElapsedMilliseconds / 1000 + " s";
return true;
}

Can you not just use a VBA script to do this kind of thing?
Option Explicit
Sub test()
Dim varSheetA As Variant
Dim varSheetB As Variant
Dim strRangeToCheck As String
Dim iRow As Long
Dim iCol As Long
strRangeToCheck = "A1:IV65536"
' If you know the data will only be in a smaller range, reduce the size of the ranges above.
Debug.Print Now
varSheetA = Worksheets("Sheet1").Range(strRangeToCheck)
varSheetB = Worksheets("Sheet2").Range(strRangeToCheck) ' or whatever your other sheet is.
Debug.Print Now
For iRow = LBound(varSheetA, 1) To UBound(varSheetA, 1)
For iCol = LBound(varSheetA, 2) To UBound(varSheetA, 2)
If varSheetA(iRow, iCol) = varSheetB(iRow, iCol) Then
' Cells are identical.
' Do nothing.
Else
' Cells are different.
' Code goes here for whatever it is you want to do.
Cells(iRow, iCol).Select
With Selection.Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.Color = 49407
.TintAndShade = 0
.PatternTintAndShade = 0
End With
End If
Next iCol
Next iRow
End Sub

Related

Create an Excel file with the Excel interop class from SQL Database

I want to create an Excel file with the Excel interop class from the SQL Database. With SQL query, I had previously transferred the dataset into dataGridview. The result of the query is the data. However, I can't Print this data from the SQL database to Excel cells with the dataset.
private void linkLabel10_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
using (var fbd = new FolderBrowserDialog())
{
DialogResult result = fbd.ShowDialog();
if (result == DialogResult.OK && !string.IsNullOrWhiteSpace(fbd.SelectedPath))
{
string fileTest = fbd.SelectedPath.ToString() + "\\"+ comboBox1.Text.Substring(0, comboBox1.Text.IndexOf("*") - 1) +"-"+ comboBox2.Text.Substring(0, comboBox2.Text.IndexOf("(") - 1) +"-"+ comboBox2.Text.Substring(comboBox2.Text.IndexOf("(") + 1, 10) + ".xlsx";
MessageBox.Show(fileTest);
if (File.Exists(fileTest))
{
File.Delete(fileTest);
}
SqlDataAdapter da = new SqlDataAdapter("select PersonelKodu= ztSinifEgitimiDurum.KisiID, İsim= dbo.cdCurrAcc.FullName, Katılım = CASE WHEN Katilim = 1 THEN 'Katıldı' ELSE 'Katılmadı' END, ztSinifEgitimiDurum.ID FROM dbo.ztSinifEgitimiDurum INNER JOIN dbo.cdCurrAcc ON cdCurrAcc.CurrAccCode = ztSinifEgitimiDurum.KisiID AND cdCurrAcc.CurrAccTypeCode = ztSinifEgitimiDurum.KisiTipiID WHERE AtamaID =" + AtamaIDBul(), baglan);
DataSet ds = new DataSet();
Excel.Application Excel;
Excel.Worksheet excelWorkSheet;
Excel.Workbook excelWorkBook;
Excel = new Excel.Application();
excelWorkBook = Excel.Workbooks.Add();
excelWorkSheet = (Excel.Worksheet)excelWorkBook.Worksheets.get_Item(1);
excelWorkSheet.Cells[1, 1] = "some value";
excelWorkSheet.Name = "Ali";
foreach (DataTable table in ds.Tables)
{
for (int i = 1; i < table.Columns.Count + 1; i++)
{
excelWorkSheet.Cells[1, i] = table.Columns[i - 1].ColumnName;
MessageBox.Show(table.Columns[i - 1].ColumnName.ToString());
}
for (int j = 0; j < table.Rows.Count; j++)
{
for (int k = 0; k < table.Columns.Count; k++)
{
excelWorkSheet.Cells[j + 2, k + 1] = table.Rows[j].ItemArray[k].ToString();
}
}
}
// excelWorkSheet.Cells[1, 1] = "some value";
excelWorkBook.SaveAs(fileTest);
excelWorkBook.Close();
Excel.Quit();
}
}
}
You forgot to fill your DataSet, add this line
da.Fill(ds); after DataSet ds = new DataSet();
You can try this code to fill your sheet with values from a table:
ws.Activate();
foreach (DataTable dt in ds.Tables)
{
// Add column headers from the datatable
for (int Idx = 0; Idx < dt.Columns.Count; Idx++)
{
ws.Range["A1"].Offset[0, Idx].Value = dt.Columns[Idx].ColumnName;
}
// add data rows
for (int Idx = 0; Idx < dt.Rows.Count; Idx++)
{ // hey I did not invent this line of code, I found it somewhere on CodeProject.
// It works to add the whole row at once
ws.Range["A2"].Offset[Idx].Resize[1, dt.Columns.Count].Value = dt.Rows[Idx].ItemArray;
}
...
But you will have to add more worksheets for individual tables in case there is more than one table in your DataSet.
It is based on this article.

SSIS Export to Excel using Script Task

I'm trying to use a Script Task to export data to Excel because some of the reports I generate simply have too many columns to keep using a template file.
The most annoying part about using a template is: if something as simple as a column header changes, the metadata gets screwed forcing me to recreate my DataFlow. Because I use an OLE DB source, I need to use a Data Transformation task to convert between unicode and non-unicode character sets, then remap my Excel Destination to the "Copy of field x" in order for the Excel document to create properly.
This takes far too long and I need a new approach.
I have the following method in a script task using Excel = Microsoft.Office.Interop.Excel:
private void ExportToExcel(DataTable dataTable, string excelFilePath = null)
{
Excel.Application excelApp = new Excel.Application();
Excel.Worksheet workSheet = null;
try
{
if (dataTable == null || dataTable.Columns.Count == 0)
throw new System.Exception("Null or empty input table!" + Environment.NewLine);
excelApp.Workbooks.Add();
workSheet = excelApp.ActiveSheet;
for (int i = 0; i < dataTable.Columns.Count; i++)
{
workSheet.Cells[1, (i + 1)] = dataTable.Columns[i].ColumnName;
}
foreach (DataTable dt in dataSet.Tables)
{
// Copy the DataTable to an object array
object[,] rawData = new object[dt.Rows.Count + 1, dt.Columns.Count];
// Copy the column names to the first row of the object array
for (int col = 0; col < dt.Columns.Count; col++)
{
rawData[0, col] = dt.Columns[col].ColumnName;
}
// Copy the values to the object array
for (int col = 0; col < dt.Columns.Count; col++)
{
for (int row = 0; row < dt.Rows.Count; row++)
{
rawData[row + 1, col] = dt.Rows[row].ItemArray[col];
}
}
// Calculate the final column letter
string finalColLetter = string.Empty;
string colCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int colCharsetLen = colCharset.Length;
if (dt.Columns.Count > colCharsetLen)
{
finalColLetter = colCharset.Substring((dt.Columns.Count - 1) / colCharsetLen - 1, 1);
}
finalColLetter += colCharset.Substring((dt.Columns.Count - 1) % colCharsetLen, 1);
workSheet.Name = dt.TableName;
// Fast data export to Excel
string excelRange = string.Format("A1:{0}{1}", finalColLetter, dt.Rows.Count + 1);
//The code crashes here (ONLY in SSIS):
workSheet.get_Range(excelRange, Type.Missing).Value2 = rawData;
// Mark the first row as BOLD
((Excel.Range)workSheet.Rows[1, Type.Missing]).Font.Bold = true;
}
List<int> lstColumnsToSum = new List<int>() { 9 };
Dictionary<int, string> dictColSumName = new Dictionary<int, string>() { { 9, "" } };
Dictionary<int, decimal> dictColumnSummation = new Dictionary<int, decimal>() { { 9, 0 } };
// rows
for (int i = 0; i < dataTable.Rows.Count; i++)
{
for (int j = 1; j <= dataTable.Columns.Count; j++)
{
workSheet.Cells[(i + 2), (j)] = dataTable.Rows[i][j - 1];
if (lstColumnsToSum.Exists(x => (x == j)))
{
decimal val = 0;
if (decimal.TryParse(dataTable.Rows[i][j - 1].ToString(), out val))
{
dictColumnSummation[j] += val;
}
}
}
}
//Footer
int footerRowIdx = 2 + dataTable.Rows.Count;
foreach (var summablecolumn in dictColSumName)
{
workSheet.Cells[footerRowIdx, summablecolumn.Key] = String.Format("{0}", dictColumnSummation[summablecolumn.Key]);
}
// check fielpath
if (excelFilePath != null && excelFilePath != "")
{
try
{
if (File.Exists(excelFilePath))
File.Delete(excelFilePath);
workSheet.Activate();
workSheet.Application.ActiveWindow.SplitRow = 1;
workSheet.Application.ActiveWindow.FreezePanes = true;
int row = 1;
int column = 1;
foreach (var item in dataTable.Columns)
{
Excel.Range range = workSheet.Cells[row, column] as Excel.Range;
range.NumberFormat = "#";
range.EntireColumn.AutoFit();
range.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightGray);
column++;
}
Excel.Range InternalCalculatedAmount = workSheet.Cells[1, 9] as Excel.Range;
InternalCalculatedAmount.EntireColumn.NumberFormat = "#0.00";
InternalCalculatedAmount.Columns.AutoFit();
workSheet.SaveAs(excelFilePath);
}
catch (System.Exception ex)
{
throw new System.Exception("Excel file could not be saved! Check filepath." + Environment.NewLine + ex.Message);
}
}
else // no filepath is given
{
excelApp.Visible = true;
}
}
catch (System.Exception ex)
{
throw new System.Exception("ex.Message + Environment.NewLine, ex.InnerException);
}
}
The exception thrown is a System.OutOfMemoryException when trying to execute the following piece of code:
workSheet.get_Range(excelRange, Type.Missing).Value2 = rawData;
My biggest frustration is that this method works 100% in a regular C# application.
The DataTable contains about 435000 rows. I know it's quite a bit of data but I use this very method, modified of course, to split data across multiple Excel worksheets in one of my other applications, and that DataSet contains about 1.1m rows. So less than half of my largest DataSet should be a walk-in-the-park...
Any light shed on this matter would be amazing!

Export Sql data to Excel , Store long int to string in excel

I have this working C# code to export Sql Server data to excel. The problem is one column contains long int and it appears in excel as 6.71524E+11. So I understand that we have to convert it as string in excel.
How to implement that in my code? Examples would be appreciated.
public static void ExportToExcel(DataTable dt)
{
try
{
string conString = "Data Source=DELL\\SQLSERVER1;Trusted_Connection=True;DATABASE=Camo;CONNECTION RESET=FALSE";
SqlConnection sqlCon = new SqlConnection(conString);
sqlCon.Open();
SqlDataAdapter da = new SqlDataAdapter("select TOP 10000 LocalSKU,ItemName, QOH,Price,Discontinued,Barcode,Integer2,Integer3,SalePrice,SaleOn,Price2 from dbo.Inventory", sqlCon);
System.Data.DataTable dtMainSQLData = new System.Data.DataTable();
da.Fill(dtMainSQLData);
DataColumnCollection dcCollection = dtMainSQLData.Columns;
// Export Data into EXCEL Sheet
Microsoft.Office.Interop.Excel.ApplicationClass ExcelApp = new Microsoft.Office.Interop.Excel.ApplicationClass();
ExcelApp.Application.Workbooks.Add(Type.Missing);
int i = 1;
int j = 1;
//header row
foreach (DataColumn col in dtMainSQLData.Columns)
{
ExcelApp.Cells[i, j] = col.ColumnName;
j++;
}
i++;
//data rows
foreach (DataRow row in dtMainSQLData.Rows)
{
for (int k = 1; k < dtMainSQLData.Columns.Count + 1; k++)
{
ExcelApp.Cells[i, k] = row[k - 1].ToString();
}
i++;
}
ExcelApp.ActiveWorkbook.SaveCopyAs("C:/Users/Administrator.CAMO/Downloads/FtpFilesStorage/Export/Sheet1.xlsx");
ExcelApp.ActiveWorkbook.Saved = true;
ExcelApp.Quit();
Console.WriteLine(".xlsx file Exported succssessfully.");
}
This method work for exporting to an Excel file.
private void ExportToExcel(DataTable Tbl, string ExcelFilePath = null)
{
try
{
if (Tbl == null || Tbl.Columns.Count == 0)
throw new Exception("ExportToExcel: Null or empty input table!\n");
// load excel, and create a new workbook
Excel.Application excelApp = new Excel.Application();
//Excel.Workbook ExcelBookServices = excelApp.Workbooks.Add();
excelApp.Workbooks.Add();
// single worksheet
Excel._Worksheet workSheet = (Excel._Worksheet)excelApp.ActiveSheet;
// column headings
for (int i = 0; i < Tbl.Columns.Count; i++)
{
workSheet.Cells[1, (i + 1)] = Tbl.Columns[i].ColumnName;
}
// rows
for (int i = 0; i < Tbl.Rows.Count; i++)
{
// to do: format datetime values before printing
for (int j = 0; j < Tbl.Columns.Count; j++)
{
workSheet.Cells[(i + 2), (j + 1)] = Tbl.Rows[i][j];
}
}
workSheet.Columns.AutoFit();
//workSheet.Columns.Style = "Output";
//Excel.Range cell = ((Excel.Range)workSheet.Cells[Tbl.Rows.Count, Tbl.Columns.Count]);
// check fielpath
if (ExcelFilePath != null && ExcelFilePath != "")
{
try
{
workSheet.SaveAs(ExcelFilePath);
excelApp.Quit();
//File Saved Message
}
catch (Exception ex)
{
//ExportToExcel: Excel file could not be saved! Check filepath Message
}
}
else // no filepath is given
{
excelApp.Visible = true;
}
}
catch (Exception ex)
{
//Error in creating the Excel file
ScriptManager.RegisterStartupScript(this, GetType(), "Message", "ExportToExcel: \n" + ex.Message, true);
}
}
Change the for loop to
int i = 1;
int j = 1;
//header row
foreach (DataColumn col in dtMainSQLData.Columns)
{
ExcelApp.Cells[i, j] = col.ColumnName;
j++;
ExcelApp.Rows.AutoFit();
ExcelApp.Columns.AutoFit();
}
i++;
Console.Write("Progressing......65% \n Wait for around 8 minutes \r");
//data rows
foreach (DataRow row in dtMainSQLData.Rows)
{
for (int k = 1; k < dtMainSQLData.Columns.Count + 1; k++)
{
ExcelApp.Cells[i, k] = "'" + row[k - 1].ToString();
}
i++;
ExcelApp.Columns.AutoFit();
ExcelApp.Rows.AutoFit();
}
use the property NumberFormat:
MyWorkBook.NumberFormat = "#";

c# Excelpackage reading cell range into a datatable

Using C# I can manage to read in range of cells using Excel interop. But it's having trouble keeping consistent with dollar values and percentages. So I'm trying out EEPLus.
FileInfo newFile = new FileInfo(#excelFilePath);
ExcelPackage pck = new ExcelPackage(newFile);
var Summary = workbook1.Worksheets[1];
and then using the following
Convert.ToString(Summary.Cells["I35"].Value);
I get a decent value.
But what I would like to do is something the following
Summary.Cells["E29:P32"].Value
and put that into a datatable. There is a method for ImportDataTable but it moves from datatable to excel. Does something similarly simple exist for excel to datatable?
Cheers.
There is no built in method, but you can try something like this:
var range = summary.Cells["E6:G7"];
DataTable tbl = GetDataTableFromRange(range);
and GetDataTableFromRange method:
public static DataTable GetDataTableFromRange(ExcelRange range)
{
DataTable tbl = new DataTable();
tbl.Columns.Add("Column1");
tbl.Columns.Add("Column2");
tbl.Columns.Add("Column3");
int dataTableColumn = 0;
DataRow newRow = null;
int currRow = -1;
foreach (var item in range)
{
if (currRow != item.Start.Row)
{
newRow = tbl.NewRow();
tbl.Rows.Add(newRow);
dataTableColumn = 0;
currRow = item.Start.Row;
}
newRow[dataTableColumn] = item.Value.ToString();
dataTableColumn++;
}
return tbl;
}
You can do like this:
public bool readXLS(string FilePath)
{
FileInfo existingFile = new FileInfo(FilePath);
using (ExcelPackage package = new ExcelPackage(existingFile))
{
//get the first worksheet in the workbook
ExcelWorksheet worksheet = package.Workbook.Worksheets[1];
int colCount = worksheet.Dimension.End.Column; //get Column Count
int rowCount = worksheet.Dimension.End.Row; //get row count
string queryString = "INSERT INTO tableName VALUES"; //Here I am using "blind insert". You can specify the column names Blient inset is strongly not recommanded
string eachVal = "";
bool status;
for (int row = 1; row <= rowCount; row++)
{
queryString += "(";
for (int col = 1; col <= colCount; col++)
{
eachVal = worksheet.Cells[row, col].Value.ToString().Trim();
queryString += "'" + eachVal + "',";
}
queryString = queryString.Remove(queryString.Length - 1, 1); //removing last comma (,) from the string
if (row % 1000 == 0) //On every 1000 query will execute, as maximum of 1000 will be executed at a time.
{
queryString += ")";
status = this.runQuery(queryString); //executing query
if (status == false)
return status;
queryString = "INSERT INTO tableName VALUES";
}
else
{
queryString += "),";
}
}
queryString = queryString.Remove(queryString.Length - 1, 1); //removing last comma (,) from the string
status = this.runQuery(queryString); //executing query
return status;
}
}
Source: http://sforsuresh.in/read-data-excel-sheet-insert-database-table-c/
I'ill do like this to find the range and store the values in database.
bool hasHeader = true;
using (var pck = new OfficeOpenXml.ExcelPackage(file.InputStream))
{
var ws = pck.Workbook.Worksheets.First();
DataTable tbl = new DataTable();
foreach (var firstRowCell in ws.Cells[1, 1, 1, ws.Dimension.End.Column])
{
if (firstRowCell.Text != "" && firstRowCell.Text != null)
{
tbl.Columns.Add(hasHeader ? firstRowCell.Text : string.Format("Column {0}", firstRowCell.Start.Column));
}
else
{
break;
}
}
var startRow = hasHeader ? 2 : 1;
for (int rowNum = startRow; rowNum <= ws.Dimension.End.Row; rowNum++)
{
var wsRow = ws.Cells[rowNum, 1, rowNum, ws.Dimension.End.Column];
DataRow row = tbl.Rows.Add();
foreach (var cell in wsRow)
{
if (cell.Text != "" && cell.Text != null)
row[cell.Start.Column - 1] = cell.Text;
}
}
}

How to convert a DataTable to a string in C#?

I'm using Visual Studio 2005 and have a DataTable with two columns and some rows that I want to output to the console. I hoped there would be something like:
DataTable results = MyMethod.GetResults();
Console.WriteLine (results.ToString());
What's the best way (i.e. least amount of coding from me) to convert a simple DataTable to a string?
Prerequisite
using System.Linq;
then ...
string res = string.Join(Environment.NewLine,
results.Rows.OfType<DataRow>().Select(x => string.Join(" ; ", x.ItemArray)));
Late but this is what I use
public static string ConvertDataTableToString(DataTable dataTable)
{
var output = new StringBuilder();
var columnsWidths = new int[dataTable.Columns.Count];
// Get column widths
foreach (DataRow row in dataTable.Rows)
{
for(int i = 0; i < dataTable.Columns.Count; i++)
{
var length = row[i].ToString().Length;
if (columnsWidths[i] < length)
columnsWidths[i] = length;
}
}
// Get Column Titles
for (int i = 0; i < dataTable.Columns.Count; i++)
{
var length = dataTable.Columns[i].ColumnName.Length;
if (columnsWidths[i] < length)
columnsWidths[i] = length;
}
// Write Column titles
for (int i = 0; i < dataTable.Columns.Count; i++)
{
var text = dataTable.Columns[i].ColumnName;
output.Append("|" + PadCenter(text, columnsWidths[i] + 2));
}
output.Append("|\n" + new string('=', output.Length) + "\n");
// Write Rows
foreach (DataRow row in dataTable.Rows)
{
for (int i = 0; i < dataTable.Columns.Count; i++)
{
var text = row[i].ToString();
output.Append("|" + PadCenter(text,columnsWidths[i] + 2));
}
output.Append("|\n");
}
return output.ToString();
}
private static string PadCenter(string text, int maxLength)
{
int diff = maxLength - text.Length;
return new string(' ', diff/2) + text + new string(' ', (int) (diff / 2.0 + 0.5));
}
using(var writer = new StringWriter()) {
results.WriteXml(writer);
Console.WriteLine(writer.ToString());
}
Of course the usefulness of this depends on how important the formatting is. If it's just a debug dump, I find XML outputs like this very readable. However, if the formatting is important to you, then you have no choice but to write your own method to do it.
You could use something like this:
Private Sub PrintTableOrView(ByVal table As DataTable, ByVal label As String)
Dim sw As System.IO.StringWriter
Dim output As String
Console.WriteLine(label)
' Loop through each row in the table. '
For Each row As DataRow In table.Rows
sw = New System.IO.StringWriter
' Loop through each column. '
For Each col As DataColumn In table.Columns
' Output the value of each column's data.
sw.Write(row(col).ToString() & ", ")
Next
output = sw.ToString
' Trim off the trailing ", ", so the output looks correct. '
If output.Length > 2 Then
output = output.Substring(0, output.Length - 2)
End If
' Display the row in the console window. '
Console.WriteLine(output)
Next
Console.WriteLine()
End Sub
i know i'm years late xD but Here's how i did it
public static string convertDataTableToString(DataTable dataTable)
{
string data = string.Empty;
for (int i = 0; i < dataTable.Rows.Count; i++)
{
DataRow row = dataTable.Rows[i];
for (int j = 0; j < dataTable.Columns.Count; j++)
{
data += dataTable.Columns[j].ColumnName + "~" + row[j];
if (j == dataTable.Columns.Count - 1)
{
if (i != (dataTable.Rows.Count - 1))
data += "$";
}
else
data += "|";
}
}
return data;
}
If someone ever optimizes this please let me know
i tried this :
public static string convertDataTableToString(DataTable dataTable)
{
string data = string.Empty;
int rowsCount = dataTable.Rows.Count;
for (int i = 0; i < rowsCount; i++)
{
DataRow row = dataTable.Rows[i];
int columnsCount = dataTable.Columns.Count;
for (int j = 0; j < columnsCount; j++)
{
data += dataTable.Columns[j].ColumnName + "~" + row[j];
if (j == columnsCount - 1)
{
if (i != (rowsCount - 1))
data += "$";
}
else
data += "|";
}
}
return data;
}
but this answer says it's worse
I would install PowerShell. It understands .NET objects and has an Format-Table and Export-Csv that would do exactly what you are looking for. If you do any sort of console work it is a great complement/replacement to C# console apps.
When I started using it, I rewrote my console apps as libraries and import the libraries into Powershell. The built-in commandlets make console work so nice.
two for loops, one for rows, another for columns, output dataRow(i).Value.
Watch out for nulls and DbNulls.
/// <summary>
/// Dumps the passed DataSet obj for debugging as list of html tables
/// </summary>
/// <param name="msg"> the msg attached </param>
/// <param name="ds"> the DataSet object passed for Dumping </param>
/// <returns> the nice looking dump of the DataSet obj in html format</returns>
public static string DumpHtmlDs(string msg, ref System.Data.DataSet ds)
{
StringBuilder objStringBuilder = new StringBuilder();
objStringBuilder.AppendLine("<html><body>");
if (ds == null)
{
objStringBuilder.AppendLine("Null dataset passed ");
objStringBuilder.AppendLine("</html></body>");
WriteIf(objStringBuilder.ToString());
return objStringBuilder.ToString();
}
objStringBuilder.AppendLine("<p>" + msg + " START </p>");
if (ds != null)
{
if (ds.Tables == null)
{
objStringBuilder.AppendLine("ds.Tables == null ");
return objStringBuilder.ToString();
}
foreach (System.Data.DataTable dt in ds.Tables)
{
if (dt == null)
{
objStringBuilder.AppendLine("ds.Tables == null ");
continue;
}
objStringBuilder.AppendLine("<table>");
//objStringBuilder.AppendLine("================= My TableName is " +
//dt.TableName + " ========================= START");
int colNumberInRow = 0;
objStringBuilder.Append("<tr><th>row number</th>");
foreach (System.Data.DataColumn dc in dt.Columns)
{
if (dc == null)
{
objStringBuilder.AppendLine("DataColumn is null ");
continue;
}
objStringBuilder.Append(" <th> |" + colNumberInRow.ToString() + " | ");
objStringBuilder.Append( dc.ColumnName.ToString() + " </th> ");
colNumberInRow++;
} //eof foreach (DataColumn dc in dt.Columns)
objStringBuilder.Append("</tr>");
int rowNum = 0;
foreach (System.Data.DataRow dr in dt.Rows)
{
objStringBuilder.Append("<tr><td> row - | " + rowNum.ToString() + " | </td>");
int colNumber = 0;
foreach (System.Data.DataColumn dc in dt.Columns)
{
objStringBuilder.Append(" <td> |" + colNumber + "|" );
objStringBuilder.Append(dr[dc].ToString() + " </td>");
colNumber++;
} //eof foreach (DataColumn dc in dt.Columns)
rowNum++;
objStringBuilder.AppendLine(" </tr>");
} //eof foreach (DataRow dr in dt.Rows)
objStringBuilder.AppendLine("</table>");
objStringBuilder.AppendLine("<p>" + msg + " END </p>");
} //eof foreach (DataTable dt in ds.Tables)
} //eof if ds !=null
else
{
objStringBuilder.AppendLine("NULL DataSet object passed for debugging !!!");
}
return objStringBuilder.ToString();
}
public static string DataTable2String(DataTable dataTable)
{
StringBuilder sb = new StringBuilder();
if (dataTable != null)
{
string seperator = " | ";
#region get min length for columns
Hashtable hash = new Hashtable();
foreach (DataColumn col in dataTable.Columns)
hash[col.ColumnName] = col.ColumnName.Length;
foreach (DataRow row in dataTable.Rows)
for (int i = 0; i < row.ItemArray.Length; i++)
if (row[i] != null)
if (((string)row[i]).Length > (int)hash[dataTable.Columns[i].ColumnName])
hash[dataTable.Columns[i].ColumnName] = ((string)row[i]).Length;
int rowLength = (hash.Values.Count + 1) * seperator.Length;
foreach (object o in hash.Values)
rowLength += (int)o;
#endregion get min length for columns
sb.Append(new string('=', (rowLength - " DataTable ".Length) / 2));
sb.Append(" DataTable ");
sb.AppendLine(new string('=', (rowLength - " DataTable ".Length) / 2));
if (!string.IsNullOrEmpty(dataTable.TableName))
sb.AppendLine(String.Format("{0,-" + rowLength + "}", String.Format("{0," + ((rowLength + dataTable.TableName.Length) / 2).ToString() + "}", dataTable.TableName)));
#region write values
foreach (DataColumn col in dataTable.Columns)
sb.Append(seperator + String.Format("{0,-" + hash[col.ColumnName] + "}", col.ColumnName));
sb.AppendLine(seperator);
sb.AppendLine(new string('-', rowLength));
foreach (DataRow row in dataTable.Rows)
{
for (int i = 0; i < row.ItemArray.Length; i++)
{
sb.Append(seperator + String.Format("{0," + hash[dataTable.Columns[i].ColumnName] + "}", row[i]));
if (i == row.ItemArray.Length - 1)
sb.AppendLine(seperator);
}
}
#endregion write values
sb.AppendLine(new string('=', rowLength));
}
else
sb.AppendLine("================ DataTable is NULL ================");
return sb.ToString();
}
output:
======================= DataTable =======================
MyTable
| COL1 | COL2 | COL3 1000000ng name |
----------------------------------------------------------
| 1 | 2 | 3 |
| abc | Dienstag, 12. März 2013 | xyz |
| Have | a nice | day! |
==========================================================
very vague ....
id bung it into a dataset simply so that i can output it easily as xml ....
failing that why not iterate through its row and column collections and output them?
Or, change the app to WinForms, use grid and bind DataTable to grid. If it is a demo/sample app.
I created my variant of class for your needs. I believe it is a bit more configurable than already provided variants.
You can use it with all default settings just create an instance of a class and call StringifyDataTable method, or you can set additional options if needed.
public class DataTableStringifier
{
public bool IsOuterBordersPresent { get; set; } //Whether outer borders of table needed
public bool IsHeaderHorizontalSeparatorPresent { get; set; } // Whether horizontal line separator between table title and data is needed. Useful to set 'false' if you expect only 1 or 2 rows of data - no need for additional lines then
public char ValueSeparator { get; set; } //Vertical line character
public char HorizontalLinePadChar { get; set; } // Horizontal line character
public char HorizontalLineSeparator { get; set; } // Horizontal border (between header and data) column separator (crossing of horizontal and vertical borders)
public int ValueMargin { get; set; } // Horizontal margin from table borders (inner and outer) to cell values
public int MaxColumnWidth { get; set; } // To avoid too wide columns with thousands of characters. Longer values will be cropped in the center
public string LongValuesEllipses { get; set; } // Cropped values wil be inserted this string in the middle to mark the point of cropping
public DataTableStringifier()
{
MaxColumnWidth = int.MaxValue;
IsHeaderHorizontalSeparatorPresent = true;
ValueSeparator = '|';
ValueMargin = 1;
HorizontalLinePadChar = '-';
HorizontalLineSeparator = '+';
LongValuesEllipses = "...";
IsOuterBordersPresent = false;
}
public string StringifyDataTable(DataTable table)
{
int colCount = table.Columns.Count;
int rowCount = table.Rows.Count;
string[] colHeaders = new string[colCount];
string[,] cells = new string[rowCount, colCount];
int[] colWidth = new int[colCount];
for (int i = 0; i < colCount; i++)
{
var column = table.Columns[i];
var colName = ValueToLimitedLengthString(column.ColumnName);
colHeaders[i] = colName;
if (colWidth[i] < colName.Length)
{
colWidth[i] = colName.Length;
}
}
for (int i = 0; i < rowCount; i++)
{
DataRow row = table.Rows[i];
for (int j = 0; j < colCount; j++)
{
var valStr = ValueToLimitedLengthString(row[j]);
cells[i, j] = valStr;
if (colWidth[j] < valStr.Length)
{
colWidth[j] = valStr.Length;
}
}
}
string valueSeparatorWithMargin = string.Concat(new string(' ', ValueMargin), ValueSeparator, new string(' ', ValueMargin));
string leftBorder = IsOuterBordersPresent ? string.Concat(ValueSeparator, new string(' ', ValueMargin)) : "";
string rightBorder = IsOuterBordersPresent ? string.Concat(new string(' ', ValueMargin), ValueSeparator) : "";
string horizLine = new string(HorizontalLinePadChar, colWidth.Sum() + (colCount - 1)*(ValueMargin*2 + 1) + (IsOuterBordersPresent ? (ValueMargin + 1)*2 : 0));
StringBuilder tableBuilder = new StringBuilder();
if (IsOuterBordersPresent)
{
tableBuilder.AppendLine(horizLine);
}
tableBuilder.Append(leftBorder);
for (int i = 0; i < colCount; i++)
{
tableBuilder.Append(colHeaders[i].PadRight(colWidth[i]));
if (i < colCount - 1)
{
tableBuilder.Append(valueSeparatorWithMargin);
}
}
tableBuilder.AppendLine(rightBorder);
if (IsHeaderHorizontalSeparatorPresent)
{
if (IsOuterBordersPresent)
{
tableBuilder.Append(ValueSeparator);
tableBuilder.Append(HorizontalLinePadChar, ValueMargin);
}
for (int i = 0; i < colCount; i++)
{
tableBuilder.Append(new string(HorizontalLinePadChar, colWidth[i]));
if (i < colCount - 1)
{
tableBuilder.Append(HorizontalLinePadChar, ValueMargin);
tableBuilder.Append(HorizontalLineSeparator);
tableBuilder.Append(HorizontalLinePadChar, ValueMargin);
}
}
if (IsOuterBordersPresent)
{
tableBuilder.Append(HorizontalLinePadChar, ValueMargin);
tableBuilder.Append(ValueSeparator);
}
tableBuilder.AppendLine();
}
for (int i = 0; i < rowCount; i++)
{
tableBuilder.Append(leftBorder);
for(int j=0; j<colCount; j++)
{
tableBuilder.Append(cells[i, j].PadRight(colWidth[j]));
if(j<colCount-1)
{
tableBuilder.Append(valueSeparatorWithMargin);
}
}
tableBuilder.AppendLine(rightBorder);
}
if (IsOuterBordersPresent)
{
tableBuilder.AppendLine(horizLine);
}
return tableBuilder.ToString(0, tableBuilder.Length - 1); //Trim last enter char
}
private string ValueToLimitedLengthString(object value)
{
string strValue = value.ToString();
if (strValue.Length > MaxColumnWidth)
{
int beginningLength = (MaxColumnWidth) / 2;
int endingLength = (MaxColumnWidth + 1) / 2 - LongValuesEllipses.Length;
return string.Concat(strValue.Substring(0, beginningLength), LongValuesEllipses, strValue.Substring(strValue.Length - endingLength, endingLength));
}
else
{
return strValue;
}
}
}
If you have a single column in datatable than it's simple to change datatable to string.
DataTable results = MyMethod.GetResults();
if(results != null && results.Rows.Count > 0) // Check datatable is null or not
{
List<string> lstring = new List<string>();
foreach(DataRow dataRow in dt.Rows)
{
lstring.Add(Convert.ToString(dataRow["ColumnName"]));
}
string mainresult = string.Join(",", lstring.ToArray()); // You can Use comma(,) or anything which you want. who connect the two string. You may leave space also.
}
Console.WriteLine (mainresult);

Categories