How to apply Linq to Observable Collection of Generic Property? - c#

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:

Related

Generic class for convert list of object to IEnumerable<SqlDataRecord>

I've this object:
public class ConsentData
{
public string ConsentName { get; set; }
public bool ConsentValue { get; set; }
public DateTime ConsentDate { get; set; }
}
I should pass a List of ConsentData to a stored procedure in SQL Server via a table-value parameter.
Looking for a way to convert the List of ConsentData in a List of SqlDataRecord i found this generic class on web:
public class SqlTableValueSupport<T> : List<T>, IEnumerable<SqlDataRecord> where T : class, new()
{
IEnumerator<SqlDataRecord> IEnumerable<SqlDataRecord>.GetEnumerator()
{
//Create Columns (SqlMetaData)
List<SqlMetaData> records = new List<SqlMetaData>();
var properties = typeof(T).GetProperties();
foreach (var prop in properties)
{
SqlDbType sdbtyp = GetSqlType(prop.PropertyType);
records.Add(new SqlMetaData(prop.Name, sdbtyp));
}
//Create records/rows (SqlDataRecord)
SqlDataRecord ret = new SqlDataRecord(records.ToArray());
foreach (T data in this)
{
for (int i = 0; i < properties.Length; i++)
{
ret.SetValue(i, properties[i].GetValue(data, null));
}
yield return ret;
}
}
// Map C# Types to SqlDbType
private SqlDbType GetSqlType(Type type)
{
SqlDbType val = SqlDbType.VarChar;
if (type == typeof(Int64) || type == typeof(Nullable<Int64>))
{
val = SqlDbType.BigInt;
}
else if (type == typeof(Byte[]))
{
val = SqlDbType.Binary;
}
else if (type == typeof(Boolean) || type == typeof(Nullable<Boolean>))
{
val = SqlDbType.Bit;
}
else if (type == typeof(DateTime) || type == typeof(Nullable<DateTime>))
{
val = SqlDbType.DateTime;
}
else if (type == typeof(Decimal))
{
val = SqlDbType.Decimal;
}
// Please refer to the following document to add other types
// http://msdn.microsoft.com/en-us/library/ms131092.aspx
return val;
}
}
I'd like to know how to use the class, how can i pass the List of ConsentData and retrive a List of SqlDataRecord?
In the end, I solved the problem by handling the class with a static method.
This is the class:
public class SqlTableValueSupport<T> where T : class
{
public static DataTable ConvertData(List<T> ValuesList)
{
DataTable dtData = new DataTable();
var objectReference = ValuesList.GetType().GetGenericArguments()[0];
var properties = objectReference.GetProperties();
foreach (var prop in properties)
{
dtData.Columns.Add(prop.Name, prop.PropertyType);
}
foreach (var item in ValuesList)
{
var dataArray = new List<object>();
foreach (var prop in properties)
{
dataArray.Add(prop.GetValue(item));
}
dtData.Rows.Add(dataArray.ToArray());
}
return dtData;
}
}
The method is called in the following way
var ListOfMyObjectDT = SqlTableValueSupport<MyObject>.ConvertData(ListOfMyObject);

C# List to DataTable extension method not retrieving properties

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.

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

Creating Business Class for a DataRow with unknown number of columns

I have a stored procedure which returns pivoted columns along with columns from tables. I get the DataTable after executing the stored procedure. Now i want to convert this DataTable to a List<'MyClass'>. The DataTable has some known columns ( coming from tables ) and some unknown number of columns as a result of pivot.
How do i create a Class which really represents one DataRow. The idea i have is following:
public class TableColumns
{
public int TableColumn1 { get;set; }
public string TableColumn2 { get;set; }
public float TableColumn1 { get;set; }
//additional columns if any
}
public class PivotColumns
{
public string ColumnName { get;set; }
public string Value { get;set; }
//additional columns if any
}
public class MyClass
{
public TableColumns tableColumns { get;set; }
public List<PivotColumns> pivotedColumns { get;set; }
//overload the [] operator with real implementation
public string this[string pivotedColumnName] { get;set; }
}
and then a helper class to do the conversion:
public static class ConversionHelper
{
public static MyClass ConvertDataRowToMyClass(DataRow dataRow)
{
// some implementation
}
public static DataRow ConvertMyClassToDataRow(MyClass myClass)
{
// some implementation
}
}
How good is the approach i mentioned above? Please share ideas / alternates
Thanks
I would have done the below for myself.
public class TableColumns
{
public int TableColumn1 { get;set; }
public string TableColumn2 { get;set; }
public float TableColumn3 { get;set; }
//additional columns if any
}
public class PivotColumns
{
public string PivotColumn1 { get;set; }
public int PivotColumn2 { get;set; }
public float PivotColumn3 { get;set; }
//additional columns if any
}
public class MyClass : TableColumns, PivotColumns{ }
public static class ConversionHelper
{
public static List<MyClass> ConvertDataRowToMyClass(DataTable dt)
{
// some implementation
List<MyClass> ltMyClass = (from dr in dataTable.AsEnumerable()
select new MyClass
{
TableColumn1 = dr["TableColumn1"] == DBNull.Value || dr["TableColumn1"] == null ? default(int) : dr.Field<int>("TableColumn1"),
PivotColumn2 = dr.Field<int>("PivotColumn2"),
TableColumn2 = dr.Field<string>("TableColumn2")
}).ToList<MyClass>();
}
public static DataTable ConvertMyClassToDataRow(List<MyClass> lstMyClass)
{
// some implementation
PropertyDescriptorCollection properties =
TypeDescriptor.GetProperties(typeof(MyClass));
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;
}
}
I have copied the list to datatable conversion logic from here.

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