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.
Related
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
I have a DataTable from which I would like to loop through each row and column and then select a value from a specific column depending on the other values in the columns/each row.
My code currently looks like this:
foreach (DataRow drow in dt.Rows)
{
foreach (DataColumn dcol in dt.Columns)
{
foreach (var Item in ImportData)
{
if (Item.Value.Equals(true))
{
if (Item.Key.Equals("" + dcol))
{
string value = drow[dcol].ToString();
if (value.Equals("X"))
{
outDraws += drow["Drawing"].ToString();
outDraws += "\n";
}
}
}
}
}
}
ImportData is a Dictionary<string, bool>, which holds the data that I want to compare with my DataTable.
string outDraws is just a string which should hold the content of the drawings I want to print out.
My problem now is that I only want to print out the content in the column 'Drawing' of the row where all columns with the same name as the Keys in ImportData have 'X' as value. At the moment I'm getting all the rows where any of the columns have 'X' as value and has the same name as any Key in ImportData.
I understand that it will be quite hard for you to get what I want to do but please ask if you need any more information and I will try to provide.
Many thanks in advance.
Edit:
ImportData contains the name of different products as keys. These products have either been selected or not by the customer through another program, if they have been selected they have the value true and if not selected they have the value false.
With the method presented above I would like to compare ALL the keys that have the value true with the column names in the DataTable. If the column name corresponds to the key in ImportData (which is the name of a product) then I want to check if that column in a specific row has 'X' as value.
This goes on for ALL the keys in ImportData and in the end I should know which row in the DataTable that has an 'X' in all the columns with the same name as the keys in ImportData. For this row I would like to get the content of the column called 'Drawing'.
So for an example say that ImportData contains:
[Motor, true][Product6, true][Product7, true]
Then I would like to print out the column Drawing at row 6.
Unfortunately I can't post pictures..
As with any problem: divide and conquer. Break down your problem in smaller pieces and go from there.
From what I understand, you want to do something with certain rows from the datatable. Something like:
foreach (var drow in dt.Rows.OfType<DataRow>())
{
if (SomeConditionIsMet(dt, drow, ImportData))
{
outDraws += drow["Drawing"].ToString();
outDraws += "\n";
}
}
The function SomeConditionIsMetcould looks like this:
private static bool SomeConditionIsMet(
DataTable dt, DataRow drow,
IDictionary<string, bool> importData)
{
// TODO if the condition is met, return true
// otherwise, return false
}
Now your problem is simplified to thinking about what it means that 'Some condition is met'. Once you can clearly express that in words, rename the function to reflect that (e.g. to 'AllColumnsAreChecked')
Here's a sample with solution as I understand it:
internal class Program
{
private static void Main(string[] args)
{
var importData = new Dictionary<string, bool>()
{
{"Product1", true},
{"Product2", false},
{"Product3", true},
};
var dt = new DataTable();
dt.Columns.Add("Product1");
dt.Columns.Add("Product2");
dt.Columns.Add("Product3");
dt.Columns.Add("Product4");
dt.Columns.Add("Drawing");
// row1 should be added
var row1 = dt.NewRow();
row1["Product1"] = "X";
row1["Product3"] = "X";
row1["Drawing"] = "Drawing1";
dt.Rows.Add(row1);
// row2 should not be added
var row2 = dt.NewRow();
row2["Product1"] = "X";
row2["Drawing"] = "Drawing2";
dt.Rows.Add(row2);
string outDraws = string.Empty;
foreach (DataRow drow in dt.Rows.OfType<DataRow>())
{
if (AllColumnsAreChecked(drow, importData))
{
outDraws += drow["Drawing"].ToString();
outDraws += "\n";
}
}
Console.WriteLine(outDraws);
}
private static bool AllColumnsAreChecked(DataRow drow, Dictionary<string, bool> importData)
{
foreach (var key in importData.Keys)
{
if (!importData[key])
continue;
var value = drow[key] as string;
if (value != "X")
return false;
}
}
}
Bonus: here's a LINQ based implementation of the check:
private static bool AllColumnsAreChecked(DataRow drow, Dictionary<string, bool> importData)
{
return importData.Keys
.Where(k => importData.ContainsKey(k) && importData[k]) // the field must be enabled in importData
.All(k => (drow[k] as string) == "X"); // the corresponding value in the row must be 'X'
}
Try this
DataTable tbl = new DataTable();
foreach (DataRow row in tbl.Rows)
{
object cellData = row["colName"];
}
I need to eliminate all rows that contain either string notUsed or string notUsed2, where a particular identity is 2.
I'm using a foreach loop to accomplish this, prior to appending any of my data to a stringbuilder.
I would have chosen a method like so:
foreach (DataRow dr in ds.Tables[0].Rows)
{
int auth = (int)dr[0];
if (auth == 2) continue;
string notUsed = "NO LONGER USED";
string notUsed2 = "NO LONGER IN USE";
if (dr.Cells[3].ToString().Contains(string)notUsed)
{
dr.Delete();
}
else
{
if (dr.Cells[3].ToString().Contains(string)notUsed2)
{
dr.Delete();
}
}
}
However, the above is... utterly wrong. It seems logical to me, but I don't quite understand how to form that method in a way that C# understands.
You should
Remove Cells as it is not a property of DataRow instead use
dr[3].
Remove string from Contains so your check should be like: if (dr[3].ToString().Contains(notUsed))
You are modifying the collection inside a foreach loop, by deleting the row. You should use for loop backward.
Like:
for (int i = ds.Tables[0].Rows.Count - 1; i >= 0; i--)
{
DataRow dr = ds.Tables[0].Rows[i];
int auth = (int)dr[0];
if (auth == 2) continue;
string notUsed = "NO LONGER USED";
string notUsed2 = "NO LONGER IN USE";
if (dr[3].ToString().Contains(notUsed))
{
dr.Delete();
}
else
{
if (dr[3].ToString().Contains(notUsed2))
{
dr.Delete();
}
}
}
You can also use LINQ to DataSet and get a new DataTable like:
DataTable newDataTAble = ds.Tables[0].AsEnumerable()
.Where(r => !r.Field<string>(3).Contains(notUsed) &&
r.Field<string>(3).Contains(notUsed2))
.CopyToDataTable();
I am importing data from csv file, sometimes there are column headers and some times not the customer chooses custom columns(from multiple drop downs)
my problem is I am able to change the columns type and name but when I want to import data row into cloned table it just adds rows but no data with in those rows. If I rename the column to old values it works, let's say column 0 name is 0 if I change that to something else which I need to it won't fill the row below with data but If I change zero to zero again it will any idea:
here is my coding:
#region Manipulate headers
DataTable tblCloned = new DataTable();
tblCloned = tblDataTable.Clone();
int i = 0;
foreach (string item in lstRecord)
{
if (item != "Date")
{
var m = tblDataTable.Columns[i].DataType;
tblCloned.Columns[i].DataType = typeof(System.String);
tblCloned.Columns[i].ColumnName = item;
}
else if(item == "Date")
{
//get the proper date format
//FillDateFormatToColumn(tblCloned);
tblCloned.Columns[i].DataType = typeof(DateTime);
tblCloned.Columns[i].ColumnName = item;
}
i++;
}
tblCloned.AcceptChanges();
foreach (DataRow row in tblDataTable.Rows)
{
tblCloned.ImportRow(row);
}
tblCloned.AcceptChanges();
#endregion
in the second foreach loop when it calls to import data to cloned table it adds empty rows.
After couple of tries I came up with this solution which is working:
foreach (DataRow row in tblDataTable.Rows)
{
int x = 0;
DataRow dr = tblCloned.NewRow();
foreach (DataColumn dt in tblCloned.Columns)
{
dr[x] = row[x];
x++;
}
tblCloned.Rows.Add(dr);
//tblCloned.ImportRow(row);
}
but I will accept Scottie's answer because it is less code after all.
Instead of
foreach (DataRow row in tblDataTable.Rows)
{
tblCloned.ImportRow(row);
}
try
foreach (DataRow row in tblDataTable.Rows)
{
tblCloned.LoadDataRow(row.ItemArray, true);
}
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));
}