DataTable Extension methods C# - c#

I have a method to perform operation in Datatable.
public DataTable SetColumnsOrder(DataTable table, String[] columnNames)
{
int columnIndex = 0;
foreach (var columnName in columnNames)
{
if (table.Columns.Contains(columnName))
{
table.Columns[columnName].SetOrdinal(columnIndex);
columnIndex++;
}
} return table;
}
To access this method I need to do like this
dt = SetColumnsOrder(dt,colNames);
Instead of doing like this, how to create a function to call it like below in c#
dt.SetColumnOrder(colNames);
where the function should take dt as input to perform operations and store back in same dt.

You would need to use an extension method like so:
public static class DataTableExtensions
{
public static DataTable SetColumnsOrder(this DataTable table, string[] columnNames)
{
int columnIndex = 0;
foreach (var columnName in columnNames)
{
if (table.Columns.Contains(columnName))
{
table.Columns[columnName].SetOrdinal(columnIndex);
columnIndex++;
}
}
return table;
}
}
Usage would be:
dt.SetColumnsOrder(columnNames);
And since you're modifying the DataTable, which is a reference type. You can use void as the return type and just access the sorted dt variable

First of all, you don't need to return the same DataTable that you pass in. You could change your method signature to:
public void SetColumnsOrder(DataTable table, String[] columnNames)
and remove the return, and it would still work the same (obviously you'd call it like SetColumnsOrder(dt,colNames); instead of dt = SetColumnsOrder(dt,colNames);. And you should do that, because it's less confusing design.
Then, in order to call it as an extension method, just change the signature again, to:
public static void SetColumnsOrder(this DataTable table, String[] columnNames)
And now you can use it like dt.SetColumnOrder(colNames);.

Change your signature from
public DataTable SetColumnsOrder(DataTable table, String[] columnNames)
to
public static DataTable SetColumnsOrder(this DataTable table, String[] columnNames)

Related

Cannot implicitly convert type 'System.Data.DataTable' to 'System.Collections.Generic.IEnumerable'. An explicit conversion exists [duplicate]

I cannot use AsEnumerable() on DataTable, I'm using C# 3 but I'm just targeting 2.0 framework (LINQ capability is courtesy of LINQBridge). Is there any way I can make DataTable enumerable without using Select() ?
bool isExisting = (bdsAttachments.DataSource as DataTable).Select().Any(xxx => (string)dr["filename"] == filename);
Update:
I wanted it to make it look like this:
bool isExisting = (bdsAttachments.DataSource as DataTable).AsEnumerable().Any(xxx => (string)dr["filename"] == filename);
I'm getting an inkling that the Select method of DataTable returns a copy, I'm thinking to just use AsEnumerable, the problem is I'm just targeting 2.0 framework, System.Data.DataSetExtensions is not available
BTW, i tried this: http://cs.rthand.com/blogs/blog_with_righthand/archive/2006/01/15/284.aspx, but has compilation errors.
public static IEnumerable<DataRow> EnumerateRows(this DataTable table)
{
foreach (var row in table.Rows)
{
yield return row;
}
}
Allows you to call:
bool isExisting = (bdsAttachments.DataSource as DataTable).EnumerateRows().Any(dr => (string)dr["filename"] == filename);
IEnumerable<DataRow> rows = dataTable.AsEnumerable(); (System.Data.DataSetExtensions.dll)
IEnumerable<DataRow> rows = dataTable.Rows.OfType<DataRow>(); (System.Core.dll)
Keeping your enumerator strictly 2.0:
public static IEnumerable<DataRow> getRows(DataTable table)
{
foreach (DataRow row in table.Rows)
{
yield return row;
}
}
Then call with linqbridge like this:
bool isExisting = getRows(bdsAttachments.DataSource as DataTable).Any(row => (string)row["filename"] == filename);
You can try casting the DataTable as IEnumerable and iterate over the set:
//your data exists as a DataTable
DataTable dt = (DataTable)bdsAttachments.DataSource;
foreach (DataRow row in dt)
{
if (row["filename"] == filename)
return row;
}
The foreach will iterate through the list and search of the filename (I assume you're searching for the first DataRow with that filename, not all rows that match the filename).

How to store values in a list or array and bind all to datatable then gridview

private void ListViewAddMethod(string fItem, string sItem, string tItem, string foItem)
{
List<ReportList> alstNames = new List<ReportList>();
alstNames.Add(new ReportList(fItem, sItem, tItem, foItem));
DataTable dt = new DataTable();
dt.Columns.Add("Particulars", typeof(string));
dt.Columns.Add("Amount", typeof(string));
dt.Columns.Add("Particulars1", typeof(string));
dt.Columns.Add("Amount1", typeof(string));
foreach (var lstNames in alstNames)
{
// Add new Row - inside the foreach loop - to enable creating new row for each object.
DataRow row = dt.NewRow();
row["Amount"] = fItem;
row["Particulars"] = sItem;
row["Particulars1"] = tItem;
row["Amount1"] = foItem;
dt.Rows.Add(row);
dgvProfitAndLoss.DataSource = alstNames;
dgvProfitAndLoss.DataBind();
}
}
This returns only the most recent row added and ignores the others. How do I create an array from parameters passed to a method and bind them to a gridview?
Your code does not look good. First, you are doing a loop over the alstNames and binding it into a gridview over each iteration of this loop. Second, you do not need a DataTable to bind it into a grid and your code you are just creating a DataTable on the heap, adding some Rows and keep it to Garbage Collector remove it, you are not even using it. Third, considering you are doing a method to add a new item on the GRidView, remove the initilization of your list to the scope of your class and keep it a valid instance (or static one if it is the case):
// declare the list output of the method
private private List<ReportList> alstNames = new List<ReportList>();
private void ListViewAddMethod(string fItem, string sItem, string tItem, string foItem)
{
// add the ReportList on the list
alstNames.Add(new ReportList(fItem, sItem, tItem, foItem));
// bind it on the gridView
dgvProfitAndLoss.DataSource = alstNames;
dgvProfitAndLoss.DataBind();
}

The given DataRow is not in the current DataRowCollection when tried move item from DataGridView to another

I have two DataGridView like this:
Button selected will move DataRow of DataGridView to another DataGridView.
My code like this:
public static DataRow[] GetSelectedDataRows(DataGridView grid)
{
DataRow[] dRows = new DataRow[grid.SelectedRows.Count];
for (int i = 0; i < grid.SelectedRows.Count; i++)
dRows[i] = ((DataRowView)grid.SelectedRows[i].DataBoundItem).Row;
return dRows;
}
public void MoveRows(DataTable src, DataTable dest, DataRow[] rows)
{
foreach (DataRow row in rows)
{
// add to dest
dest.Rows.Add(row.ItemArray);
// remove from src
src.Rows.Remove(row);
}
}
I use it in Event btnMoveToRight_Click().
MoveRows(dtUser, dtUserStop, GetSelectedDataRows(dgvUser));
It throws exception error like:
The given DataRow is not in the current DataRowCollection
at line:
src.Rows.Remove(row);
You can't change the Table field of a DataRow.
So you need to create a new row and also copy the data separately, maybe like this:
public void MoveRows(DataTable src, DataTable dest, DataRow[] rows)
{
foreach (DataRow row in rows)
{
// create empty row
DataRow newrow = dest.NewRow();
// copy data
newrow.ItemArray = row.ItemArray;
// add to dest
dest.Rows.Add(newrow); // (*)
// remove from src NOTE: This may or may not throw an RowNotInTableException
// to avoid it you can skip the Remove and use the loop below instead..
src.Rows.Remove(row);
}
// alternative way of removing the rows..
//foreach (DataGridViewRow item in this.dgvUser.SelectedRows)
//{
// dgvUser.Rows.RemoveAt(item.Index);
//}
}
(*) An even shorter way to clone a DataRow is using this overload of the Add method:
dest.Rows.Add(row.ItemArray);
No need for the newrow now. Note that you may want to copy the RowState from the old to the new row!? See here and here for examples of changing the RowState
If you find you need to remove the DataRows via removing the DataGridViewRows you should add the DGV to the parameters of the MoveRows function to avoid an unecessary dependence..!
Of course this assumes that the DataTables have the same structure, maybe because one is a (structural) clone of the other:
dest = src.Clone();
DataRow has a DataTable property on it (DataRow.Table).
This leads me to think that adding the row to a new table breaks the association with the old DataTable, so you don't need to call src.Rows.Remove(row).

DataColumn Name from DataRow (not DataTable)

I need to iterate the columnname and column datatype from a specific row. All of the examples I have seen have iterated an entire datatable. I want to pass a single row to a function to do a bunch of conditional processing. I want to separate the conditional processing for ease of readability.
This is what I have:
private void doMore(DataRow dr)
{
foreach (DataColumn c in dr.ItemArray) //loop through the columns.
{
MessageBox.Show(c.ColumnName.ToString());
}
}
The error returned is
System.InvalidCastException: Unable to cast object of type 'System.String' to type 'System.Data.DataColumn'.
How would I get the column name from the row or do I have no choice and must pass the entire datatable to the function?
You would still need to go through the DataTable class. But you can do so using your DataRow instance by using the Table property.
foreach (DataColumn c in dr.Table.Columns) //loop through the columns.
{
MessageBox.Show(c.ColumnName);
}
You can make it easier in your code (if you're doing this a lot anyway) by using an extension on the DataRow object, like:
static class Extensions
{
public static string GetColumn(this DataRow Row, int Ordinal)
{
return Row.Table.Columns[Ordinal].ColumnName;
}
}
Then call it using:
string MyColumnName = MyRow.GetColumn(5);
You need something like this:
foreach(DataColumn c in dr.Table.Columns)
{
MessageBox.Show(c.ColumnName);
}
use DataTable object instead:
private void doMore(DataTable dt)
{
foreach(DataColumn dc in dt.Columns)
{
MessageBox.Show(dc.ColumnName);
}
}

How to Avoid Explicit Casting using Generics

I have a routine which converts all the data in a DataGridView to a corresponding DataTable. I want to extend this method to incorporate the ability to convert just the selected range of the DataGridView. I can clearly do this using basic logic if (bIsSelection) /*Do stuff*/ else /*Do other stuff*/ but I would like to use generics here. The problem is that the full DataGridView range is a DataGridViewColumnCollection and the selected range will be a DataGridViewSelectedColumnCollection and C# does not seem to like any conversion between the two, or allow implicit typing in the case of generics.
The first part of my code was
public static DataTable BuildDataSetFromDgv(DataGridView _dataGridView,
string strTabName)
{
DataTable dt = new DataTable();
dt.TableName = strTabName;
foreach (DataGridViewColumn col in _dataGridView.Columns)
dt.Columns.Add(col.DataPropertyName, col.ValueType);
and I have attempted
public static DataTable BuildDataSetFromDgv<T>(DataGridView _dataGridView,
string strTabName, ICollection<T> _columnColl, ICollection<T> _rowColl)
{
DataTable dt = new DataTable();
dt.TableName = strTabName;
//foreach (DataGridViewColumn col in _dataGridView.Columns)
foreach (DataGridViewColumn col in _columnColl)
dt.Columns.Add(col.DataPropertyName, col.ValueType);
which does not work due to the implicit conversion, and changing _columnColl to _columnColl as DataGridView[Selected]ColumnCollection defeats the objective.
How do I make this work with generics in this case?
Thanks for your time.
I am not sure why you need a generic one - both are collection of DataGridViewColumn. For example, why below shouldn't work for you
public static DataTable BuildDataSetFromDgv(DataGridView _dataGridView,
string strTabName, IEnumerable columns)
{
DataTable dt = new DataTable();
dt.TableName = strTabName;
var dvgColumns = columns.Cast<DataGridViewColumn>();
foreach (var col in dvgColumns)
dt.Columns.Add(col.DataPropertyName, col.ValueType);

Categories