c# Write null field to a datatable - c#

`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;

Related

Generic Method to convert datatable to list

I'm using below code:
public static List<T> ConvertToList1<T>(DataTable dt)
{
var columnNames = dt.Columns.Cast<DataColumn>()
.Select(c => c.ColumnName)
.ToList();
var properties = typeof(T).GetProperties();
return dt.AsEnumerable().Select(row =>
{
T objT1 = Activator.CreateInstance<T>();
foreach (var pro in properties)
{
if (columnNames.Contains(pro.Name))
{
PropertyInfo? pI = objT.GetType().GetProperty(pro.Name);
pro.SetValue(objT, row[pro.Name] == DBNull.Value ? null : Convert.ChangeType(row[pro.Name], pI.PropertyType));
}
}
return objT1;
}).ToList();
}
But I'm getting error for decimal field having null values.
Invalid cast from 'System.Decimal' to 'System.Nullable`1[[System.Decimal, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral
public class SampleModel
{
public Guid Id { get; set; }
public Guid ProjectId { get; set; }
public string? Code { get; set; } = null!;
public string? Description { get; set; }
public decimal? Quantity1 { get; set; }
public decimal? Quantity2 { get; set; }
}
Can anyone suggest how to fix this?
Um, reading again... You mean that a non-null decimal value in the row cannot be assigned to a nullable decimal? Or did you mean a dbnull that cannot be assigned to the nullable decimal?
You can use the DataRow.IsNull to check if the value is dbnull. In that case you just skip it as the nullable already has default null.
// <CODE SNIP />
if (columnNames.Contains(pro.Name))
{
// if the value was null, just skip it.
if(!row.IsNull(pro.Name))
{
PropertyInfo? pI = objT.GetType().GetProperty(pro.Name);
pro.SetValue(objT, Convert.ChangeType(row[pro.Name], pI.PropertyType));
}
}
I've had a play.
The link from #Ryan Wilson was very useful.
In short, where you have a nullable type, you want to cast to the underlying type. Consider:
decimal? target;
decimal source = 42;
target = source;
This is perfectly reasonable code. We can assign a decimal value to a decimal? variable.
Using Nullable.GetUnderlyingType(myType) returns a type or null if myType is not nullable. Extending the above snippet:
var underlying1 = Nullable.GetUnderlyingType(target.GetType());
var underlying2 = Nullable.GetUnderlyingType(source.GetType());
Here, underlying2 is null, and underlying1 is decimal.
To put this into practice, then, we slightly alter the innermost part of your conversion method to become:
PropertyInfo pI = objT1.GetType().GetProperty(pro.Name);
var targetType = Nullable.GetUnderlyingType(pI.PropertyType) ?? pI.PropertyType;
pro.SetValue(objT1, row[pro.Name] == DBNull.Value
? null
: Convert.ChangeType(row[pro.Name], targetType));
Try following :
public static List<SampleModel> ConvertToList1<T>(DataTable dt)
{
List<SampleModel> results = dt.AsEnumerable().Select(x => new SampleModel()
{
Id = x.Field<Guid>("Id"),
ProjectId = x.Field<Guid>("ProjectId"),
Code = x.Field<string>("Code"),
Description = x.Field<string>("Description"),
Quantity1 = x.Field<decimal>("Quantity1"),
Quantity2 = x.Field<decimal>("Quantity1")
}).ToList();
return results;
}

C# complex class to SQLBulkInsert millions or rows

I'm having an issue, apparently is very simple but I haven't found anything related.
public class Fruit
{
public string name { get; set; }
public string color { get; set; }
public string shape { get; set; }
public Image image { get; set; }
}
public class Image
{
public string Id { get; set; }
public string URL { get; set; }
}
That's my "complex" class, and I want to convert this to DataTable to apply the SQLBulkCopy but when it retrieves the Image class it's not bringing the values but the data type "Image" which throws an exception.
public static DataTable ToDataTable<T>(this IList<T> data)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
DataTable table = new DataTable();
foreach (PropertyDescriptor prop in properties)
table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
foreach (T item in data)
{
DataRow row = table.NewRow();
foreach (PropertyDescriptor prop in properties)
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
table.Rows.Add(row);
}
return table;
}
Any clue on how can I retrieve the ID for the Image property to do the SQLBulkCopy?
By the way, this is the method I'm using for the insert:
public static void SaveToDatabase(DataTable data)
{
using (var connection = new SqlConnection(dbConn))
{
SqlTransaction transaction = null;
connection.Open();
try
{
transaction = connection.BeginTransaction();
using (var sqlBulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, transaction))
{
sqlBulkCopy.DestinationTableName = "Fruit";
sqlBulkCopy.ColumnMappings.Add("name", "name");
sqlBulkCopy.ColumnMappings.Add("color", "color");
sqlBulkCopy.ColumnMappings.Add("shape", "shape");
sqlBulkCopy.ColumnMappings.Add("image", "image");
sqlBulkCopy.WriteToServer(data);
}
transaction.Commit();
}
catch (Exception ex) // Here I got the datatype Exception because I don't know how to get the Image.ID value
{
transaction.Rollback();
}
}
}
Apparently my post it's a little bit confusing, so I'm going to add the issue here:
How to get the URL and ID values from the Image class in the following code? Currently, it's bringing the object Image which it's unknown and throws an exception
sqlBulkCopy.ColumnMappings.Add("image", "image");
How can I map them? I know I can do it row by row, but I have more than 2 million rows to process on each execution. That's why I'm trying to use the bulkcopy.
I need to add that this data comes from an API call in Json format
After modifying the code to test this I did find a solution:
if (prop.GetChildProperties().Count > 1)
row[prop.Name] = (Image)prop.GetValue(item);
else
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
The solution was only to cast the GetValue to the Image class, this was applied in the ToDataTable method:
public static DataTable ToDataTable<T>(this IList<T> data)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
DataTable table = new DataTable();
foreach (PropertyDescriptor prop in properties)
table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
foreach (T item in data)
{
DataRow row = table.NewRow();
foreach (PropertyDescriptor prop in properties)
if (prop.GetChildProperties().Count > 1)
row[prop.Name] = (Image)prop.GetValue(item);
else
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
table.Rows.Add(row);
}
return table;
}
Thank you all for your time and help!

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);
}

C# retrieve properties through reflection [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Get property value from string using reflection in C#
I have an application that merges fields from a database into emails and letters. As there are different users they request different fields to be merged, and as I create a new mergefield the documentation also has to be reworked. This gives problems so I want to automate the documentation.
I came up with the following code (in this example I defined only 2 mergefields, called stringField, but currently it's allmost 100):
namespace ReflectionTest
{
public class clReflection
{
private System.Data.DataTable dtClient = new System.Data.DataTable();
public int ClientNumber { get; set; }
public int AdressNumber { get; set; }
public stringField ClientName
{
get
{
stringField _ClientName = new stringField();
_ClientName.FieldContent = "Obama";
_ClientName.FieldNote = "Last name of client";
//Available email and available letter should be default true
return _ClientName;
}
set { }
}
public stringField ClientEmail
{
get
{
stringField _ClientEmail = new stringField();
_ClientEmail.FieldContent = "obama#whitehouse.gov";
_ClientEmail.FieldNote = "use only tested email adresses";
_ClientEmail.AvailableLetter = false;
return _ClientEmail;
}
set
{ }
}
}
public static class FindStringFields
{
public static System.Data.DataTable stringFields()
{
System.Data.DataTable dtStringFields = new System.Data.DataTable();
dtStringFields.Columns.Add(new System.Data.DataColumn("FieldName", typeof(string)));
dtStringFields.Columns.Add(new System.Data.DataColumn("FieldNote", typeof(string)));
dtStringFields.Columns.Add(new System.Data.DataColumn("AvailableEmail", typeof(bool)));
clReflection test = new clReflection();
System.Reflection.PropertyInfo[] props = test.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
for (int i = 0; i < props.Length; i++)
{
if (props[i].PropertyType == typeof(stringField))
{
dtStringFields.Rows.Add(new object[] { props[i].Name , "FieldNote", true });
}
}
return dtStringFields;
}
}
public class stringField
{
private bool _AvailableEmail = true;
private bool _AvailableLetter = true;
public string FieldContent { get; set; }
public string FieldNote { get; set; }
public bool AvailableEmail
{
get { return _AvailableEmail; }
set { AvailableEmail = value; }
}
public bool AvailableLetter
{
get { return _AvailableLetter; }
set { _AvailableLetter = value; }
}
}
}
If you fire the instruction: System.Data.DataTable dtTest = FindStringFields.stringFields();
you get a datatable with all defined stringFields.
However, apart from the name of the stringfield I can't retrieve the properties of a stringfield.
How do I instantiate a found stringField so I can retrieve the other properties?
Thanks,
Rob
Use Type.GetProperties method to get all public properties of type
Type type = typeof(stringField);
PropertyInfo[] properties = type.GetProperties();
Also there is overload where you can specify BindingFlags.
Then you can get values of properties:
object value = property.GetValue(_ClientEmail);
If you have the string field name try with this:
public static object GetPropValue( object src, string propName )
{
return src.GetType( ).GetProperty( propName ).GetValue( src, null );
}
Extracted from this SO post:
Get property value from string using reflection in C#
You can do something like this:
//its better to use a property than get it from the array every time!
PropertyInfo pi = prop[i];
//get the underlying value
stringField underlyingStringField = prop.GetValue(test, null) as stringField;
//use the underlying value now
Debug.Write(underlyingStringField.AvalilableEmail);
...

Invalid Arguments Error Using Generics

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;
}

Categories