I wrote a method for splitting a DataTable into multiple small data tables; however I am getting exception. How do I correct it? Please share the code.
Exception message:
This row already belongs to another table.
Framework: .Net 3.0
private static List<DataTable> SplitDataTable(DataTable dt, int size)
{
List<DataTable> split = new List<DataTable>();
DataTable current = dt.Clone();
int iterator = 0;
foreach (DataRow dr in dt.Rows)
{
iterator = iterator + 1;
if (iterator == size)
{
current = dt.Clone();
split.Add(current);
iterator = 0;
}
current.Rows.Add(dr);
//Exception: This row already belongs to another table.
}
return split;
}
Client:
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("TEST", typeof(int));
dt.Columns.Add("VAL", typeof(string));
dt.Rows.Add(0,"a");
dt.Rows.Add(1,"b");
dt.Rows.Add(2,"c");
dt.Rows.Add(3,"d");
List<DataTable> split = SplitDataTable(dt, 2);
}
Use dt.Copy(); instead of dt.Clone();
Before you add a DataRow to your cloned datatable, you need to remove it from the original source datatable:
foreach (DataRow dr in dt.Rows)
{
iterator = iterator + 1;
if (iterator == size)
{
current = dt.Clone();
split.Add(current);
iterator = 0;
}
dt.Rows.Remove(dr); // remove it from the source FIRST, then add it to the cloned DataTable
current.Rows.Add(dr);
}
Use current.ImportRow(dr); instead of current.Rows.Add(dr);
You can either remove the DataRow from the source DataTable or create a new DataRow and add it to the new DataTable.
You just need to change the line where you add the data row to current data table. Use the overload which takes an object array to create a new row. This way you are not cloning or copying any rows, instead creating a new row.
current.Rows.Add(dr.ItemArray);
I think this function will not work properly try this
I make some modification on you code it's working fine
private static List<DataTable> SplitDataTable(DataTable dt, int size)
{
List<DataTable> split = new List<DataTable>();
DataTable current = dt.Clone();
int iterator1 = 0;
foreach (DataRow dr in dt.Rows)
{
if (current.Rows.Count < size)
{
current.Rows.Add(dr.ItemArray);
}
if (current.Rows.Count == size)
{
iterator1= iterator1+size;
split.Add(current);
current = dt.Clone();
}
}
if (iterator1 < dt.Rows.Count) { split.Add(current); }
return split;
}
happy codding
Related
string s = "primaryKeyValue";
DataRow foundRow = dataSet1.Tables["AnyTable"].Rows.Find(s);
if (foundRow != null)
{
foreach(DataRow r in dataTable)
{
if(r == foundRow)
r.Delete();
}
}
dataTable.AcceptChanges();
dataTable and dataSet1.Tables["AnyTable"] are different table.
dataTable is a clone of dataSet1.Tables["AnyTable"]
this code not working..If anyone know how to "find and delete row in datatable" let me know. Thanks in advance
You can use indexOf instead of returning all records to delete the data. You already know which row to delete.
I think that you have deleted one record directly from Pk, this code is more performant.
You must be set PK on Table
ds.Tables["AnyTable"].PrimaryKey = new[] { dt.Columns["YourPKcolumn"] };
DataRow dr = ds.Tables["AnyTable"].Rows.Find(PrimaryKeyValue);
int idx = dt.Rows.IndexOf(dr);
dt.Rows.RemoveAt(idx);
ds.Tables["AnyTable"].AcceptChanges();
Please try this.
var datatable = new DataTable(); // take your datatable
string s = "primaryKeyValue";
DataRow[] row = datatable.Select("name='" + s + "'");
for (int i = row.Length - 1; i >= 0; i--)
{
datatable.Rows.Remove(row[i]);
}
datatable.AcceptChanges();
I am searching a PDF file for a keyword and returning the pages on which that keyword was found. If the keyword IS FOUND, I'm returning a list of pages and the fileName. However, if the keyword was NOT FOUND in the PDF file, I want to deleted the row in the datatable.
public DataTable dtPubSearchResultsFromFiles(string sqlQuery, string safeKeyword)
{
// Returns a datatable of publication search results based on PDF files.
SqlConnection con = new SqlConnection(getConnectionString());
SqlCommand cmd = new SqlCommand(sqlQuery, con);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
dt.Columns.Add("Pages", typeof(string));
da.Fill(dt);
dt.PrimaryKey = new DataColumn[] { dt.Columns["publicationID"] };
foreach (DataRow row in dt.Rows)
{
//call search function to look for keyword
List<int> myPages = new List<int>();
string fileName = row["linkToPublicationPDF"].ToString();
myPages = ReadPdfFile(fileName, safeKeyword);
if (myPages.Count > 0)
{
string pagelist = "";
foreach (int page in myPages)
{
pagelist = pagelist + page + " ";
}
row["Pages"] = pagelist;
}
else
{
//remove/delete the row from the datatable if "myPages.Count" is 0
dt.Rows.Remove(row);
}
}
return dt;
}
When I add this ("dt.Rows.Remove(row)"), I get this error when the page is called "System.InvalidOperationException: Collection was modified; enumeration operation might not execute."
Suggestions? Comments? Fixes? All are welcome...
Bob
Your code is getting some data from the database such that your program can work with it.
The exception you're getting is because your you're modifying (by removing an element) the collection you're iterating on and that's not possible.
You can solve this by creating a temporary List where you store the rows you want to delete. Once you're done with the iteration you can iterate on the temporary list and remove what you decided you don't want anymore.
var toRemove = new List<DataRow>();
foreach (DataRow row in dt.Rows)
{
//call search function to look for keyword
List<int> myPages = new List<int>();
string fileName = row["linkToPublicationPDF"].ToString();
myPages = ReadPdfFile(fileName, safeKeyword);
if (myPages.Count > 0)
{
string pagelist = "";
foreach (int page in myPages)
{
pagelist = pagelist + page + " ";
}
row["Pages"] = pagelist;
}
else
{
//remove/delete the row from the datatable if "myPages.Count" is 0
toRemove.Add(row);
}
}
}
foreach (DataRow row toRemove.Add)
{
dt.Rows.Remove(row);
}
try a simple Delete
row.Delete();
then after the loop
dt.AcceptChanges();
but it will probably fail
see answer from mario
it may work if it is really only marking the row for deletion
Up until now, I had a utility class that contained a function called "getTable", which took a string query and returned a datatable. Now I'm trying to get these queries to be returned via excel. Simply copying the contents of the datatable to excel works fine - however there's no header row on the spreadsheet to explain what each column is.
Below is my attempt:
public static DataTable getTable(String sql, bool includeHeaderRow)
{
DataTable table = new DataTable();
using (SqlDataAdapter adpt = new SqlDataAdapter(sql, getConn()))
{
adpt.Fill(table);
if (includeHeaderRow)
{
DataRow headerRow = table.NewRow();
for (int i = 0; i < table.Columns.Count; i++)
{
headerRow[i] = table.Columns[i].ColumnName;
}
table.Rows.InsertAt(headerRow, 0);
}
}
return table;
}
The above almost works, but an exception is thrown when I try and write a column name (obviously a string) into a non-string column of the datatable.
Any ideas for an easy way to achieve this?
Of course the code may give error, because as you told you are assigning none compatible datatypes into one column with specific datatype, columns at Datatable accepts specific datatypes now if you try to change the column datatype to object by creating a copy of table(cloning it) I think the problem will fix:
public static DataTable getTable(String sql, bool includeHeaderRow)
{
DataTable table = new DataTable();
using (SqlDataAdapter adpt = new SqlDataAdapter(sql, getConn()))
{
adpt.Fill(table);
if (includeHeaderRow)
{
DataTable dt = table.Clone();
for (int i = 0; i < table.Columns.Count; i++)
{
dt.Columns[i].DataType = typeof(Object);
}
foreach (DataRow row in table.Rows)
{
dt.ImportRow(row);
}
DataRow headerRow = dt.NewRow();
for (int i = 0; i < table.Columns.Count; i++)
{
headerRow[i] = table.Columns[i].ColumnName;
}
dt.Rows.InsertAt(headerRow, 0);
}
}
return dt;
}
I may well be looking at this problem backwards but I am curious none the less. Is there a way to build a DataTable from what is currently displayed in the DataGridView?
To be clear, I know you can do this DataTable data = (DataTable)(dgvMyMembers.DataSource); however that includes hidden columns. I would like to build it from the displayed columns only.
Hope that makes sense.
So I ended up trying a combination of a couple of answers as that seemed best. Below is what I am trying. Basically I am creating the DataTable from the DataSource and then working backwards based on if a column is visible or not. However, after it removes a column I get a Collection was modified; enumeration operation may not execute on the next iteration of the foreach.
I am confused as I am not trying to modify the DataGridView, only the DataTable so what's up?
DataTable data = GetDataTableFromDGV(dgvMyMembers);
private DataTable GetDataTableFromDGV(DataGridView dgv)
{
var dt = ((DataTable)dgv.DataSource).Copy();
foreach (DataGridViewColumn column in dgv.Columns)
{
if (!column.Visible)
{
dt.Columns.Remove(column.Name);
}
}
return dt;
}
Well, you can do
DataTable data = (DataTable)(dgvMyMembers.DataSource);
and then use
data.Columns.Remove(...);
I think it's the fastest way. This will modify data source table, if you don't want it, then copy of table is reqired. Also be aware that DataGridView.DataSource is not necessarily of DataTable type.
I don't know anything provided by the Framework (beyond what you want to avoid) that would do what you want but (as I suspect you know) it would be pretty easy to create something simple yourself:
private DataTable GetDataTableFromDGV(DataGridView dgv) {
var dt = new DataTable();
foreach (DataGridViewColumn column in dgv.Columns) {
if (column.Visible) {
// You could potentially name the column based on the DGV column name (beware of dupes)
// or assign a type based on the data type of the data bound to this DGV column.
dt.Columns.Add();
}
}
object[] cellValues = new object[dgv.Columns.Count];
foreach (DataGridViewRow row in dgv.Rows) {
for (int i = 0; i < row.Cells.Count; i++) {
cellValues[i] = row.Cells[i].Value;
}
dt.Rows.Add(cellValues);
}
return dt;
}
one of best solution enjoyed it ;)
public DataTable GetContentAsDataTable(bool IgnoreHideColumns=false)
{
try
{
if (dgv.ColumnCount == 0) return null;
DataTable dtSource = new DataTable();
foreach (DataGridViewColumn col in dgv.Columns)
{
if (IgnoreHideColumns & !col.Visible) continue;
if (col.Name == string.Empty) continue;
dtSource.Columns.Add(col.Name, col.ValueType);
dtSource.Columns[col.Name].Caption = col.HeaderText;
}
if (dtSource.Columns.Count == 0) return null;
foreach (DataGridViewRow row in dgv.Rows)
{
DataRow drNewRow = dtSource.NewRow();
foreach (DataColumn col in dtSource .Columns)
{
drNewRow[col.ColumnName] = row.Cells[col.ColumnName].Value;
}
dtSource.Rows.Add(drNewRow);
}
return dtSource;
}
catch { return null; }
}
First convert you datagridview's data to List, then convert List to DataTable
public static DataTable ToDataTable<T>( this List<T> list) where T : class {
Type type = typeof(T);
var ps = type.GetProperties ( );
var cols = from p in ps
select new DataColumn ( p.Name , p.PropertyType );
DataTable dt = new DataTable();
dt.Columns.AddRange(cols.ToArray());
list.ForEach ( (l) => {
List<object> objs = new List<object>();
objs.AddRange ( ps.Select ( p => p.GetValue ( l , null ) ) );
dt.Rows.Add ( objs.ToArray ( ) );
} );
return dt;
}
I have a row collection (DataRow[] rows). And I want to import all rows to another DataTable (DataTable dt).
But how?
Code
DataTable dt;
if (drs.Length>0)
{
dt = new DataTable();
foreach (DataRow row in drs)
{
dt.Columns.Add(row???????)
}
// If it possible, something like that => dt.Columns.AddRange(????????)
for(int i = 0; i < drs.Length; i++)
{
dt.ImportRow(drs[i]);
}
}
Assuming the rows all have the same structure, the easiest option is to clone the old table, omitting the data:
DataTable dt = drs[0].Table.Clone();
Alternatively, something like:
foreach(DataColumn col in drs[0].Table.Columns)
{
dt.Columns.Add(col.ColumnName, col.DataType, col.Expression);
}
If your DataRows is from a Data Table with Columns defined in it,
DataRow[] rows;
DataTable table = new DataTable();
var columns = rows[0].Table.Columns;
table.Columns.AddRange(columns.Cast<DataColumn>().ToArray());
foreach (var row in rows)
{
table.Rows.Add(row.ItemArray);
}
How about
DataTable dt = new DataTable;
foreach(DataRow dr in drs)
{
dt.ImportRow(dr);
}
Note this only works if drs is a DataRowCollection. Detached rows (not in a DataRowCollection are ignored).
Don't forget to call AcceptChanges.
Try this:
// Assuming you have a DataRow object named row:
foreach(DataColumn col in row.Table.Columns)
{
// Do whatever you need to with these columns
}