Invalid Arguments Error Using Generics - c#

I am trying to use the function found here:
DataTable to List<object>
public static IList<T> ConvertTo<T>(DataTable table)
{
if (table == null)
return null;
List<DataRow> rows = new List<DataRow>();
foreach (DataRow row in table.Rows)
rows.Add(row);
return ConvertTo<T>(rows);
}
the return statement is giving me an exception stating:
The best overloaded method match for 'Utilities.Utility.ConvertTo<T>(System.Data.DataTable)' has some invalid arguments
Can someone help me fix this error??

Don't do it that way use Marc's amazing function ( https://stackoverflow.com/a/545429/215752 ) I mean really... that is exactly what you want.. right?
You need another function
public static IList<T> ConvertTo<T>(IList<DataRow> rows)
{
IList<T> list = null;
if (rows != null)
{
list = new List<T>();
foreach (DataRow row in rows)
{
T item = CreateItem<T>(row);
list.Add(item);
}
}
return list;
}
The question you link had a link in it. I'd suggest looking there for more info:
http://lozanotek.com/blog/archive/2007/05/09/Converting_Custom_Collections_To_and_From_DataTable.aspx
There you will find all the code, which I expect will compile. I won't vouch for the reliability or reasonably of using it however.

First, you are returning a T but not a IList<T>. Secondly, how are you expecting a DataRow to be converted to an unknown T, especially if a row has several columns?
Try something like this
public static IList<IList<T>> ConvertTo<T>(DataTable table)
{
if (table == null)
return null;
List<IList<T>> rows = new List<IList<T>>();
foreach (DataRow row in table.Rows) {
rows.Add(row.ItemArray.Cast<T>().ToArray());
}
return rows;
}
UPDATE:
A custom object is something like this
public class Employee
{
public int ID { get; set; }
public string Name { get; set; }
public decimal Salary { get; set; }
}
However, in that case a generic interface is not useful, since you would have to code for that specific class
public static IList<Employee> GetEmployees(DataTable table)
{
var employees = new List<Employee>();
if (table != null) {
foreach (DataRow row in table.Rows) {
var emp = new Employee();
emp.ID = (int)row["ID"];
emp.Name = (string)row["Name"];
emp.Salary = (decimal)row["Salary"];
employees.Add(emp);
}
}
return employees;
}
This code has to be different for different tables and cannot be generic. At least not without using Reflection and assuming that the properties have the same names as the table columns.
A solution not using some tricky Reflection code or other magic tricks would be to define an interface like this
public interface IDataObject
{
void FillFromRow(DataRow row);
}
Then you declare Employee or any other data classes like this
public class Employee : IDataObject
{
public int ID { get; set; }
public string Name { get; set; }
public decimal Salary { get; set; }
public void FillFromRow(DataRow row)
{
ID = (int)row["ID"];
Name = (string)row["Name"];
Salary = (decimal)row["Salary"];
}
}
Now you can use generics again
public static IList<T> GetItems<T>(DataTable table)
where T : IDataObject, new()
{
var items = new List<T>();
if (table != null) {
foreach (DataRow row in table.Rows) {
T item = new T();
item.FillFromRow(row);
items.Add(item);
}
}
return items;
}

Related

How do I use reflection to dynamically call a type?

I have a DataGrid and I need to convert it to a DataTable. The problem is that I need To be able to get the type of my DataSource dynamically. The grid DataSource is of type 'Observable' which is a class I have in my project, but my task is to be able to dynamically create the DataTable without needing to specify the type only using the DataSource. How can I generate a method that I can use to place in <T> without getting the error " 'mytype' is a variable but is used like a type" .
Type mytype = Grid_Job.DataSource.GetType();
DataTable dt = CreateDataTable<mytype>((IEnumerable<mytype>)Grid_Job.DataSource);
public static DataTable CreateDataTable<T>(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;
}
My Observable class is
public class Observable
{
public int JobNo { get; set; }
public string JobName { get; set; }
public string JobDescription { get; set; }
public string Job_Type { get; set; }
public string Job_Status { get; set; }
}
Effectively, you need to discard the generics and think in terms of the collection at runtime - discovering the element type itself. For example:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Reflection;
static class P
{
static object GetData()
=> new ObservableCollection<Observable>
{
new Observable { JobName = "abc", JobNo = 123 },
new Observable { JobName = "def", JobNo = 456 },
new Observable { JobName = "ghi", JobNo = 789 },
};
static void Main()
{
// note that we don't know *anything* about the data source here
object dataSource = GetData();
DataTable dt = CreateDataTable((IEnumerable)dataSource);
foreach (DataColumn col in dt.Columns)
{
Console.Write(col.ColumnName);
Console.Write("\t");
}
Console.WriteLine();
foreach (DataRow row in dt.Rows)
{
foreach (DataColumn col in dt.Columns)
{
Console.Write(row[col]);
Console.Write("\t");
}
Console.WriteLine();
}
}
public static DataTable CreateDataTable(IEnumerable list)
{
Type type = GetElementType(list.GetType());
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));
}
object[] values = new object[properties.Length];
foreach (object entity in list)
{
for (int i = 0; i < properties.Length; i++)
{
values[i] = properties[i].GetValue(entity);
}
dataTable.Rows.Add(values);
}
return dataTable;
}
static Type GetElementType(Type type)
{
foreach (Type interfaceType in type.GetInterfaces())
{
if (interfaceType.IsGenericType &&
interfaceType.GetGenericTypeDefinition()
== typeof(IList<>))
{
return type.GetGenericArguments()[0];
}
}
return null;
}
}
public class Observable
{
public int JobNo { get; set; }
public string JobName { get; set; }
public string JobDescription { get; set; }
public string Job_Type { get; set; }
public string Job_Status { get; set; }
}
Note that in some cases you should prefer the TypeDescriptor model over reflection, as this allows runtime definition of properties; this is a niche area and probably doesn't apply to you, but it relevant to know about. As an example, this is how DataTable itself chooses to expose the properties it has as discoverable at runtime to general purpose tools. There are also a few list indirection APIs that need to be considered in those cases.

how to call the generic method IEnumerator<> IEnumerable<>.GetEnumerator()?

For Some debug i need to get the list of SqlDataRecord generated by the method IEnumerator IEnumerable.GetEnumerator() of the class TVPDataCollection
this code :
TVPDataCollection<AttributiDocumento> oAttributiDocumentoList = new TVPDataCollection<AttributiDocumento>();
AttributiDocumento doc1 = new AttributiDocumento();
AttributiDocumento doc2 = new AttributiDocumento();
oAttributiDocumentoList.Add(doc1);
oAttributiDocumentoList.Add(doc2);
var x = oAttributiDocumentoList.GetEnumerator();
x is a list of AttributiDocumento, but i need a list of SqlDataRecord because i want to inspect the values. what's the way to call the GetEnumerator() that returns SqlDataRecord?
public class TVPDataCollection<T> : List<T>, IEnumerable<SqlDataRecord> where T : class
{
IEnumerator<SqlDataRecord> IEnumerable<SqlDataRecord>.GetEnumerator()
{
List<SqlMetaData> records = new List<SqlMetaData>();
var properties = typeof(T).GetProperties();
foreach (var prop in properties)
{
SqlType oSqlType = GetSqlType(prop);
if (oSqlType.UseSize)
records.Add(new SqlMetaData(prop.Name, oSqlType.SqlDbType, oSqlType.Size));
else
records.Add(new SqlMetaData(prop.Name, oSqlType.SqlDbType));
}
SqlDataRecord oSqlDataRecord = new SqlDataRecord(records.ToArray());
foreach (T data in this)
{
for (int i = 0; i < properties.Length; i++)
{
oSqlDataRecord.SetValue(i, properties[i].GetValue(data, null));
}
yield return oSqlDataRecord;
}
}
}
public class AttributiDocumento
{
public int IdProvPart { get; set; }
//uso stringa in quanto con data agigunge 2 ore del fuso orario
public DateTime Data { get; set; }
public int Articolo { get; set; }
public int ExArticolo { get; set; }
public int DocVer { get; set; }
[LenAttribute(255)]
public string Altro { get; set; }
public AttributiDocumento()
{
this.Data = new DateTime(1900, 1, 1);
this.Altro = string.Empty;
}
}
To do exactly what you are asking for you need to cast oAttributiDocumentoList to the (IEnumerable<SqlDataRecord>) like this:
var x = ((IEnumerable<SqlDataRecord>)oAttributiDocumentoList).GetEnumerator();
But there are some disadvantages working with Enumerators.
Usually you need to access elements of your enumerable. To do so just use foreach loop.
foreach(var record in ((IEnumerable<SqlDataRecord>)oAttributiDocumentoList)) {
//Do whatever you want with record
}
If you still prefer to stick with .GetEnumerator(); do not forget to dispose it at the end or embrace in using.

How to deserialize a datatable to list<T>

Im looking for a library(nuget preferably) that converts a datatable with column names matching a generic object, into a list of that object. For example if i have a class:
public class foo{
public string bar{get;set;}
public int count{get;set;}
}
And a datatable
+---------+-------+
| bar | count |
+---------+-------+
| string1 | 1 |
| string2 | 5 |
+---------+-------+
Be able to call something like
List<foo> foos =DTSerializer.Deserialize<foo>(dt).ToList();
Think what you need is a little reflection magic rather than serialization:
class Program
{
static void Main(string[] args)
{
DataTable table = new DataTable();
table.Columns.Add("foo",typeof(string));
table.Columns.Add("bar",typeof(int));
table.Rows.Add("row1", 1);
table.Rows.Add("row2", 2);
var result = table.MapTableToList<foobar>();
foreach (foobar item in result)
{
Console.WriteLine("{0}:{1}", item.foo, item.bar);
}
}
}
class foobar
{
public string foo { get; set; }
public int bar { get; set; }
}
public static class ExtensionMethods
{
public static List<T> MapTableToList<T>(this DataTable table) where T : new ()
{
List<T> result = new List<T>();
var Type = typeof(T);
foreach (DataRow row in table.Rows)
{
T item = new T();
foreach (var property in Type.GetProperties())
{
property.SetMethod.Invoke(item, new object[] { row[table.Columns[property.Name]] });
}
result.Add(item);
}
return result;
}
}
This will only work if your Column Names match up with the properties though.
Enhance this extension code for nullable and for
public static List<T> MapTableToList<T>(this DataTable table) where T : new()
{
List<T> result = new List<T>();
var Type = typeof(T);
foreach (DataRow row in table.Rows)
{
T item = new T();
foreach (var property in Type.GetProperties())
{
if(table.Columns.IndexOf(property.Name) > -1)
{
if (row[table.Columns[property.Name]] == System.DBNull.Value && property.GetMethod.ReturnType.Name.IndexOf("Nullable") > -1)
{
property.SetMethod.Invoke(item, new object[] { null });
}
else
{
property.SetMethod.Invoke(item, new object[] { row[table.Columns[property.Name]] });
}
}
}
result.Add(item);
}
return result;
}

c# Write null field to a datatable

`I'm having a problem writing a null result to a datatable.
My linq query is returning values with which I'm populating a new instance of a class.
My datatable is being created generically and being populated with a generically created datarow.
What happens is my datatable is being created succesfully, the query runs, but when I hit the VAR statement it fails because one of the decimal fields is null. I cannot change this in the class because then I cannot create the datatable.
I need to change this one line I think to make it accept a null value:
moneyvalue = result.moneyvalue,
This is my table definition:
[Table(Name = "t_sdi_traded_product")]
public class t_sdi_traded_product
{
[Column]
public string deal_position_id;
[Column]
public decimal moneyvalue;
[Column]
public string cost_centre;
}
This is my class
public class traded_product
{
public string Deal { get; set; }
public decimal moneyvalue { get; set; }
public string InvolvedPartyId { get; set; }
}
This is my query
var query =
from result in t_sdi_traded_product_hsbc.AsQueryable()
where result.sdi_control_id == current_control_id
select new traded_product()
{
Deal = result.deal_position_id,
moneyvalue = result.moneyvalue,
InvolvedPartyId = result.involved_party_id
}
Here is how I create my datatable and datarow
public static DataTable CreateDataTable(Type animaltype)
{
DataTable return_Datatable = new DataTable();
foreach (PropertyInfo info in animaltype.GetProperties())
{
return_Datatable.Columns.Add(new DataColumn(info.Name, info.PropertyType));
}
return return_Datatable;
}
public static DataRow makeRow(object input, DataTable table)
{
Type inputtype = input.GetType();
DataRow row = table.NewRow();
foreach (PropertyInfo info in inputtype.GetProperties())
{
row[info.Name] = info.GetValue(input, null);
}
return row;
}
Now as soon as it hits this part of the code after the "var query" I get the problem:
foreach (var results in query)
{
foreach (PropertyInfo result in results.GetType().GetProperties())
{
string name = result.Name;
foreach (PropertyInfo info in used.GetType().GetProperties())
{
if (result.Name == info.Name)
{
try
{
info.SetValue(used, result.GetValue(results, null), null);
}
catch (NoNullAllowedException e)
{
}
finally
{
info.SetValue(used, DBNull.Value, null);
}
//Console.WriteLine("Result {0} matches class {1} and the value is {2}", result.Name, info.Name, result.GetValue(results,null));
}
}
}
tp_table.Rows.Add(used, tp_table);
}
It fails as soon as it hits the foreach, because the value returned from the database for moneyvalue is null.
I cannot change the class piece to decimal? otherwise the CreateDatable method fails because it says DataTable cannot have a nullable value.
I think your issue is in
select new traded_product()
{
Deal = result.deal_position_id,
moneyvalue = result.moneyvalue, <-- here you need some handling for DBNULL.Value
InvolvedPartyId = result.involved_party_id
}
select new traded_product()
{
Deal = result.deal_position_id,
moneyvalue = result.moneyvalue == DBNull.Value ? 0m : result.moneyvalue,
InvolvedPartyId = result.involved_party_id
}
* Update *
Why not construct your datatable using traded_product and as #user65439 mentioned change your DB class (t_sdi_traded_product) to have a nullable column
[Column]
public decimal? moneyvalue;
Then you just have to handle nulls being returned and converting them to 0 for your not-nullable decimal in your traded_product class
If it is allowed to write NULL values to the database you should make your variable types nullable, for example
[Column]
public decimal? moneyvalue;
instead of
[Column]
public decimal moneyvalue;

How to convert a List into DataTable

I'm getting values from another data table as input to list. Now i need to save those list values into another DataTable.
List:
List<DataRow> list = slectedFieldsTable.AsEnumerable().ToList();
foreach (DataRow dr in slectedFieldsTable.Rows)
{
list.Add(dr);
}
New Data table :
DataRow newRow = tempTable.NewRow();
newRow["Field Name"] = fieldLabel;
newRow["Field Type"] = fieldType;
for(int gg =0 ; gg<list.Count; gg++)
{
tempTable.Rows.Add(????);
}
I'm stuck here in adding up rows in to new data table.
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;
}
Variable declare:
DataTable tempTable = new DataTable();
DataTable slectedFieldsTable = new DataTable();
DataRow newRow;
List<object> list = new List<object>();
Add Column in DataTable:
slectedFieldsTable = new DataTable();
slectedFieldsTable.Columns.Add("Field Name");
slectedFieldsTable.Columns.Add("Field Type");
Add Value in DataTable:
slectedFieldsTable.Rows.Add("1", "AAA");
slectedFieldsTable.Rows.Add("2", "BBB");
slectedFieldsTable.Rows.Add("3", "CCC");
Convert DataTable to List:
foreach (DataRow dr in slectedFieldsTable.Rows)
{
list.Add(dr);
}
Add Column in another DataTable:
tempTable.Columns.Add("Field Name", typeof(string));
tempTable.Columns.Add("Field Type", typeof(string));
Convert List to dataTable:
foreach(DataRow dr in list)
{
newRow = tempTable.NewRow();
newRow["Field Name"] = dr.ItemArray[0].ToString();
newRow["Field Type"] = dr.ItemArray[1].ToString();
tempTable.Rows.Add(newRow);
tempTable.AcceptChanges();
}
use CopyToDataTable() method. CopyToDataTable
IEnumerable<DataRow> query = TempselectedFieldsTable.AsEnumerable().ToList();
// Create a table from the query.
DataTable boundTable = query.CopyToDataTable<DataRow>();
The answer providing the ToDataTable is a very nice start but it is missing some key elements. Namely, it ignores that List item properties may:
...be marked ReadOnly
...use the DisplayName attribute
...have a DefaultValue which the DataColumn should know about
...be Nullable
...be marked BrowsableAttribute(false)
The following is an extension method to return a DataTable and either accounts for the above or provides the means for your code to apply them. It also uses an Interface to get the values from the class object rather than Reflection.
public static DataTable ToDataTable<T>(this IList<T> lst, bool includeAll = true)
{
DataTable dt = new DataTable();
DataColumn dc;
PropertyDescriptor pd;
bool Browsable;
PropertyDescriptorCollection propCol = TypeDescriptor.GetProperties(typeof(T));
for (int n = 0; n < propCol.Count; n++)
{
pd = propCol[n];
Type propT = pd.PropertyType;
dc = new DataColumn(pd.Name);
// if Nullable, get underlying type
// the first test may not be needed
if (propT.IsGenericType && Nullable.GetUnderlyingType(propT) != null )
{
propT = Nullable.GetUnderlyingType(propT);
dc.DataType = propT;
dc.AllowDBNull = true;
}
else
{
dc.DataType = propT;
dc.AllowDBNull = false;
}
// is it readonly?
if (pd.Attributes[typeof(ReadOnlyAttribute)] != null)
{
dc.ReadOnly = ((ReadOnlyAttribute)pd.
Attributes[typeof(ReadOnlyAttribute)]).IsReadOnly;
}
// DefaultValue ...
if (pd.Attributes[typeof(DefaultValueAttribute)] != null)
{
dc.DefaultValue = ((DefaultValueAttribute)pd.
Attributes[typeof(DefaultValueAttribute)]).Value;
}
// caption / display name
dc.ExtendedProperties.Add("DisplayName", dc.Caption);
if (pd.Attributes[typeof(DisplayNameAttribute)] != null)
{
// these are usually present but blank
string theName = ((DisplayNameAttribute)pd.
Attributes[typeof(DisplayNameAttribute)]).DisplayName;
dc.Caption = string.IsNullOrEmpty(theName) ? dc.Caption : theName;
// DGV doesnt use Caption...save for later
dc.ExtendedProperties["DisplayName"] = dc.Caption;
}
Browsable = true;
dc.ExtendedProperties.Add("Browsable", Browsable);
var foo = pd.Attributes[typeof(BrowsableAttribute)];
if (pd.Attributes[typeof(BrowsableAttribute)] != null)
{
Browsable = ((BrowsableAttribute)pd.Attributes[typeof(BrowsableAttribute)]).Browsable;
// no such thing as a NonBrowsable DataColumn
dc.ExtendedProperties["Browsable"] = Browsable;
}
// ToDo: add support for custom attributes
if (includeAll || Browsable)
{
dt.Columns.Add(dc);
}
}
// the lst could be empty such as creating a typed table
if (lst.Count == 0) return dt;
if (lst[0] is IDataValuesProvider)
{
IDataValuesProvider dvp;
// copy the data - let the class do the work
foreach (T item in lst)
{
dvp = (IDataValuesProvider)item;
dt.Rows.Add(dvp.GetDataValues(includeAll).ToArray());
}
}
else
{
List<object> values;
foreach (T item in lst)
{
values = new List<object>();
// only Browsable columns added
for (int n = 0; n < dt.Columns.Count; n++)
{
values.Add(propCol[dt.Columns[n].ColumnName].GetValue(item));
}
dt.Rows.Add(values.ToArray());
}
}
return dt;
}
The method allows you to specify whether columns for non Browsable properties should be added to the DataTable. Rather than hiding the columns later, you can omit them entirely if you want.
An interface proves the means to get the data values from collection members in order (as an alternative to a reflection loop):
public interface IDataValuesProvider
{
IEnumerable<object> GetDataValues(bool includeAll);
}
... on the class:
public class StockItem : IDataValuesProvider
{
public int Id { get; set; }
public string ItemName {get; set;}
[Browsable(false), DisplayName("Ignore")]
public string propA {get; set;}
[ReadOnly(true)]
public string Zone { get; set; }
public string Size {get; set;}
[DisplayName("Nullable")]
public int? Foo { get; set; }
public int OnHand {get; set;}
public string ProdCode {get; set;}
[Browsable(false)]
public string propB { get; set; }
public DateTime ItemDate {get; set;}
// IDataValuesProvider implementation
public IEnumerable<object> GetDataValues(bool IncludeAll)
{
List<object> values = new List<object>();
values.AddRange(new object[] {Id, ItemName });
if (IncludeAll) values.Add(propA);
values.AddRange(new object[] { Zone, Size, Foo, OnHand, ProdCode });
if (IncludeAll) values.Add(propB);
values.Add(ItemDate);
return values;
}
}
Add the data values in the same order as they are listed in your class; be sure to update it when you add properties. The reflection version is still there so you can do it either way.
Finally, there are a few common Attributes which do not have a related DataColumn property. The method stores these for you as ExtendedProperties allowing you to easily apply them to the DGV:
var dtX = someData.ToDataTable();
dgvB.SuspendLayout();
dgvB.DataSource = dtX;
// process extended props
foreach (DataColumn dc in dtX.Columns)
{
// no need to test, the code adds them everytime
//if (dc.ExtendedProperties.ContainsKey("DisplayName"))
//{
dgvB.Columns[dc.ColumnName].HeaderText = dc.ExtendedProperties["DisplayName"].ToString();
//}
//if (dc.ExtendedProperties.ContainsKey("Browsable"))
//{
dgvB.Columns[dc.ColumnName].Visible = (bool)dc.ExtendedProperties["Browsable"];
//}
}
dgvB.ResumeLayout();
Results using a list of the class shown above:
Both OnHand and Foo show the DisplayName and both PropA and PropB are hidden. Most importantly, columns created for ReadOnly and Nullable properties act accordingly.
Try this:
foreach (DataRow dr in list)
{
tempTable.Rows.Add(dr);
}

Categories