DataTable returning empty - c#

For my application there are a few separate dataTables and I need to create a new dataTable based on matching ids. I have to do the process a few times so I created a function so I'm not duplicating code, I've done this like so:
private static DataTable CloneTable(DataTable originalTable, DataTable newTable, DataTable targetTable,
string addedColumn, string columnToExtract, bool multipleConditions = false, string secondColumnName = null, string secondColumnConditon= null)
{
newTable = originalTable.Clone();
newTable.Columns.Add(addedColumn);
foreach (DataRow row in originalTable.Rows)
{
DataRow[] rowsTarget;
if (multipleConditions == false)
{
rowsTarget = targetTable.Select(string.Format("ItemId='{0}'", Convert.ToString(row["ItemId"])));
} else
{
rowsTarget = targetTable.Select(string.Format("ItemId='{0}' AND {1} ='{2}'", Convert.ToString(row["ItemId"]), secondColumnName, secondColumnConditon));
}
if (rowsTarget != null && rowsTarget.Length > 0)
{
string data = rowsTarget[0][columnToExtract].ToString();
var lst = row.ItemArray.ToList();
lst.Add(data);
newTable.Rows.Add(lst.ToArray());
}
else
{
string data = "";
var lst = row.ItemArray.ToList();
lst.Add(data);
newTable.Rows.Add(lst.ToArray());
}
}
return newTable;
}
I then call this in a separate function like so:
private DataTable GetExtractData()
{
.........................
DataTable includeLastModified = new DataTable();
DataTable includeFunction = new DataTable();
DataTable includeDiscipline = new DataTable();
CloneTable(itemsTable, includeLastModified, lastModifiedTable, "LastModifiedDate", "LastModifiedDate");
CloneTable(includeLastModified, includeFunction, customPropertiesTable, "Function", "ItemTitle", true, "Title", "Function");
CloneTable(includeFunction, includeDiscipline, customPropertiesTable, "Discipline", "ItemTable", true, "Title", "Discipline");
return includeDiscipline;
}
The issue I am having is that the dataTable here is returning empty and I am not sure why.
In my CloneTable function I did the following to make sure that the new table is not empty:
foreach (DataRow row in newTable.Rows)
{
foreach (var item in row.ItemArray)
{
Console.WriteLine(item);
}
}
It is not empty so I am not sure why when I'm returning it in a separate function it is now empty?
I call the same thing but for the includeDiscipline table in the GetData function but it comes back empty.
There are no errors but there is a message that comes and goes that says that the parameter "newTable" can be removed as the initial value isn't used. I'm not sure how that could be the case though as it is clearly being used?
I'm assuming that it is probably the way I am calling the function but I'm really not sure what it is that I have done wrong here

Okay face palm moment, just realised I forgot to assign it to something.
So if I do something like:
var test = CloneTable(itemsTable, includeLastModified, lastModifiedTable, "LastModifiedDate", "LastModifiedDate");
return test;
It works fine and no longer returns empty

Related

C# 'Continue' not functioning as expected

I have the following code
private void bgwSendMail_DoWork(object sender, DoWorkEventArgs e)
{
DataSet ds = getMailToSend();
DataTable table = ds.Tables[0];
{
foreach (DataRow row in table.Rows)
{
{
string attachment1 = ds.Tables[0].Rows[0]["Attachment1"].ToString();
string attachment2 = ds.Tables[0].Rows[0]["Attachment2"].ToString();
string attachment3 = ds.Tables[0].Rows[0]["Attachment3"].ToString();
string attachment4 = ds.Tables[0].Rows[0]["Attachment4"].ToString();
string mailTo = ds.Tables[0].Rows[0]["EmailTo"].ToString();
string mailSubject = ds.Tables[0].Rows[0]["EmailSubject"].ToString();
string mailBody= ds.Tables[0].Rows[0]["EmailBody"].ToString();
string uid = ds.Tables[0].Rows[0]["uid"].ToString();
if (String.IsNullOrEmpty(attachment1))
{
//TODO Send Email Straight away ignore rest
}
else
{
if (!String.IsNullOrEmpty(attachment1))
{
bool attachment1Exists = checkFileExists(attachment1);
if (attachment1Exists == false)
{
continue;
}
}
Now I would expect, when we hit continue (which does get hit) at the bottom, that we should exit back up to the foreach as below and move on to the next record in the dataset
This does not happen, it iterates over the same record from which the continue came from over and over, is this normal?
If it's normal what's the best way to get the foreach to ignore that row in the datatable once it's been exited once?
The continue is working as expected.
You are enumerating all rows in the table but you aren't using it. Instead you are always accessing the first row in the table:
DataTable table = ds.Tables[0];
foreach(DataRow row in table.Rows)
{
string attachment1 = ds.Tables[0].Rows[0]["Attachment1"].ToString();
// ...
}
You are always accessing ds.Tables[0].Rows[0].
Instead you should use this code:
foreach(DataRow row in table.Rows)
{
string attachment1 = row["Attachment1"].ToString();
// ...
}
So you are actually enumerating all rows in the table as expected, it's not an infinite loop, but you are not using every row in the table but only the first.
Change
string attachment1 = ds.Tables[0].Rows[0]["Attachment1"].ToString();
to
string attachment1 = row["Attachment1"].ToString();
and all other subsequent references to the DataRow.

Read CSV file in DataGridView

I want to read a csv-file into a Datagridview. I would like to have a class and a function which reads the csv like this one:
class Import
{
public DataTable readCSV(string filePath)
{
DataTable dt = new DataTable();
using (StreamReader sr = new StreamReader(filePath))
{
string strLine = sr.ReadLine();
string[] strArray = strLine.Split(';');
foreach (string value in strArray)
{
dt.Columns.Add(value.Trim());
}
DataRow dr = dt.NewRow();
while (sr.Peek() >= 0)
{
strLine = sr.ReadLine();
strArray = strLine.Split(';');
dt.Rows.Add(strArray);
}
}
return dt;
}
}
and call it:
Import imp = new Import();
DataTable table = imp.readCSV(filePath);
foreach(DataRow row in table.Rows)
{
dataGridView.Rows.Add(row);
}
Result of this is-> rows are created but there is no data in the cells!!
First solution using a litle bit of linq
public DataTable readCSV(string filePath)
{
var dt = new DataTable();
// Creating the columns
File.ReadLines(filePath).Take(1)
.SelectMany(x => x.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
.ToList()
.ForEach(x => dt.Columns.Add(x.Trim()));
// Adding the rows
File.ReadLines(filePath).Skip(1)
.Select(x => x.Split(';'))
.ToList()
.ForEach(line => dt.Rows.Add(line));
return dt;
}
Below another version using foreach loop
public DataTable readCSV(string filePath)
{
var dt = new DataTable();
// Creating the columns
foreach(var headerLine in File.ReadLines(filePath).Take(1))
{
foreach(var headerItem in headerLine.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
{
dt.Columns.Add(headerItem.Trim());
}
}
// Adding the rows
foreach(var line in File.ReadLines(filePath).Skip(1))
{
dt.Rows.Add(x.Split(';'));
}
return dt;
}
First we use the File.ReadLines, that returns an IEnumerable that is a colletion of lines. We use Take(1), to get just the first row, that should be the header, and then we use SelectMany that will transform the array of string returned from the Split method in a single list, so we call ToList and we can now use ForEach method to add Columns in DataTable.
To add the rows, we still use File.ReadLines, but now we Skip(1), this skip the header line, now we are going to use Select, to create a Collection<Collection<string>>, then again call ToList, and finally call ForEach to add the row in DataTable. File.ReadLines is available in .NET 4.0.
Obs.: File.ReadLines doesn't read all lines, it returns a IEnumerable, and lines are lazy evaluated, so just the first line will be loaded two times.
See the MSDN remarks
The ReadLines and ReadAllLines methods differ as follows: When you use ReadLines, you can start enumerating the collection of strings before the whole collection is returned; when you use ReadAllLines, you must wait for the whole array of strings be returned before you can access the array. Therefore, when you are working with very large files, ReadLines can be more efficient.
You can use the ReadLines method to do the following:
Perform LINQ to Objects queries on a file to obtain a filtered set of its lines.
Write the returned collection of lines to a file with the File.WriteAllLines(String, IEnumerable) method, or append them to an existing file with the File.AppendAllLines(String, IEnumerable) method.
Create an immediately populated instance of a collection that takes an IEnumerable collection of strings for its constructor, such as a IList or a Queue.
This method uses UTF8 for the encoding value.
If you still have any doubt look this answer: What is the difference between File.ReadLines() and File.ReadAllLines()?
Second solution using CsvHelper package
First, install this nuget package
PM> Install-Package CsvHelper
For a given CSV, we should create a class to represent it
CSV File
Name;Age;Birthdate;Working
Alberto Monteiro;25;01/01/1990;true
Other Person;5;01/01/2010;false
The class model is
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public DateTime Birthdate { get; set; }
public bool Working { get; set; }
}
Now lets use CsvReader to build the DataTable
public DataTable readCSV(string filePath)
{
var dt = new DataTable();
var csv = new CsvReader(new StreamReader(filePath));
// Creating the columns
typeof(Person).GetProperties().Select(p => p.Name).ToList().ForEach(x => dt.Columns.Add(x));
// Adding the rows
csv.GetRecords<Person>().ToList.ForEach(line => dt.Rows.Add(line.Name, line.Age, line.Birthdate, line.Working));
return dt;
}
To create columns in DataTable e use a bit of reflection, and then use the method GetRecords to add rows in DataTabble
using Microsoft.VisualBasic.FileIO;
I would suggest the following. It should have the advantage at least that ';' in a field will be correctly handled, and it is not constrained to a particular csv format.
public class CsvImport
{
public static DataTable NewDataTable(string fileName, string delimiters, bool firstRowContainsFieldNames = true)
{
DataTable result = new DataTable();
using (TextFieldParser tfp = new TextFieldParser(fileName))
{
tfp.SetDelimiters(delimiters);
// Get Some Column Names
if (!tfp.EndOfData)
{
string[] fields = tfp.ReadFields();
for (int i = 0; i < fields.Count(); i++)
{
if (firstRowContainsFieldNames)
result.Columns.Add(fields[i]);
else
result.Columns.Add("Col" + i);
}
// If first line is data then add it
if (!firstRowContainsFieldNames)
result.Rows.Add(fields);
}
// Get Remaining Rows
while (!tfp.EndOfData)
result.Rows.Add(tfp.ReadFields());
}
return result;
}
}
CsvHelper's Author build functionality in library.
Code became simply:
using (var reader = new StreamReader("path\\to\\file.csv"))
using (var csv = new CsvReader(reader, CultureInfo.CurrentCulture))
{
// Do any configuration to `CsvReader` before creating CsvDataReader.
using (var dr = new CsvDataReader(csv))
{
var dt = new DataTable();
dt.Load(dr);
}
}
CultureInfo.CurrentCulture is used to determine the default delimiter and needs if you want to read csv saved by Excel.
I had the same problem but I found a way to use #Alberto Monteiro's Answer in my own way...
My CSV file does not have a "First-Line-Column-Header", I personally didn't put them there for some reasons, So this is the file sample
1,john doe,j.doe,john.doe#company.net
2,jane doe,j.doe,jane.doe#company.net
So you got the idea right ?
Now in I am going to add the Columns manually to the DataTable. And also I am going to use Tasks to do it asynchronously. and just simply using a foreach loop adding the values into the DataTable.Rows using the following function:
public Task<DataTable> ImportFromCSVFileAsync(string filePath)
{
return Task.Run(() =>
{
DataTable dt = new DataTable();
dt.Columns.Add("Index");
dt.Columns.Add("Full Name");
dt.Columns.Add("User Name");
dt.Columns.Add("Email Address");
// splitting the values using Split() command
foreach(var srLine in File.ReadAllLines(filePath))
{
dt.Rows.Add(srLine.Split(','));
}
return dt;
});
}
Now to call the function I simply ButtonClick to do the job
private async void ImportToGrid_STRBTN_Click(object sender, EventArgs e)
{
// Handling UI objects
// Best idea for me was to put everything a Panel and Disable it while waiting
// and after the job is done Enabling it
// and using a toolstrip docked to bottom outside of the panel to show progress using a
// progressBar and setting its style to Marquee
panel1.Enabled = false;
progressbar1.Visible = true;
try
{
DataTable dt = await ImportFromCSVFileAsync(#"c:\myfile.txt");
if (dt.Rows.Count > 0)
{
Datagridview1.DataSource = null; // To clear the previous data before adding the new ones
Datagridview1.DataSource = dt;
}
}
catch (Exception ex)
{
MessagBox.Show(ex.Message, "Error");
}
progressbar1.Visible = false;
panel1.Enabled = true;
}

how to split the values of column in datatable of dataset?

In my dataset I have many rows,
In first row last column has the value is "12356.56#Firefox 1#23423"
In Second row same column has the value is "12356.56#Chrome2.0#23423"
In Third row same column has the value is "3423#Firefox 14.0#sdfsd"
here instead of displaying same value like above..
I need to display just "Firefox" if that column has Firefox in UI
I need to display just "Chrome" if that column has Chromein UI
how can i do it...
Use this helper function
public static string GetBrowser(string str)
{
str = str.ToUpper();
if(str.Contains("CHROME"))
{
return "Chrome";
}
else if(str.Contains("FIREFOX"))
{
return "Firefox";
}
return "Unknown";
}
Then when you bind the dataset to the UI use it to display the value.
If you have a list of things you want to check for, you can use .Contains to check:
var myValue = myDS[rowIndex][lastColumn].ToString();
if (myValue.Contains("Firefox"))
return "Firefox";
else if (myValue.Contains("Chrome"))
return "Chrome";
else
return "Unknown";
Demo: http://ideone.com/pMVeD
From the examples you've given, it appears the following logic may work, but it should be tested against a wider variety of sample values:
var myValue = myDS[rowIndex][lastColumn].ToString();
var parts = myValue.Split('#');
var browser = parts[1].Split(' ','0','1','2','3','4','5','6','7','8','9');
return browser[0];
Demo: http://ideone.com/NrY1e
Here is code. This might help.
DataSet ds = new DataSet();
//DataTable dt = new DataTable();
ds.Tables.Add("table0");
DataColumn dc = new DataColumn("browser");
ds.Tables[0].Columns.Add(dc);
ds.Tables[0].Rows.Add("12356.56#Firefox1#23423");
for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
{
string str = ds.Tables[0].Rows[i][0].ToString();
if((str.ToLower().CompareTo("firefox"))!=0)
{
ds.Tables[0].Rows[i][0] = "firefox";
}
}
_gridView.DataSource = ds;
_gridView.DataBind();

Cannot convert a row to a DataTable in C#

I'm trying to convert a DataRow to a DataTable, but I'm getting errors. I searched and tried all possible solutions, but none worked!
I have a method which accepts a DataTable as its parameter (this DataTable has one row exactly). That method will return some information.
At first, I tried to convert the DataRow to a DataTable using ImportRow(newtable.ImportRow(row)), but newtable is empty afterward. Then, I tried dt.clone(), but this fills the newtable with just everything, which is not what I was after! Actually the exact thing I was after.
private static void BatchFrontSidePrinting(Student St, frmBaseCard frm)
{
DBINFOPACK UserInfo;
DBCARDINFO CardInfo;
DataTable newtable = new DataTable("newtable");
foreach (DataRow row in dt.Rows)
{
try
{
// here, I'm trying to send one DataRow as a DataTable to the GetInfo() method,
// and for the next iteratio , after getting the info I'm removing the row which was just added,
// so that for the next iteration, newdatatable is empty. All of the proceeding actions fail !:(
newtable.ImportRow(row); // doesn't work!
UserInfo = GetInfo(newtable);
newtable.Rows.Remove(row); // doesn't work!
St = UserInfo.STUDENT;
((frmFrontSideCard)frm).Replica = UserInfo.CARDINFO.Replica;
if (UserInfo.CARDINFO.Replica)
{
Loger.Items.Add("Replication !");
}
// print
((frmFrontSideCard)frm).Print = St;
// update
CardInfo = UserInfo.CARDINFO;
CardInfo.SID = UserInfo.STUDENT.ID;
CardInfo.BIMAGE = UserInfo.BIMAGE;
SetInfo(CardInfo);
}
catch (Exception exep)
{
Loger.Items.Add(String.Format("Inside [BatchFrontSidePrinting()] : Student {0} {1}:", St.ID, exep.Message));
}
}
}
foreach (DataRow row in dt.Rows)
{
try
{
DataTable newtable = new DataTable();
newtable = dt.Clone(); // Use Clone method to copy the table structure (Schema).
newtable.ImportRow(row); // Use the ImportRow method to copy from dt table to its clone.
UserInfo = GetInfo(newtable);
catch (Exception exep)
{
//
}
}
var someRow = newTable.NewRow();
someRow[0] = row[0]; // etc
newTable.Rows.Add(someRow);
It looks like you are using newtable as a temporary container to send each row in dt to the GetInfo method. If so, why not change the GetInfo method to take a DataRow rather than a DataTable that contains a single DataRow? Then you can get rid of newtable and not bother with creating and copying DataRows in the first place.
private static void BatchFrontSidePrinting(Student St, frmBaseCard frm)
{
DBINFOPACK UserInfo ;
DBCARDINFO CardInfo;
foreach (DataRow row in dt.Rows)
{
try
{
// just pass the row
UserInfo = GetInfo(row);
// rest of the code as before
St = UserInfo.STUDENT;
((frmFrontSideCard)frm).Replica = UserInfo.CARDINFO.Replica;
if (UserInfo.CARDINFO.Replica)
{
Loger.Items.Add("Replication !");
}
//print
((frmFrontSideCard)frm).Print = St;
//update
CardInfo = UserInfo.CARDINFO;
CardInfo.SID = UserInfo.STUDENT.ID;
CardInfo.BIMAGE = UserInfo.BIMAGE;
SetInfo(CardInfo);
}
catch (Exception exep)
{
Loger.Items.Add(String.Format("Inside [BatchFrontSidePrinting()] : Student {0} {1}:", St.ID, exep.Message));
}

Datatable subset of columns from another datatable

I have a datatable with 17 columns and a bunch of data.
I wnat a datatable with only 6 of the columns and the data for those 6 columns.
So I need a subset of the original datatable.
How do I loop through the original datatable with 17 columns and end up with a datatable with only the 6 columns I want with the corresponding data for those 6 columns?
Private Function createSmallCopyofExistingTable(ByVal SourceTable As DataTable) As DataTable
Dim newTable As DataTable = New DataTable()
'Copy Only 6 columns from the datatable
Dim ColumnsToExport() As String = {"ID", "FirstName", "LastName", "DateOfBirth", "City", "State"}
newTable = SourceTable.DefaultView.ToTable("tempTableName", False, ColumnsToExport)
Return newTable
End Function
Without knowing more about how generic this needs to be its really just...
foreach (DataRow dr in dt.Rows)
{
newDt.Rows.Add(dr["col1"],dr["col5"],etc);
}
what about data types, and columns? are these same? if yes, you can create
object[] row = new object[]{// Fill your rows manually};
before filling it create
DataTable dt = new DataTable();
dt.Columns.Add("Title",typeof(string etc..));.....
and finally
dt.Rows.Add(row);
Personally, I would avoid creating another instance of a DataTable.
It depends on your situation, of course, but if this is purely for usability and not for security (i.e. you're not trying to remove columns with sensitive data before transmitting it somewhere), then I would create a wrapper object that encapsulates the columns that you want to expose.
The benefit of using a wrapper is in case you are doing any updates, then you can update the source table directly rather than the copy. Whether this really matters, of course, depends on your situation.
A simple example with limited functionality:
public class MyFormOrPage
{
void UsageExample()
{
DataTable allDataTable = new DataTable();
// populate the data table with whatever logic ...
// wrap the data table to expose only the Name, Address, and PhoneNumber columns
var limitedDataTable = new DataTableWrapper(allDataTable, "Name", "Address", "PhoneNumber");
// iterate over the rows
foreach (var limitedDataRow in limitedDataTable)
{
// iterate over the columns
for (int i = 0; i < limitedDataTable.ColumnCount; i++)
{
object value = limitedDataRow[i];
// do something with the value ...
}
}
// bind the wrapper to a control
MyGridControl.DataSource = limitedDataTable;
}
}
public class DataTableWrapper : IEnumerable<DataRowWrapper>
{
private DataTable _Table;
private string[] _ColumnNames;
public DataTableWrapper(DataTable table, params string[] columnNames)
{
this._Table = table;
this._ColumnNames = columnNames;
}
public int ColumnCount
{
get { return this._ColumnNames.Length; }
}
public IEnumerator<DataRowWrapper> GetEnumerator()
{
foreach (DataRow row in this._Table.Rows)
{
yield return new DataRowWrapper(row, this._ColumnNames);
}
}
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
// if you _really_ want to make a copy of the DataTable, you can use this method
public DataTable CopyToDataTable()
{
DataTable copyTable = new DataTable();
for (int index = 0; index < this._ColumnNames.Length; index++)
{
DataColumn column = this._Table.Columns[index];
copyTable.Columns.Add(column);
}
foreach (DataRow row in this._Table.Rows)
{
DataRow copyRow = copyTable.NewRow();
for (int index = 0; index < this._ColumnNames.Length; index++)
{
copyRow[index] = row[this._ColumnNames[index]];
}
copyTable.Rows.Add(copyRow);
}
return copyTable;
}
}
// let's make this a struct, since potentially very many of these will be instantiated
public struct DataRowWrapper
{
private DataRow _Row;
private string[] _ColumnNames;
public DataRowWrapper(DataRow row, params string[] columnNames)
{
this._Row = row;
this._ColumnNames = columnNames;
}
// use this to retrieve column values from a row
public object this[int index]
{
get { return this._Row[this._ColumnNames[index]]; }
set { this._Row[this._ColumnNames[index]] = value; }
}
// just in case this is still needed...
public object this[string columnName]
{
get { return this._Row[columnName]; }
set { this._Row[columnName] = value; }
}
}

Categories

Resources