C# List to DataTable extension method not retrieving properties - c#

I want to convert a ObservableCollection of type KNMOLijst to a DataTable. I found a extension method for it, but it is not retrieving my properties?
Extension method:
public static class ListToDataTable
{
public static DataTable ToDataTable<T>(this IList<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;
}
}
The peace of code below is not retrieving any properties, I also tried to include the nonpublic members in the bindingflags, but that didn't seem to work for me
//Get all the properties
PropertyInfo[] Props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
This is the type KNMOLijst it receives:
public class KNMOLijst
{
public string VoorLetters { get; set; }
public string Voornaam { get; set; }
public string TussenVoegsel { get; set; }
public string Achternaam { get; set; }
public string Geslacht { get; set; }
public DateTime GeboorteDatum { get; set; }
public string InstrumentNaam { get; set; }
public KNMOLijst()
{
}
}
I made sure that the properties were public.
This is the list that the extension method receives.
generatedList.Add(new KNMOLijst()
{
VoorLetters = (string)(row["VoorLetters"]),
Voornaam = (string)(row["Voornaam"]),
TussenVoegsel = (string)(row["TussenVoegsel"]),
Achternaam = (string)row["Achternaam"],
Geslacht = (string)row["Geslacht"],
GeboorteDatum = (DateTime)row["GeboorteDatum"],
InstrumentNaam = (string)row["InstrumentNaam"]
});
My viewmodel that invokes the ToDataTable method.
public class SecretarisViewModel : BaseViewModel
{
private readonly SecretarisBLL secretarisBll;
private ObservableCollection<object> generatedList;
public ObservableCollection<object> GeneratedList
{
get { return generatedList; }
set
{
generatedList = value;
NotifyPropertyChanged();
}
}
private DataTable generatiedDataTable;
public DataTable GeneratedDataTable
{
get => generatiedDataTable;
set
{
generatiedDataTable = value;
NotifyPropertyChanged();
}
}
public SecretarisViewModel()
{
secretarisBll = new SecretarisBLL();
GeneratedList = new ObservableCollection<object>();
generatiedDataTable = new DataTable();
}
public async Task GetKNMOList()
{
var dataset = await secretarisBll.GetKNMOList();
foreach (DataRow row in dataset.Tables[0].Rows)
{
generatedList.Add(new KNMOLijst()
{
VoorLetters = (string)(row["VoorLetters"]),
Voornaam = (string)(row["Voornaam"]),
TussenVoegsel = (string)(row["TussenVoegsel"]),
Achternaam = (string)row["Achternaam"],
Geslacht = (string)row["Geslacht"],
GeboorteDatum = (DateTime)row["GeboorteDatum"],
InstrumentNaam = (string)row["InstrumentNaam"]
});
}
GeneratedDataTable = generatedList.ToDataTable();
}
}
Why is it not able to get the properties of my list?

The generatedlist is an ObservableCollection<object>,
so I assume that if I would add a new KNMOLijst row into it, it would
be of the type KNMOLijst?
No, if the list is of type ObservableCollection<object> then T is System.Object which has no public properties. So make it a ObservableCollection<KNMOLijst>.
If you can't do that modify the method to derive the type from the first item:
public static DataTable ToDataTable<T>(this IList<T> items)
{
Type type = items.FirstOrDefault()?.GetType();
if (type == null)
throw new InvalidOperationException("The properties are derived from the first item, so the list must not be empty");
DataTable dataTable = new DataTable(type.Name);
//Get all the properties
PropertyInfo[] Props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
// ...
}
By the way, this behavior(throwing InvalidOperationException on empty list) is similar to CopyToDataTable which does the same. It also needs to use reflection to get the columns.

Related

How to apply Linq to Observable Collection of Generic Property?

How to apply the linq concept to Generic property Collection. I have converted the Data Table to Collection as like Converter
I have the DataTable and then convert it to Collection as like
class Program
{
static void Main(string[] args)
{
DataTable dtResult = new DataTable();
dtResult.Columns.Add("ID", typeof(int));
dtResult.Columns.Add("Name", typeof(string));
DataRow dtRow = dtResult.NewRow();
dtRow["ID"] = 1;
dtRow["Name"] = "Bala";
dtResult.Rows.Add(dtRow);
dtRow = dtResult.NewRow();
dtRow["ID"] = 2;
dtRow["Name"] = "Bala1";
dtResult.Rows.Add(dtRow);
var Collection = Convert(dtResult);
// var property = Collection.Where(a=>a.Properties.Where(x => (x as GenericProperty).Name.Equals("ID") && (x as GenericProperty).Value.Equals(1));
// I would like to get the ID matching 2 Record How to get this using linq query
}
private static ObservableCollection<GenericObject> Convert(DataTable toConvert)
{
ObservableCollection<GenericObject> _result = new ObservableCollection<GenericObject>();
foreach (DataRow _row in toConvert.Rows)
{
GenericObject _genericObject = new GenericObject();
foreach (DataColumn _column in toConvert.Columns)
{
_genericObject.Properties.Add(new GenericProperty(_column.ColumnName, _row[_column]));
}
_result.Add(_genericObject);
}
return _result;
}
}
public class GenericObject
{
private readonly ObservableCollection<GenericProperty> properties = new ObservableCollection<GenericProperty>();
public GenericObject(params GenericProperty[] properties)
{
foreach (var property in properties)
Properties.Add(property);
}
public ObservableCollection<GenericProperty> Properties
{
get { return properties; }
}
}
public class GenericProperty : INotifyPropertyChanged
{
public GenericProperty(string name, object value)
{
Name = name;
Value = value;
}
public string Name { get; private set; }
public object Value { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
}
Now my question is to how can i apply the linq concept to get the MAtched Record??
Is this what you want?
var property =
from a in Collection
where a.Properties.Any(p => p.Name == "ID" && (int)p.Value == 1)
select a;
That gives:

Ignore properties of class by comparing with a list in c#

I have a list where TestClass is a class with some predefined properties. So when i get data and bind my list with data i need to ignore some properties of TestClass by comparing it with a list. How can i achieve that?
Below is my code
public class TestClass
{
public int id{get;set;}
public string fname{get;set;}
public string lname{get;set;}
public string job {get;set;}
public string role{get;set;}
public string address{get;set;}
}
List<TestClass> ulist = null;
ulist = ToList<TestClass>(usersdataset.tables[0]); //fill my list with the data code is given below
so after getting the data into the list i need to remove some properties by comparing it with list of properties which should be returned.for example if my filteredlist should only show id,fname,role then i need to remove the extra properties from my ulist. so after the filter ulist should only contain id,fname and role
ToList Method
public static List<T> ToList<T>(DataTable dataTable) where T : new()
{
var dataList = new List<T>();
//Define what attributes to be read from the class
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
//Read Attribute Names and Types
var objFieldNames = typeof(T).GetProperties(flags).Cast<PropertyInfo>().
Select(item => new
{
Name = item.Name,
Type = Nullable.GetUnderlyingType(item.PropertyType) ?? item.PropertyType
}).ToList();
//Read Datatable column names and types
var dtlFieldNames = dataTable.Columns.Cast<DataColumn>().
Select(item => new {
Name = item.ColumnName,
Type = item.DataType
}).ToList();
foreach (DataRow dataRow in dataTable.AsEnumerable().ToList())
{
var classObj = new T();
foreach (var dtField in dtlFieldNames)
{
PropertyInfo propertyInfos = classObj.GetType().GetProperty(dtField.Name);
var field = objFieldNames.Find(x => x.Name == dtField.Name);
//var field = filteredColumns.Find(x => x.PropertyName == dtField.Name);
if (field != null)
{
if (dataRow[dtField.Name] != DBNull.Value)
propertyInfos.SetValue(classObj, dataRow[dtField.Name], null);
}
}
dataList.Add(classObj);
}
return dataList;
}
Use the overvride function Equals:
This sample compare only the id property
public class TestClass
{
public int id { get; set; }
public string fname { get; set; }
public string lname { get; set; }
public string job { get; set; }
public string role { get; set; }
public string address { get; set; }
public override bool Equals(object obj)
{
if (obj.GetType().Name != this.GetType().Name)
{
return false;
}
TestClass testclassObject = (TestClass)obj;
if (testclassObject.id != this.id)
{
return false;
}
return true;
}

c# Reflection - Accessing object properties on static class

I used code from this post to implement feature toggles in an application and it is working great. However, now instead of my features being a straight bool on/off, each feature has two bool values: one for the general setting and one for the setting which is only applied to a subset of users. I've been trying to update the code to handle this and I'm finding my basic understanding of reflection and accessors is coming up short.
I'm using the code below to test in LINQPad - basically, compared to the post mentioned above, all I've done is tried to change TestFeatureToggle from a bool, to type MultiToggle but I'm struggling to Set or Get the value of the bool properties of the MultiToggle object. I get an exception at
return (bool)multiToggleProperty.GetValue(null,null);
"Non-static method requires a target". Which makes sense because the properties on MultiToggle are non-static. However, I don't have an instance of those objects and I don't understand how I would get one from a static class.
Any help would be really appreciated. My gut says it might not be possible!
void Main()
{
var exampleFeatureToggles = new List<FeatureToggle>
{
new FeatureToggle {Description = "Test", Id = 1, IsActive = true, Name = "TestFeatureToggle"}
};
FeatureToggles.SetFeatureToggles(exampleFeatureToggles);
Console.WriteLine(FeatureToggles.GetFeatureToggleSetting("TestFeatureToggle"));
}
public class FeatureToggle
{
public string Description { get; set; }
public int Id { get; set; }
public bool IsActive { get; set; }
public string Name { get; set; }
}
public class MultiToggle
{
public bool DefaultValue { get; private set; }
public bool OtherValue { get; private set; }
}
public static class FeatureToggles
{
//public static bool TestFeatureToggle { get; private set; }
public static MultiToggle TestFeatureToggle { get; private set; }
public static void SetFeatureToggles(List<FeatureToggle> toggles)
{
if (toggles == null) return;
var properties = typeof(FeatureToggles).GetProperties(BindingFlags.Public | BindingFlags.Static);
// All properties must be set to false to prevent a property staying true when deleted from the database
foreach (var property in properties)
{
var multiToggleProperties = typeof(MultiToggle).GetProperties();
foreach (var multiToggleProperty in multiToggleProperties)
{
multiToggleProperty.SetValue(new MultiToggle(), false, null);
}
}
foreach (var toggle in toggles)
{
foreach (var property in properties)
{
if (property.Name.ToLower() == toggle.Name.ToLower())
{
Type tProp = property.GetType();
var multiToggleProperties = typeof(MultiToggle).GetProperties();
foreach (var multiToggleProperty in multiToggleProperties)
{
Console.WriteLine(multiToggleProperty);
multiToggleProperty.SetValue(new MultiToggle(), toggle.IsActive, null);
}
}
}
}
}
public static bool GetFeatureToggleSetting(string propertyName)
{
var properties = typeof(FeatureToggles).GetProperties(BindingFlags.Public | BindingFlags.Static);
foreach (var property in properties)
{
if (property.Name.ToLower() == propertyName.ToLower())
{
Type tProp = property.GetType();
var multiToggleProperties = typeof(MultiToggle).GetProperties();
Console.WriteLine(multiToggleProperties);
foreach (var multiToggleProperty in multiToggleProperties)
{
Console.WriteLine(multiToggleProperty);
return (bool)multiToggleProperty.GetValue(null, null);
}
}
}
return false;
}
}
The idea was there but you've essentially "just overshot the mark". You are getting the "Non-static method requires a target" error because you are trying to get the value of a property in the value of a static property, without getting the value of the static property first (what a mouthful). i.e. you are going:
get static property type -> get instance property type -> get value of property from static property.
DefaultValue, and OtherValue are instance properties so you will need the instance object first before you can get their value. I made a few tweaks just to show you how to both set the static property and get the values from the static property. You should be able to tweak it from there:
void Main()
{
var exampleFeatureToggles = new List<FeatureToggle>
{
new FeatureToggle {Description = "Test", Id = 1, IsActive = true, Name = "TestFeatureToggle"}
};
FeatureToggles.SetFeatureToggles(exampleFeatureToggles);
Console.WriteLine(FeatureToggles.GetFeatureToggleSetting("TestFeatureToggle"));
}
public class FeatureToggle
{
public string Description { get; set; }
public int Id { get; set; }
public bool IsActive { get; set; }
public string Name { get; set; }
}
public class MultiToggle
{
public bool DefaultValue { get; private set; }
public bool OtherValue { get; private set; }
}
public static class FeatureToggles
{
//public static bool TestFeatureToggle { get; private set; }
public static MultiToggle TestFeatureToggle { get; private set; }
public static void SetFeatureToggles(List<FeatureToggle> toggles)
{
if (toggles == null) return;
var properties = typeof(FeatureToggles).GetProperties(BindingFlags.Public | BindingFlags.Static);
// All properties must be set to false to prevent a property staying true when deleted from the database
foreach (var property in properties)
{
// first change: set the _property_, not multiToggleProperty
property.SetValue(null, new MultiToggle());
}
foreach (var toggle in toggles)
{
foreach (var property in properties)
{
if (property.Name.ToLower() == toggle.Name.ToLower())
{
Type tProp = property.GetType();
var multiToggleProperties = typeof(MultiToggle).GetProperties();
// second change: create a nee instance, set the values, then set that value on the static property
var newMultiToggle = new MultiToggle();
property.SetValue(null, newMultiToggle);
foreach (var multiToggleProperty in multiToggleProperties)
{
Console.WriteLine(multiToggleProperty);
multiToggleProperty.SetValue(newMultiToggle, toggle.IsActive, null);
}
}
}
}
}
public static bool GetFeatureToggleSetting(string propertyName)
{
var properties = typeof(FeatureToggles).GetProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
foreach (var property in properties)
{
if (property.Name.ToLower() == propertyName.ToLower())
{
Type tProp = property.GetType();
// third change: get the static value first, then get the value from the properties on that instance.
var value = property.GetValue(null);
var multiToggleProperties = typeof(MultiToggle).GetProperties();
Console.WriteLine(multiToggleProperties);
foreach (var multiToggleProperty in multiToggleProperties)
{
Console.WriteLine(multiToggleProperty);
return (bool)multiToggleProperty.GetValue(value, null); //
}
}
}
return false;
}
}

Get Properties of container class using .net reflection

I have a class contains set of properties, one of the properties is a class type
as below :
public class ProgramData
{
public string code { get; set; }
public string message { get; set; }
public string program_id { get; set; }
public string email { get; set; }
public string first_name { get; set; }
public string last_name { get; set; }
public GeneralSetup general_setup { get; set; }
}
public class GeneralSetup
{
public string store_name { get; set; }
public bool store_enabled { get; set; }
public bool promotions_enabled { get; set; }
public bool barcode_scan_enabled { get; set; }
public bool barcode_generate_enabled { get; set; }
}
i have a generic method [because i have set of classes] to validate the properties and i use reflection to get props name and value dynamically and its working fine, but the problem is when it validates general_setup property it gets its props and start validating them.
based on my business rules if it string.empty i want to set [code and message] props of the container class and i can not get this props at this level.
any ideas? thanks
public T ValidateObjectFields<T>(T entity) where T : class
{
Type objType = entity.GetType();
PropertyInfo[] properties = objType.GetProperties();
foreach (PropertyInfo property in properties)
{
object propValue = property.GetValue(entity, null);
var elems = propValue as IList;
if (elems != null)
{
foreach (var item in elems)
ValidateObjectFields(item);
}
else
{
// Check if current property has sub object
if (property.PropertyType.Assembly == objType.Assembly)
{
#region Validate Objects
var code = objType.GetProperty("code");
var mesg = objType.GetProperty("message");
// in this case the property has sub object and i want to get these properties of container class
if (code == null && mesg == null)
{
code = objType.GetProperty("code", BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public);
mesg = objType.GetProperty("message", BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public);
}
if (String.IsNullOrEmpty(Convert.ToString(propValue)))
{
//strDict = systemResponse.GetSystemResponse(Constants.structSystemResponses.Required_Field, //Constants.ConfigurableLanguages.English, Constants.enResponseSourceSystems.Webservice);
foreach (DictionaryEntry value in strDict)
{
code.SetValue(entity, Convert.ToString(value.Key), null);
mesg.SetValue(entity, Convert.ToString(value.Value) + " " + property.Name, null);
}
break;
}
#endregion
ValidateObjectFields(propValue);
}
else
{
#region Validate Objects
var code = objType.GetProperty("code");
var mesg = objType.GetProperty("message");
// in this case the property has sub object and i want to get these properties of container class
if(code == null && mesg == null)
{
PropertyInfo[] info = objType.BaseType.GetProperties(BindingFlags.Public);
code = objType.GetProperty("code", BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public);
mesg = objType.GetProperty("message", BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public);
}
if (String.IsNullOrEmpty(Convert.ToString(propValue)))
{
strDict = systemResponse.GetSystemResponse(Constants.structSystemResponses.Required_Field, Constants.ConfigurableLanguages.English, Constants.enResponseSourceSystems.Webservice);
foreach (DictionaryEntry value in strDict)
{
code.SetValue(entity, Convert.ToString(value.Key), null);
mesg.SetValue(entity, Convert.ToString(value.Value) + " " + property.Name, null);
}
break;
}
#endregion
}
}
}
return entity;
}
You can write an overload for the ValidateObjectFields which takes a parent element and this way you can access the properties of the containing class.
public TEntity ValidateObjectFields<TEntity>(TEntity entity, object Entity)
{
//Here put the code for handling the properties.
}
And in your code above call this method
foreach (var item in elems)
ValidateObjectFields(item,entity);
This will fix your problem I think.

Convert DataTable to List of strongly typed objects

I am trying to write a generic method that will convert a DataTable to a list of strongly typed objects.
The code that I'm working with so far is...
public List<T> ImportTable<T>(String fileName, String table)
{
//Establish Connection to Access Database File
var mdbData = new ConnectToAccess(#"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=F:\ACCESS\" + fileName + ".mdb;");
var tableData = new List<T>();
foreach (DataRow row in mdbData.GetData("SELECT * FROM " + table).Rows)
{
tableData.Add(ConvertRowToType<T>(row));
}
return tableData;
}
public T ConvertRowToType<T>(DataRow row)
{
//??? What is the best thing to do here ???
}
I'm not fixated on this code if anybody's suggestions would require changes to it.
So let's say I call this function passing in the type...
public class mdbConcern
{
public Int32 ConcernId { get; set; }
public String Concern { get; set; }
}
And the Data coming back in the DataTable looks like...
ConcernID Concern
1 Law and Ethics
2 Mail
3 Business English
... ...
What would be the best way to implement the ConvertRowToType(DataRow row) method?
Can someone show me how to use Func as one of the parameters so I can pass in some mapping information?
I think an extension method is the best way to go:
public static class Helper
{
public static T ToType<T>(this DataRow row) where T : new()
{
T obj = new T();
var props = TypeDescriptor.GetProperties(obj);
foreach (PropertyDescriptor prop in props)
{
if(row.Table.Columns.IndexOf(prop.Name) >= 0
&& row[prop.Name].GetType() == prop.PropertyType)
{
prop.SetValue(obj, row[prop.Name]);
}
}
return obj;
}
}
Usage:
public List<T> ImportTable<T>(String fileName, String table)
{
//Establish Connection to Access Database File
var mdbData = new ConnectToAccess(#"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=F:\ACCESS\" + fileName + ".mdb;");
var tableData = new List<T>();
foreach (DataRow row in mdbData.GetData("SELECT * FROM " + table).Rows)
{
tableData.Add(row.ToType<T>());
}
return tableData;
}
Update I see that you asked for a Func that would provide the mapping. I'm not sure exactly what you envisioned but here is a method I came up with:
public class mdbConcern
{
public Int32 ConcernId { get; set; }
public String Concern { get; set; }
public static PropertyDescriptor Mapping(string name)
{
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(mdbConcern));
switch (name)
{
case "Concern_Id":
return props.GetByName("ConcernId");
case "Concern":
return props.GetByName("Concern");
default:
return null;
}
}
}
public static class Helper
{
public static T ToType<T>(this DataRow row, Func<string, PropertyDescriptor> mapping)
where T : new()
{
T obj = new T();
foreach (DataColumn col in row.Table.Columns)
{
var prop = mapping(col.ColumnName);
if(prop != null)
prop.SetValue(obj, row[col]);
}
return obj;
}
}
Usage:
foreach (DataRow row in mdbData.GetData("SELECT * FROM " + table).Rows)
{
tableData.Add(row.ToType<mdbConcern>(mdbConcern.Mapping));
}
Here's a version using attributes on the type's properties to store its mapping. I think it's a more natural solution:
[AttributeUsage(AttributeTargets.Property)]
public class ColumnMappingAttribute : Attribute
{
public string Name { get; set; }
public ColumnMappingAttribute(string name)
{
Name = name;
}
}
public class mdbConcern
{
ColumnMapping("Concern_Id")]
public Int32 ConcernId { get; set; }
ColumnMapping("Concern")]
public String Concern { get; set; }
}
public static class Helper
{
public static T ToType<T>(this DataRow row) where T : new()
{
T obj = new T();
var props = TypeDescriptor.GetProperties(obj);
foreach (PropertyDescriptor prop in props)
{
var columnMapping = prop.Attributes.OfType<ColumnMappingAttribute>().FirstOrDefault();
if(columnMapping != null)
{
if(row.Table.Columns.IndexOf(columnMapping.Name) >= 0
&& row[columnMapping.Name].GetType() == prop.PropertyType)
{
prop.SetValue(obj, row[columnMapping.Name]);
}
}
}
return obj;
}
}
Addition to #Sorax answer. I enhanced ToType method to support Nullable<> type members (using fields instead of properties and TypeInfo instead of TypeDescriptor). It takes whole DataTable object as a paramater and returns IList.
protected IList<TResult> TableToList<TResult>(DataTable table) where TResult : new()
{
var result = new List<TResult>(table.Rows.Count);
var fields = typeof(TResult).GetTypeInfo().DeclaredFields;
TResult obj;
Object colVal;
var columns = table.Columns;
var nullableTypeDefinition = typeof(Nullable<>);
var dbNullType = typeof(DBNull);
Type[] genericArguments;
foreach (DataRow row in table.Rows)
{
obj = new TResult();
foreach (var f in fields)
{
if (columns.Contains(f.Name))
{
colVal = row[f.Name];
if (colVal.GetType() == f.FieldType)
{
f.SetValue(obj, colVal);
}
else if (colVal.GetType() != dbNullType && f.FieldType.IsGenericType &&
f.FieldType.GetGenericTypeDefinition() == nullableTypeDefinition)
{
genericArguments = f.FieldType.GetGenericArguments();
if (genericArguments.Length > 0 && genericArguments[0] == colVal.GetType())
{
f.SetValue(obj, colVal);
}
}
}
}
result.Add(obj);
}
return result;
}

Categories

Resources