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;
}
}
Related
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'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
I can't work out how to get the Type of a LINQ result. My set contains both strings and bools, so I run into trouble when I try to act on the rows. I attached an incredibly rough workout using try/catch (for a laugh), but it hurts my soul and would much rather know the proper method in obtaining the Type.
private AppointmentInfoClass UpdateDataContext(DataSet phaseDataSet) {
var phaseCollection = new AppointmentInfoClass();
var Type = phaseCollection.GetType();
var properties = Type.GetProperties();
var result = from DataRow myRow in DataBindings.CompanyAppsDataSet.Tables[0].Rows
where (int)myRow["AppointmentID"] == ApptID
select myRow;
var k = 0;
foreach (DataRow row in phaseDataSet.Tables[0].Rows) {
string header;
header = row.Field<string>("Header");
foreach (var field in result) {
try {
properties[k].SetValue(phaseCollection, field.Field<string>(header));
}
catch (Exception) {
properties[k].SetValue(phaseCollection, field.Field<bool>(header).ToString());
}
}
k++;
}
return phaseCollection;
}
It will return the type you have written instead of Type
string s = field.Field<string>("ColumnName");
bool b = field.Field<bool>("ColumnName");
How to convert datatable into list of generic type. Below is the scenario.
I have datatable with name table1 and contains columns col1,col2. how could we convert this table into a list of type name table1bj(which can be different per the datatable name) with properties col1 and col2 with compatible datatype as of datatable column data types.
There are many post on SO but these are with the converting datatable into predefined object list. Here in my case I have to generate object and list dynamically from the datatable. Thanks.
Assuming that you've already created the class table1bj (consider to make it uppercase due to .NET naming conventions) with two properties col1,col2 (the same). You just have to use Enumerable.Select to create instances of this class and ToList to create a generic List<table1bj>:
List<table1bj> result = table1.AsEnumerable()
.Select(row => new table1bj
{
col1 = row.Field<string>("col1"),
col1 = row.Field<string>("col1")
}
).ToList();
I have also presumed that these properties are strings, otherwise use the correct type with the Field extension method. If you don't know the type you should stay with your DataTable since it's already an in-memory collection with dynamic types.
You can do like this...
Create Class with properties :
public class table1bj
{
public string col1{ get; set; }
public string col2{ get; set; }
}
Convert DataTable to Generic Type :
List<table1bj> Objtable1bj = table1.ToCollection<table1bj>();
I know this question asked many times ago, but also I need a solutions for convert data table to dynamic or generic types in one method and I can't find answer for this, so post my answer.
You can use a extension method to convert data table to any type like below:
public static class Extension
{
public static IList<T> ToList<T>(this DataTable dt, bool isFirstRowColumnsHeader = false) where T : new()
{
var results = new List<T>();
if (dt != null && dt.Rows.Count > 0)
{
var columns = dt.Columns.Cast<DataColumn>().ToList();
var rows = dt.Rows.Cast<DataRow>().ToList();
var headerNames = columns.Select(col => col.ColumnName).ToList();
//
// Find properties name or columns name
if (isFirstRowColumnsHeader)
{
for (var i = 0; i < headerNames.Count; i++)
{
if (rows[0][i] != DBNull.Value && !string.IsNullOrEmpty(rows[0][i].ToString()))
headerNames[i] = rows[0][i].ToString();
}
//
// remove first row because that is header
rows.RemoveAt(0);
}
// Create dynamic or anonymous object for `T type
if (typeof(T) == typeof(System.Dynamic.ExpandoObject) ||
typeof(T) == typeof(System.Dynamic.DynamicObject) ||
typeof(T) == typeof(System.Object))
{
var dynamicDt = new List<dynamic>();
foreach (var row in rows)
{
dynamic dyn = new ExpandoObject();
dynamicDt.Add(dyn);
for (var i = 0; i < columns.Count; i++)
{
var dic = (IDictionary<string, object>)dyn;
dic[headerNames[i]] = row[columns[i]];
}
}
return (dynamic)dynamicDt;
}
else // other types of `T
{
var properties = typeof(T).GetProperties();
if (columns.Any() && properties.Any())
{
foreach (var row in rows)
{
var entity = new T();
for (var i = 0; i < columns.Count; i++)
{
if (!row.IsNull(columns[i]))
{
typeof(T).GetProperty(headerNames[i])? // ? -> maybe the property by name `headerNames[i]` is not exist in entity then get null!
.SetValue(entity, row[columns[i]] == DBNull.Value ? null : row[columns[i]]);
}
}
results.Add(entity);
}
}
}
}
return results;
}
}
We can do it by Reflection also, this method helps to set ClassObject properties by DataTable:
using System.Reflection;
public void SetObjectProperties(object objClass, DataTable dataTable)
{
DataRow _dataRow = dataTable.Rows[0];
Type objType = objClass.GetType();
List<PropertyInfo> propertyList = new List<PropertyInfo>(objType.GetProperties());
foreach (DataColumn dc in _dataRow.Table.Columns)
{
var _prop = propertyList.Where(a => a.Name == dc.ColumnName).Select(a => a).FirstOrDefault();
if (_prop == null) continue;
_prop.SetValue(objClass, Convert.ChangeType(_dataRow[dc], Nullable.GetUnderlyingType(_prop.PropertyType) ?? _prop.PropertyType), null);
}
}
In my project i have a fill method.
public IList<MyModel> Fill(DataTable dt)
{
IList<MyModel> IProperty = new List<MyModel>();
for (int i = 0; i < dt.Rows.Count; i++)
{
MyModel Property = new MyModel
{
Name= dt.Rows[i]["Name"].ToString(),
Surname = dt.Rows[i]["Surname"].ToString(),
Age = dt.Rows[i]["Age"].ToString(),
};
IProperty.Add(Property);
}
return IProperty;
}
It fill my model from datatable. But i must write this fill method for all model. I dont want to write this method all the time. I need a solution for this. I'm open to any kind of proposal
Not:I dont want to use Entity framework or other ORM frameworks.
If you can make sure the field names and properties of the object with the same name, try this:
public IEnumerable<T> Fill<T>(DataTable dt)
where T : new()
{
return dt.AsEnumerable().Select(row =>
{
var obj = new T();
var properties =
from p in obj.GetType().GetProperties()
join c in dt.Columns.Cast<DataColumn>() on p.Name equals c.ColumnName
select new {p, c};
foreach (var item in properties)
item.p.SetValue(obj, row[item.c]);
return obj;
});
}