I'm calling WCF Service that gives me a list of customers with specified field names in the BAL. I created a method ToDataTable as instructed on many forums (it might be wrong for this instance). I use it to convert the list into a datatable but there is a challenge that I'm facing. The error says 'Cannot implicitly convert type 'System.Data.DataTable to mHotRes.DesktopPresentation.ListFrm.ListType'.
Here is my code for binding data:
private void BindData()
{
try
{
switch (_ListType)
{
case ListType.Customers:
IHotRes res = new MHotServiceProvider().Service;
List<Customer> customer = res.CustomerSaveDataList();
_ListType = ToDataTable(customer); //the problem occurs here
break;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Here is the code for ToDataTable method:
public static DataTable ToDataTable<T>(List<T> items)
{
DataTable dataTable = new DataTable(typeof(T).Name);
//Get all the properties
PropertyInfo[] Props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo prop in Props)
{
//Setting column names as Property names
dataTable.Columns.Add(prop.Name);
}
foreach (T item in items)
{
var values = new object[Props.Length];
for (int i = 0; i < Props.Length; i++)
{
//inserting property values to datatable rows
values[i] = Props[i].GetValue(item, null);
}
dataTable.Rows.Add(values);
}
//put a breakpoint here and check datatable
return dataTable;
}
If any more code samples are needed let me know.
Your reflection ToDataTable code is working correctly:
_ListType = ToDataTable(customer); //the problem occurs here
The problem is that the _ListType have different type from DataTable.
You should change the line to
DataTable tbl = ToDataTable(customer);//Your method returns DataTable
Related
I have been trying something with LINQ to get all the data from a table customers in a data table. I have used SQL to LINQ class here.
SampleDataContext sdc = new SampleDataContext();
DataTable Cust = new DataTable();
// var query = from c in sdc.customers
// select c;
var query = (IEnumerable<DataRow>)sdc.customers.Select(c => c);
Cust = query.CopyToDataTable();
Console.WriteLine(Cust);
Now when I run this, I get an exception:
Unable to cast object of type 'System.Data.Linq.DataQuery1[BasicLearning.customer]' to type 'System.Collections.Generic.IEnumerable1[System.Data.DataRow]'
Why am I getting this exception?
ANS - from my understanding, which may be wrong, the (IEnumerable<DataRow>) casting in above logic won't work because the result set is not an IEnumerable but IQueryable. But I am not sure if I understand this exception correctly.
So, here I am stuck now, on how to get this code running. I want to use CopyToDataTable() method. But Casting is not working.
It would be great if anyone could help me understand what I am doing wrong here and how to correct it.
as #Svyatoslav Danyliv mention you need to convert object to table. there is an extention which dynamically convert object to datatable.
void Main()
{
DataTable Cust = new DataTable();
//var query = from c in sdc.customers
// select c;
var query = Customers.Select(c => c).ConvertClassToDataTable<Customer>().AsEnumerable();
Cust = query.CopyToDataTable();
Console.WriteLine(Cust);
}
public static class Extensions
{
public static DataTable ConvertClassToDataTable<T>(this IEnumerable<T> list)
{
Type type = typeof(T);
var properties = type.GetProperties();
DataTable dataTable = new DataTable();
foreach (PropertyInfo info in properties)
{
dataTable.Columns.Add(new DataColumn(info.Name, Nullable.GetUnderlyingType(info.PropertyType) ?? info.PropertyType));
}
foreach (T entity in list)
{
object[] values = new object[properties.Length];
for (int i = 0; i < properties.Length; i++)
{
values[i] = properties[i].GetValue(entity);
}
dataTable.Rows.Add(values);
}
return dataTable;
}
}
I'm trying to convert a gridview datasource to a datatable.
What I have tried so far
dt = (DataTable)GridCanvas.DataSource; // Unable to cast object of type 'System.Collections.Generic.List`1 to type 'System.Data.DataTable'
Also I have tried this
Unable to cast object of type 'System.Collections.Generic.List`1[CRM.Models.Leads]' to type 'System.Windows.Forms.BindingSource'
BindingSource bindingSource = (BindingSource)GridCanvas.DataSource;
dt = (DataTable)bindingSource.DataSource;
And this
'Object reference not set to an instance of an object.'
dt = GridCanvas.DataSource as DataTable;
I'm populating my gridview in the following way
var dispatchLeads = await API.Zelkon.Leads.Dispatch.Leads(Variables.Agent.username);
GridCanvas.DataSource = dispatchLeads;
I'm trying to avoid loop solutions. Hope someone has an idea how to solve this. Thanks!
First get the List<Leads> from GridCanvas as
List<Leads> data=(List<Leads>)GridCanvas.DataSource;
Then Convert the List<Leads> to DataTable as;
DataTable dt=ToDataTable<Leads>(data);
use following methods for conversion.
public static DataTable ToDataTable<T>(List<T> items)
{
DataTable dataTable = new DataTable(typeof(T).Name);
//Get all the properties
PropertyInfo[] Props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo prop in Props)
{
//Defining type of data column gives proper data table
var type = (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>) ? Nullable.GetUnderlyingType(prop.PropertyType) : prop.PropertyType);
//Setting column names as Property names
dataTable.Columns.Add(prop.Name, type);
}
foreach (T item in items)
{
var values = new object[Props.Length];
for (int i = 0; i < Props.Length; i++)
{
//inserting property values to datatable rows
values[i] = Props[i].GetValue(item, null);
}
dataTable.Rows.Add(values);
}
//put a breakpoint here and check datatable
return dataTable;
}
I have trouble with the following (test) code. This gives me a "Parameter Count Mismatch" error at the line
dataTable.Merge(CreateDataTableFromObject(info.GetValue(inputObject)));
The entire code looks like this:
public object SerializeThis(DataTable dataTable1, DataTable dataTable2)
{
string jsonString = #"{'EquipmentNumber':'CP5301078','Data_General_Exp': {'Authgrp':'CP01','Objecttype':'9A1B'}}";
var jConvertObejct = (JsonConvertObject)JsonConvert.DeserializeObject(jsonString, typeof(JsonConvertObject));
var jObject = JObject.Parse(jsonString);
dataTable1 = CreateDataTableFromObject(jConvertObejct);
dataTable2 = CreateDataTableFromObject(jObject);
return jConvertObejct;
}
public DataTable CreateDataTableFromObject(object inputObject)
{
DataTable dataTable = new DataTable();
Type type = inputObject.GetType();
var properties = type.GetProperties();
PropertyInfo info;
for (int i = 0; i < properties.Length; i++)
{
info = properties[i];
if (info.GetValue(inputObject).GetType().GetProperties().Count() > 2)
dataTable.Merge(CreateDataTableFromObject(info.GetValue(inputObject)));
else
if (!dataTable.Columns.Contains(info.Name))
dataTable.Columns.Add(new DataColumn(info.Name, Nullable.GetUnderlyingType(info.PropertyType) ?? info.PropertyType));
}
return dataTable;
}
Note that I am trying to do the same thing with both the JsonConvert object and the JObject - the error is emerging when executing the
CreateDataTableFromObject(object inputObject)
on the JObject object and not on the JsonConvert object.
I need a solution for the JObject as I have to handle some unknown json strings, which I need to put in to a DataTable (column names being the property names and row values being the values of the json objects). I have omitted the usings.
I don't see that this is answered by any of the other stackoverflow articles.
OK - I found that I had tangled things a bit up. And came to this solution:
public static DataTable DeSerializeThis(string jsString)
{
const string json1 = #"{""EquipmentNumber"":""CP1"",""Authgrp"":""CP01"",""Objecttype"":""9A1A""}";
const string json2 = #"{""EquipmentNumber"":""CP2"",""Authgrp"":""CP02"",""Objecttype"":""9B1B""}";
List<JObject> list = new List<JObject>();
list.Add(JObject.Parse(json1));
list.Add(JObject.Parse(json2));
DataTable table = ToDataTable(list);
return table;
}
static public DataTable ToDataTable(List<JObject> list)
{
DataTable dataTable = new DataTable();
int i = 0;
foreach (JToken content in list.ToList<JToken>())
{
dataTable.Rows.Add();
foreach (JProperty prop in content)
{
if (i == 0)
{
dataTable.Columns.Add(prop.Name);
}
dataTable.Rows[i][prop.Name] = prop.Value;
}
i++;
}
return dataTable;
}
Only now is the question if this could be re-written so that the
ToDataTable(List<JObject> list)
Could be of a
List<T>
instead - I haven't found the answer for that...
I can't seem to get passed this error message using this code.
System.Data.DuplicateNameException: A column named 'Url' already belongs to this DataTable.
foreach (var user in users.Users)
{
using (DataTable table = new DataTable())
{
table.Clear();
for (int i = 0; i < users.Users[0].GetType().GetProperties().Count(); i++)
{
table.Columns.Add(users.Users[0].GetType().GetProperties()[i].Name);
Console.WriteLine(table.Columns.Add(users.Users[0].GetType().GetProperties()[i].Name));
}
}
}
Any ideas?
UPDATE
var properties = users.Users[0].GetType().GetProperties();
for (int i = 0; i < properties.Count(); i++)
{
table.Columns.Add(properties[i].Name, typeof(String));
}
Because I didn't know what the type was to use the foreach. This:
var properties = users.Users[0].GetType().GetProperties();
foreach (PropertyInformation prop in properties)
{
table.Columns.Add(properties.);
Console.WriteLine(prop.Name);
}
gave me Cannot convert type 'System.Reflection.PropertyInfo' to 'System.Configuration.PropertyInformation'
Anyway I can figure out how to get the correct type in there? #barrick
The error won't lie. In the Console.WriteLine() call, you're adding the column again, which will cause the problem.
Try:
var properties = users.Users[0].GetType().GetProperties();
foreach (PropertyInfo prop in properties)
{
table.Columns.Add(prop.Name);
Console.WriteLine(prop.Name);
}
I have a strongly typed class PersonExport. I initially get data into a DataTable and call the following method on the DataTable to convert it to List<PersonExport>:
public static List<T> ConvertToList<T>(DataTable dt, out string message)
{
message = string.Empty;
var list = new List<T>();
try
{
var columnNames = dt.Columns.Cast<DataColumn>()
.Select(c => c.ColumnName)
.ToList();
var properties = typeof(T).GetProperties();
list = dt.AsEnumerable().Select(row =>
{
var objT = Activator.CreateInstance<T>();
foreach (var pro in properties)
{
if (columnNames.Contains(pro.Name))
{
var value = row[pro.Name];
var typeName = value.GetType().FullName;
if (typeName == "MySql.Data.Types.MySqlDateTime")
{
var mySqlDateTime = (MySqlDateTime) value;
if (mySqlDateTime.IsValidDateTime)
{
value = Convert.ToDateTime(mySqlDateTime.ToString());
pro.SetValue(objT, value, null);
}
}
else
{
pro.SetValue(objT, row.IsNull(pro.Name) ? null : value, null);
}
}
}
return objT;
}).ToList();
}
catch (Exception ex)
{
message = (ex.InnerException != null) ? ex.InnerException.Message : ex.Message;
}
return list;
}
However, once I start removing columns from the DataTable returned, it no longer works because the columns in the DataTable don't match up with the properties in the PersonExport list.
I am eventually using the exported list here to export to excel, but it is not working since I have modified by DataTable and it can't Deserialize into a List<PersonExport>:
//Trying to get data into List<object>
List<object> persons = GetPersonExport(query, out message);
var exportData = new Dictionary<string, List<object>> { { "xldata", persons} };
//Deserialize to List<object> to export
var persons = JsonConvert.DeserializeObject<List<object>>(args["xldata"]);
The above line just returns a List of empty objects.
A few things got me thinking, but I am wondering what might be the best approach. I am using the EPPLUS library to export data to excel and it has the option of hiding columns, so would it be better to just export the whole object and hide columns you don't want, this way you avoid the anonymous type or what I can do is still get the whole object, but then convert it to a DataTable and then remove the columns? Thoughts?
All that you want is:
public IEnumerable<object> GetListOfObject()
{
foreach (var prod in TenMostExpensiveProducts().Tables[0].AsEnumerable())
{
yield return prod;
}
}
Or:
TenMostExpensiveProducts().Tables[0].AsEnumerable().Select (x => x).ToList<object>()
But you can get it work more elegant via linq like this:
from prod in TenMostExpensiveProducts().Tables[0].AsEnumerable()
where prod.Field<decimal>("UnitPrice") > 62.500M
select prod
Or like this (AsDynamic is called directly on DataSet):
TenMostExpensiveProducts().AsDynamic().Where (x => x.UnitPrice > 62.500M)
I prefer the last approach while is is the most flexible.
P.S.: Don't forget to connect System.Data.DataSetExtensions.dll reference
List<Dictionary<string, object>> rows = new List<Dictionary<string, object>>();
Dictionary<string, object> row;
foreach (DataRow dr in dt.Rows)
{
row = new Dictionary<string, object>();
foreach (DataColumn col in dt.Columns)
{
row.Add(col.ColumnName, dr[col]);
}
rows.Add(row);
}
StringBuilder sbRes = new StringBuilder();
jSon.Serialize(rows, sbRes);
ret = sbRes.ToString();