Extra Columns While Converting Linq Result into Data Table - c#

When I convert a Linq result into a data table using method below, I get some extra columns!!!
private static DataTable LinqQueryToDataTable(IEnumerable<dynamic> v)
{
var firstRecord = v.FirstOrDefault();
if (firstRecord == null)
return null;
PropertyInfo[] infos = firstRecord.GetType().GetProperties();
DataTable table = new DataTable();
foreach (var info in infos)
{
Type propType = info.PropertyType;
if (propType.IsGenericType
&& propType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
table.Columns.Add(info.Name, Nullable.GetUnderlyingType(propType));
}
else
{
table.Columns.Add(info.Name, info.PropertyType);
}
}
DataRow row;
foreach (var record in v)
{
row = table.NewRow();
for (int i = 0; i < table.Columns.Count; i++)
{
row[i] = infos[i].GetValue(record,null) != null ? infos[i].GetValue(record,null) : DBNull.Value;
}
table.Rows.Add(row);
}
table.AcceptChanges();
return table;
}
For example if Linq result has 5 columns the data table will have 6 or more columns with extra data, I need to skip and delete extra generated columns.
How to do it? Or Am I doing something wrong?

For example if Linq result has 5 columns the data table will have 6 or more columns with extra data
Apparently what you call "Linq result" has these "columns". You can easily check the Linq function that provides that or the parameter v itself with a debugger.
if I connect the result into a grid view everything is OK
First, it's not clear what "grid view" you are talking about. Assuming it's a standard WF or WPF data grid view. As usual with UI, what you see there might not be what you get. The UI components does not use directly reflection as in your code, but System.ComponentModel.TypeDescriptor services. One standard behavior is to suppress properties with BrowsableAttribute = No, which I suspect is your case. But without having your actual Linq result, we can only guess - you are the only one who can check that out.
Anyway, if you are in WF, you can use the following:
private static DataTable LinqQueryToDataTable(IEnumerable<dynamic> v)
{
var firstRecord = v.FirstOrDefault();
if (firstRecord == null)
return null;
var infos = ListBindingHelper.GetListItemProperties(v);
DataTable table = new DataTable();
foreach (PropertyDescriptor info in infos)
{
Type propType = info.PropertyType;
if (propType.IsGenericType
&& propType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
table.Columns.Add(info.Name, Nullable.GetUnderlyingType(propType));
}
else
{
table.Columns.Add(info.Name, info.PropertyType);
}
}
DataRow row;
foreach (var record in v)
{
row = table.NewRow();
for (int i = 0; i < table.Columns.Count; i++)
{
row[i] = infos[i].GetValue(record) ?? DBNull.Value;
}
table.Rows.Add(row);
}
table.AcceptChanges();
return table;
}

Related

How can I save rearranged column positions when reading out a DataGrid?

I have a DataGrid implemented in a WPF-window inside a VSTO-AddIn, which is supposed to preview data to the user before it is pasted to an existing Excel-sheet. The CanUserSortColumns-Property is set to true, so the user is able to rearrange the columns before they are read out and pasted to the sheet. However, when I read out the DataRows from the DataGrid, as shown below, the columns are back in their original position.
object[,] dataArray = new object[allRows, allCols];
for (int r = 0; r < allRows; r++)
{
DataRow row = ClientGrid.Rows[r];
for (int c = 0; c < allCols; c++)
{
dataArray[r, c] = row[c];
}
}
Here is my question: Is there any way to fix this the quick way or at least to track the changes of column display indices in order to rearrange the column order in the code with every change of the columns display order?
I've already tried working something out with the DisplayIndex-property but I did not quite get the hang of the numbers it spits out.
You could order the columns by the DisplayIndex property and use reflection to export the values using reflection, e.g.:
public static void ExportUsingRefection(this DataGrid grid)
{
if (grid.ItemsSource == null || grid.Items.Count.Equals(0))
throw new InvalidOperationException("You cannot export any data from an empty DataGrid.");
IEnumerable<DataGridColumn> columns = grid.Columns.OrderBy(c => c.DisplayIndex);
ICollectionView collectionView = CollectionViewSource.GetDefaultView(grid.ItemsSource);
foreach (object o in collectionView)
{
if (o.Equals(CollectionView.NewItemPlaceholder))
continue;
foreach (DataGridColumn column in columns)
{
if (column is DataGridBoundColumn)
{
string propertyValue = string.Empty;
/* Get the property name from the column's binding */
BindingBase bb = (column as DataGridBoundColumn).Binding;
if (bb != null)
{
Binding binding = bb as Binding;
if (binding != null)
{
string boundProperty = binding.Path.Path;
/* Get the property value using reflection */
PropertyInfo pi = o.GetType().GetProperty(boundProperty);
if (pi != null)
{
object value = pi.GetValue(o);
if (value != null)
propertyValue = value.ToString();
}
}
}
//...
}
}
}
}
Please refer to the following blog post for more information.
How to export data from a DataGrid in WPF: https://blog.magnusmontin.net/2013/09/29/export-data-from-a-datagrid/
I ended up doing the following:
Reading out the DisplayIndex-Property and the initial position of each column and ordering the tuples by the DisplayIndex-Property afterwards
var columnIndices = new List<Tuple<int,int>>();
var colCounter = 0;
foreach (var col in myDataGrid.Columns)
{
columnIndices.Add(Tuple.Create(col.DisplayIndex, colCounter));
colCounter += 1;
}
var sortedColumns = columnIndices.OrderBy(x => x.Item1).ToList();
Creating an empty DataTable and adding the original DataGrid's columns to it. Then I set the new DataTable's DataRow's cells equal to the cells of the DataRows of the original DataGrid.
var myDataTable = new DataTable();
foreach (var index1 in sortedColumns)
{
var columnName = myDataGrid.Columns[index1.Item2].ColumnName;
myDataTable.Columns.Add(columnName, myDataGrid.Columns[index1.Item2].DataType);
}
foreach (DataRow dr in myDataGrid.Rows)
{
var itemArray = new object[allCols];
var indexer = 0;
foreach (var index2 in sortedColumns)
{
itemArray[indexer] = dr[index2.Item2];
indexer += 1;
}
myDataTable.Rows.Add(itemArray);
}

Check empty rows in datatable in Visual Studio 2005, no LINQ

I've uploaded data to a datatable from a excel file. Whats the best way to check empty rows in datatable. NB: Need solution for Visual Studio 2005. Cant use linq.
foreach (DataRow row in result.Rows)
{
//check if row is empty
//if not continue processing data
//else remove the row from datatable
}
Currently the solution in my mind is inside foreach loop, put another foreach loop and check each column. If all columns are null, remove the row from datatable.
Is there any other best method. Does the above method takes more time to execute as there is another for each loop. There will be lots of rows in the datatable.
Edited:
I've got one code:
private bool checkIfRowEmpty(DataRow row)
{
var r = (DataRow)row;
int emptyCount = 0;
int itemArrayCount = r.ItemArray.Length;
foreach (var i in r.ItemArray) if (string.IsNullOrWhiteSpace(i.ToString())) emptyCount++;
if (emptyCount == itemArrayCount) return false;
else return true;
}
Is this okay? Or should I use any solution from below? Which one is good?
Here is one approach - to iterate through all rows and all columns
DataTable table = new DataTable();
List<int> rowsToRemove = new List<int>();
for (int i = 0; i < table.Rows.Count; i++)
{
DataRow row = table.Rows[i];
foreach (DataColumn col in row.Table.Columns)
{
bool skip = false;
if (row[col] != null)
{
//this column is not null, so mark skip flag as true
skip = true;
}
if (skip)
//this row should be skipped because it has at least one column that isn't null
break;
else
rowsToRemove.Add(i);
//mark this row's index for deletion
}
}
//loop through list in reverse order and remove rows by their index
for (int i = rowsToRemove.Count; i > 0; i--)
table.Rows.RemoveAt(i);
Best solution I can think of is the following one:
public bool IsRowEmpty(DataRow row)
{
if (row == null)
return true;
foreach(var value in row.ItemArray)
{
if (value != null)
return false;
}
return true;
}
Then from your main code:
foreach (DataRow row in result.Rows)
{
if(!IsRowEmpty(row))
{
// Row is not empty.
}
}
Hello,
You can simply achive this using DataView : like this -
DataView dv = yourTable.DefaultView;
dv.Sort = "column_1,column_2";
DataTable dtNew = dv.ToTable(true, "column_1", "column_2",...,"column_n"); // please mention all columns here
dtNew.Rows.RemoveAt(0);

How to convert a LINQ query result to a DataTable dynamically?

How to convert a LINQ query result to a DataTable dynamically?
There are solutions where you create another class and specify the column names, but I want the flexibility to change the LINQ structure like column names, column quantities, and have a DataTable generated with the columns names automatically.
Thanks
I've included an extension method that I use with SqlBulkCopy that should do the job, but I'd like to ask why you want to this conversion. There are a very limited number of cases (SqlBulkCopy being one) where a list of objects can't do everything a datatable can. You can use them as binding sources for most controls ... just curious.
public static DataTable toDataTable<T>(this IEnumerable<T> value, List<string> exclusionList)
where T : class
{
var dataTable = new DataTable();
var type = typeof(T);
var properties = type.GetProperties().Where(x => !exclusionList.Contains(x.Name)).ToList();
foreach (var propertyInfo in properties)
{
var propertyType = propertyInfo.PropertyType;
if (!propertyType.IsScalar())
continue;
var nullableType = Nullable.GetUnderlyingType(propertyType);
propertyType = nullableType ?? propertyType;
var dataColumn = new DataColumn(propertyInfo.Name, propertyType);
if (nullableType != null)
dataColumn.AllowDBNull = true;
dataTable.Columns.Add(dataColumn);
}
foreach (var row in value)
{
var dataRow = dataTable.NewRow();
foreach (var property in properties)
{
var safeValue = property.GetValue(row, null) ?? DBNull.Value;
dataRow[property.Name] = safeValue;
}
dataTable.Rows.Add(dataRow);
}
return dataTable;
}
Look into the MoreLinq Nuget package. It has a function ToDataTable()
var LinqResults = from ......;
DataTable dt_Results = LinqResults.ToDataTable();
https://code.google.com/p/morelinq/
It has other VERY useful functions as well:
https://code.google.com/p/morelinq/wiki/OperatorsOverview
They key is to use the LINQ query result as its Implemented IList interface.
If you receive the result as a parameter on a method as an IList object, you can access its columns and rows, this way:
var props = item.GetType().GetProperties();
Refer to this example, it's a small class which please note the it just abstracts the creation of the DataTable, and there is a static method inside called "LINQToDataTable" which you should use.
Step 1, create a class called "GridHelper" (uses System.Data for DataTable structure)
public class GridHelper
{
private DataTable baseDt;
public GridHelper(string tableName)
{
baseDt = new DataTable(tableName);
}
public DataTable getDataTable()
{
return baseDt;
}
public object[,] getObjToFill()
{
object[,] obj = new object[baseDt.Columns.Count, 2];
for (int i = 0; i < baseDt.Columns.Count; i++)
{
obj[i, 0] = baseDt.Columns[i].ColumnName;
}
return obj;
}
public void addColumn(string colName, Type valueType)
{
baseDt.Columns.Add(colName, valueType);
}
public void addRow(object[,] values)
{
DataRow newRow = baseDt.NewRow();
for (int i = 0; i < values.Length / 2; i++)
{
bool colFound = false;
for (int j = 0; j < baseDt.Columns.Count; j++)
{
if (baseDt.Columns[j].ColumnName == values[i, 0].ToString())
{
colFound = true;
break;
}
}
if (colFound == false)
{
throw new Exception("The column " + values[i, 0].ToString() + " has not been added yet.");
}
newRow[values[i, 0].ToString()] = values[i, 1];
}
baseDt.Rows.Add(newRow);
}
public static DataTable LINQToDataTable<T>(T objToList) where T : System.Collections.IList
{
GridHelper ghResult = new GridHelper("Report");
foreach (Object item in objToList)
{
var props = item.GetType().GetProperties();
foreach (var prop in props)
{
ghResult.addColumn(prop.Name, typeof(string));
//prop.Name
//prop.GetValue(item)
}
break;
}
object[,] obj = ghResult.getObjToFill();
foreach (Object item in objToList)
{
var props = item.GetType().GetProperties();
int index = 0;
foreach (var prop in props)
{
//ReportValue(prop.Name, prop.GetValue(item, null));
//prop.Name
obj[index, 1] = prop.GetValue(item);
index++;
}
ghResult.addRow(obj);
}
return ghResult.getDataTable();
}
}
Usage:
var listaReporte =
(from t in dbContext.TablaPruebas
select new
{
Name = t.name,
Score = t.score
}
) .ToList();
DataTable dt = Library.GridHelper.LINQToDataTable(listaReporte);
And that is, use your DataTable as you wish, on a GridView or DataGridView

How to check duplicate rows in dataset?

I have a dataset which has duplicate rows i want my error message to execute when duplicate rows are present.
Below is my code please help
DataSet dsXml = new DataSet();
dsXml.ReadXml(new XmlTextReader(new StringReader(xml)));
Hashtable hTable = new Hashtable();
ArrayList duplicateList = new ArrayList();
foreach (DataRow drow in dsXml.Tables[0].Rows)
{
if (hTable.Contains(drow))
{
duplicateList.Add(drow);
}
else
{
script.Append("alert('Error - There are some Duplicate entries.'); ");
ErrorOcc = true;
if (ErrorOcc)
{
this.ScriptOutput = script + " ValidateBeforeSaving = false;";
this.StayContent = "yes";
return;
}
}
}
Your code is not working, because DataRow instances will be compared by references instead of comparing their fields. You can use custom comparer:
public class CustomDataRowComparer : IEqualityComparer<DataRow>
{
public bool Equals(DataRow x, DataRow y)
{
if (x.ItemArray.Length != y.ItemArray.Length)
return false;
for (int i = 0; i < x.ItemArray.Length; i++)
if (!x[i].Equals(y[i]))
return false;
return true;
}
public int GetHashCode(DataRow obj)
{
int hash = 17;
foreach (object field in obj.ItemArray)
hash = hash * 19 + field.GetHashCode();
return hash;
}
}
or use existing DataRowComparer which compares DataRow objects for equivalence by using value-based comparison:
HashSet<DataRow> set = new HashSet<DataRow>(DataRowComparer.Default);
// or: new HashSet<DataRow>(new CustomDataRowComparer());
foreach (DataRow row in dsXml.Tables[0].Rows)
{
if (!set.Add(row))
// duplicate row
}
You can also check if duplicated rows exist with Linq to DataSet query:
var duplicatedRowsExist = dsXml.Tables[0].AsEnumerable()
.GroupBy(r => r, DataRowComparer.Default)
.Any(g => g.Count() > 1);
You have to compare the content of the rows, not the rows themselves. Something like this should do it:
var hasDupes = dsXml.Tables[0].Rows
.AsEnumerable()
.GroupBy(row => new
{
row.Field<string>("Title"),
row.Field<string>("Address"),
row.Field<string>("State"),
row.Field<string>("City"),
row.Field<int>("Status"),
row.Field<int>("CreatedBy"),
row.Field<int>("UpdatedBy")
})
.Where(g => g.Count() > 1)
.Any();
if(hasDupes)
//Show error message
I think you have to alter you logic a little. You don't add the row to the hTable, so there are never duplicates. And I guess you have to show the message in the end, else the list will not be complete yet.
As stated by others, you do need Sergeys answer to get the comparison to work. If you have that covered, this code will solve the other logic problems.
foreach (DataRow drow in dsXml.Tables[0].Rows)
{
if (!hTable.Contains(drow))
{
hTable.Contains(drow);
hTable.Add(drow);
}
else
{
duplicateList.Add(drow);
}
}
script.Append("alert('Error - There are some Duplicate entries.'); ");
ErrorOcc = true;
if (ErrorOcc)
{
this.ScriptOutput = script + " ValidateBeforeSaving = false;";
this.StayContent = "yes";
return;
}
First, you need to define your comparison between rows. It appears when you create your hTable there is nothing in it, so the hTable.Contains call is always going to return false.
As a side note, you can't just compare a DataRow with another DataRow, it will use the default equality comparison (implemented using IEqualityComparer) and effectively boils down to a reference equality check, which none of the rows will be equal to each other.
Somewhere, you can either implement your own IEqualityCompariosn, or simply write a custom method to check the values of each row.
Here is the answer of my above Question
You can Check duplicate rows in dataset.. it is working fine try it.
DataSet dsXml = new DataSet();
dsXml.ReadXml(new XmlTextReader(new StringReader(xml)));
List<string> duplicateList = new List<string>();
foreach (DataRow drow in dsXml.Tables[0].Rows)
{
string strr = "";
for (int j = 0; j < dsXml.Tables[0].Columns.Count; j++ )
{
strr += drow[j];
}
if (!duplicateList.Contains(strr))
{
duplicateList.Add(strr);
}
else
{
script.Append("alert('Error - There are some Duplicate entries.'); ");
ErrorOcc = true;
if (ErrorOcc)
{
this.ScriptOutput = script + " ValidateBeforeSaving = false;";
this.StayContent = "yes";
return;
}
}
}

How to find rowindex of a datatable into another datatable?

I am using C#. I have two data tables and i want to find the rows of first data table into second data table.
Example.
First data table's data:
1 inam
2 sohan
Second data tables's data:
3 ranjan
1 inam
2 sohan
Now i want to know the index of first two rows of first data table into second data table.
Please help guys.
Any answer or advice
Best Regards
You can use following extension method which returns the first index of a "sub-sequence":
// I've used String.Join to get something that is comparable easily
// from the ItemArray that is the object-array of all fields
IEnumerable<string> first = table1.AsEnumerable()
.Select(r => string.Join(",",r.ItemArray)); //
IEnumerable<string> second = table2.AsEnumerable()
.Select(r => string.Join(",", r.ItemArray));
int index = second.IndexOfSequence(first, null); // 1
Here the extension:
public static int IndexOfSequence<TSource>(this IEnumerable<TSource> input, IEnumerable<TSource> sequence, IEqualityComparer<TSource> comparer)
{
if (input == null) throw new ArgumentNullException("input");
if (sequence == null) throw new ArgumentNullException("sequence");
if (!sequence.Any()) throw new ArgumentException("Sequence must not be empty", "sequence");
if (comparer == null)
{
comparer = EqualityComparer<TSource>.Default;
}
int index = -1;
int firstIndex = -1;
bool found = false;
TSource first = sequence.First();
using (IEnumerator<TSource> enumerator = input.GetEnumerator())
{
using (IEnumerator<TSource> enumerator2 = sequence.GetEnumerator())
{
enumerator2.MoveNext();
while (enumerator.MoveNext())
{
index++;
found = comparer.Equals(enumerator.Current, enumerator2.Current);
if (found && firstIndex == -1) firstIndex = index;
if (found && !enumerator2.MoveNext())
return firstIndex;
}
}
}
return -1;
}
tested with this sample data:
var table1 = new DataTable();
table1.Columns.Add("ID", typeof(int));
table1.Columns.Add("Name");
var table2 = table1.Clone();
table1.Rows.Add(1, "inam");
table1.Rows.Add(2, "Sohan");
table2.Rows.Add(3, "ranjan");
table2.Rows.Add(1, "inam");
table2.Rows.Add(2, "Sohan");
If you don't have much volume this might work....
var tableOneIndex = -1;
var tableTwoIndex = -1;
foreach (var tableOneRow in tableOne.Rows)
{
tableOneIndex++;
foreach (var tableTwoRow in tableTwo.Rows)
{
tableTwoIndex++;
if (tableOneRow["name"].ToString() == tableTwoRow["name"].ToString())
{
// Do whatever you wanted to do with the index values
}
}
}
As a simple solution, this should suffice:
// Create and populate data tables
DataTable dataTable1 = new DataTable();
dataTable1.Columns.Add("Name", typeof(string));
DataRow row1 = dataTable1.NewRow();
row1["Name"] = "Inam";
DataRow row2 = dataTable1.NewRow();
row2["Name"] = "Sohan";
dataTable1.Rows.Add(row1);
dataTable1.Rows.Add(row2);
DataTable dataTable2 = new DataTable();
dataTable2.Columns.Add("Name", typeof(string));
DataRow row3 = dataTable2.NewRow();
row3["Name"] = "Ranjan";
DataRow row4 = dataTable2.NewRow();
row4["Name"] = "Inam";
DataRow row5 = dataTable2.NewRow();
row5["Name"] = "Sohan";
dataTable2.Rows.Add(row3);
dataTable2.Rows.Add(row4);
dataTable2.Rows.Add(row5);
// Loop through rows in first table
foreach (DataRow row in dataTable1.Rows)
{
int rowIndexInSecondTable = 0;
// Loop through rows in second table
for (int i = 0; i < dataTable2.Rows.Count; i++)
{
// Check if the column values are the same
if (row["Name"] == dataTable2.Rows[i]["Name"])
{
// Set the current index and break to stop further processing
rowIndexInSecondTable = i;
break;
}
}
// The index of the row in the second table is now stored in the rowIndexInSecondTable variable, use it as needed, for example, writing to the console
Console.WriteLine("Row with name '" + row["Name"] + "' found at index " + rowIndexInSecondTable.ToString());
}

Categories