How to iterate on a T object to find a property? - c#

I have this code that I am currently adapting to use nested entity (entity framework). I have an entity which contains 2 property which are 2 children entities.
The first step was to read the metadata on both classes, starting from the first classes, building up a list of properties. This is completed. Now I need to iterate over my object to find the good property to do the DataBinding.
This is what I currently have :
variables example :
datasource = namespace.A
propriete = {System.Nullable`1[System.Int32] count}
propriete.DeclaringType {Name = "prop2" FullName = "Namespace.Metadata.prop2"}
code :
if (this.datasource != null)
{
var y = (T)this.datasource;
var propList = typeof(T).GetProperties();
if (propList.ToList().Contains(propriete))
{
TextBox.Text = DataBinder.Eval(y, propriete.Name).ToString();
}
else
{
TextBox.Text = ":( need child-support!";
}
}
My main problem is that my object type is unknown till runtime (Type T) so I have no idea on how to find my field.
Quick model :
Class A {
public B prop1;
public C prop2;
}
Class B {
int count;
string name;
}
Class C {
int count;
string name;
}
A.prop1.count = 1;
A.prop1.name = "a";
A.prop2.count = 2;
A.prop2.name = "b";
Right now, my property name are all unique (more specific than count/name), but I expect them to be the same (count/name) at some point.
propriete will probably have to "filter" with DeclaringType/ReflectedType for non-unique name.
A brute-force solution considering unique name, although not elegant, might be accepted.
Extra problem : propriete use another partial class contains in the metadata namespace while datasource use the main class.
(... And if you are curious as to what this system does : It builds a html table (with .net controls) based on an entity based on this metadata.entity dataAttribute.)

You can get to the type information by calling GetType() on an instance. So in your case it would be:
var propList = this.datasource.GetType().GetProperties()

Here's what I ended up with :
if (this.datasource != null)
{
var y = (T)this.datasource;
var propList = typeof(T).GetProperties();
//try to assign the prop but it might be on a child-prop.
try
{
retour = DataBinder.Eval(y, propriete.Name).ToString();
}
catch
{
foreach (PropertyInfo prop in propList)
{
if ((prop.PropertyType).FullName.Contains("Env."))
{
var childPropList = prop.PropertyType.GetProperties();
foreach (PropertyInfo childProp in childPropList)
{
if (((System.Reflection.MemberInfo)(childProp)).Name == propriete.Name)
{
var x = DataBinder.GetPropertyValue(y, prop.Name);
retour = DataBinder.Eval(x, propriete.Name).ToString();
}
}
}
}
}
}
For now it works, but it might not be versatile enough in a near future.

Related

Setting Order in the properties of a class

I have a class with 10 properties and I am using reflection to assign values to these properties. My Property names are like,
private double? _col1;
public double? Col1
{
get
{
return _col1;
}
set
{
_col1 = value;
OnPropertyChanged("Col1");
}
}
private double? _col2;
public double? Col2
{
get
{
return _col2;
}
set
{
_col2 = value;
OnPropertyChanged("Col2");
}
}
And I am using them like,
MyClass md = new MyClass();
PropertyInfo[] properties = typeof(MyClass).GetProperties();
{
foreach (PropertyInfo property in properties)
{
double? d1 = from some method();
if (property.PropertyType == typeof(Double?))
{
if (property.GetValue(md) == null)
{
property.SetValue(md, d1);
}
}
}
}
Here I want to use an orderby property name of the class. How ??
PropertyInfo[] properties = typeof(MyClass).GetProperties();
You need to sort properties alphabetically based on their name.
Add the following line before the foreach() loop
Array.Sort(properties, (x,y)=> x.Name.CompareTo(y.Name)
I think this is an XY problem.
I think what you actually need could be something like this:
public class MyClass
{
public ObservableCollection<double?> Cols { get; }
public MyClass(int initialSize)
{
if(initialSize < 0)
{
throw new ArgumentOutOfRangeException(nameof(initialSize));
}
Cols = new(Enumerable.Repeat((double?)null, initialSize));
}
}
Then, ditching the reflection, you can just use a for loop on the observable collection. The observable collection is great cause it already raises events informing about changes.
MyClass md = new MyClass(2);
for (int i = 0; i < md.Cols.Count; i++)
{
double? d1 = from some method();
if (md.Cols[i] is null)
{
md.Cols[i] = d1;
}
}
Then, if someone needs to listen to changes made to Cols, they subscribe to the Cols.CollectionChanged event.
Order of properties in reflection is the same as in their declaration in program code. Property declared first is first. Property declared second is second. I have never tested myself, but in case of partial classes, when a class is split into several files, the situation might be tricky.
how we can ensure that it takes first Col1, then secondly Col2, etc ??
To ensure, You should use names of the properties. Example
if (property.Name == nameof(MyClass.Col1))
{
property.SetValue(instanceOfClassName, value);
}
Alternative for if is switch statement
switch (property.Name)
{
case nameof(MyClass.Col1):
break;
case nameof(MyClass.Col2):
break;
}
If you want to order the properties by name then simply use OrderBy LINQ query
var orderedByNameProperties = properties.OrderBy(property => property.Name);

How to combine many properties into one List<string> property in C#

I am a little bit confused about how to read data from Excel. I am trying to import Excel for updating product list, I create an Excel model; I added all basic properties like name, price, quantity, etc. into this model. I will read all Excel and map into this model. That's ok, then I will give this model to EF Core 5 to save to SQL Server.
public class ExcelModel
{
public string Name { get; set }
public int Price { get; set }
public int Quantity { get; set }
}
I have a problem with product options. According to my DB schema, I have one table of products, one for options, one for option values, one for productOptionRelation.
Can you suggest another solution way or just solve on my way?
My colleges did this created field corresponding to values. like option1 and optionValue1, option2 and optionValue2 many of them, because each product could have many options. Model look like that, 20 option and 20 value was declared here and they manually map all these
For a temporary solution, I limited this option up to 5 and I created an list. and encapsulate all of them into list
public class ExcelOptionViewModel
{
public string Option { get; set; }
public string Value { get; set; }
}
This is my temp model, I encapsulated like that.
public IList<ExcelOptionViewModel> OptionModels { get; set; } = new List<ExcelOptionViewModel>();
public string Option1
{
get { return OptionModels[0].Option; }
set
{
this.OptionModels.Insert(0, new ExcelOptionViewModel { Option = value });
}
}
public string Option1Value
{
get { return OptionModels[0].Value; }
set { this.OptionModels[0].Value = value; }
}
This would be unlimited, You should enter how much you want
I have 2 solutions still I am researching one is, creating a method inside the excelviewmodel, this method will add all options and values into a list or I will use reflection, I am looking something like underlying type I will all option and values this underlying base type or something, when property loop came here, checking the type and assign all option1,option2,option3 or name like that properties to List<string> options, and same for the option values. I will use reading like option[0] and optionvalue[0]
Excel column names must be different because I read excel and turn it into datatable. Datatable column names must be different, it's not valid for reading into datatable
I used basically excel to data table function I can't remember but probably I found it in StackOverflow. Also, I added a feature there If some cell is null it will miss.
public List<T> ConvertDataTableToList<T>(DataTable dt)
{
//datatable clomun names
var columnNames = dt.Columns.Cast<DataColumn>().Select(c => c.ColumnName.ToLower()).ToList();
//selection properties equals to columnnames because I dont want loop for all props
var properties = typeof(T).GetProperties().Where(prp => columnNames.Any(t => t.ToLower() == prp.Name.ToLower()));
return dt.AsEnumerable().Select(row =>
{
var objT = Activator.CreateInstance<T>();
foreach (var pro in properties)
{
try
{
if (row[pro.Name] != DBNull.Value)
pro.SetValue(objT, row[pro.Name], null);
else
pro.SetValue(objT, null, null);
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
return objT;
}).ToList();
}
I am looking something here when option1 or option2 comes here it would put this into a list
Also in my dt to model converter I dont want to use If but if some data value is null It throws an error which cant convert from dbnull value. If you have a suggest for it I would like release if condition :)
When All done I will map this excelviewmodel to product model something like this
foreach (var prop in SideParams.columns)
{
var source = row.GetType().GetProperty(prop);
var destination = product.GetType().GetProperty(prop);
if (destination != null && source.GetValue(row) != null)
{
Type t = Nullable.GetUnderlyingType(destination.PropertyType) ?? destination.PropertyType;
object safeValue = Convert.ChangeType(source.GetValue(row), t);
destination.SetValue(product, safeValue);
}
}
I saw something here
https://learn.microsoft.com/en-us/dotnet/api/system.reflection.bindingflags?view=net-6.0
it about binding flangs when reflecting model. "Specifies flags that control binding and the way in which the search for members and types is conducted by reflection." If there is way I can redirect option(1-2-3-4-5-6...) to list options
thanks for the help I solved my problem. If you need something like that, my solution is;
As you know OptionModels is what I created before, AddOptipns function is a new one I use for add data to list,
The function work with the ref, otherwise it must be static, if I turn it static, option models also must be static, so I can't access the list.
public IList<ExcelOptionViewModel> OptionModels { get; set; } = new List<ExcelOptionViewModel>();
public void AddOptions(ref String option, ref String value)
{
OptionModels.Add(new ExcelOptionViewModel { Option = option.Trim(), Value = value.Trim() });
}
And also add some new parts to convert model function,
calling that AddOptions method with reflection, I got an example from here
https://learn.microsoft.com/en-us/dotnet/api/system.reflection.bindingflags?view=net-6.0
I was inspired by the swap example there.
public List<T> ConvertDataTableToList<T>(DataTable dt)
{
var columnNames = dt.Columns.Cast<DataColumn>().Select(c => c.ColumnName.ToLower()).ToList();
//selection properties equals to columnnames because I dont want loop for all props
var type = typeof(T);
var properties = type.GetProperties().Where(prp => columnNames.Any(t => t.ToLower() == prp.Name.ToLower())).ToList();
var productOptions = columnNames.Where(x => x.Contains("option")).ToList() ?? new List<string>();
return dt.AsEnumerable().Select(row =>
{
var objT = Activator.CreateInstance<T>();
foreach (var pro in properties)
{
try
{
if (row[pro.Name] != DBNull.Value)
pro.SetValue(objT, row[pro.Name], null);
else
pro.SetValue(objT, null, null);
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
for (var i = 0; i < productOptions.Count(); i += 2)
{
object[] argValues = new object[] { row[productOptions[i]].ToString(), row[productOptions[i + 1]].ToString() };
String[] argNames = new String[] { "option", "value" } ;
var method = type.GetMethod("AddOptions");
method.Invoke(objT, argValues);
}
return objT;
}).ToList();
}
here is the added data :)

How to dynamically cast a property to an unknown class and perform a check on it in C# Entity Framework?

I'm trying to put a series of functions together for Entity Framework use. The idea is that I want to pass generic classes all into one routine, and then have the routine "type" it for me and act accordingly. I can't seem to figure out how to marry the Class back up with it's PropertyType.
public void AddUpdate(string classType, Object o)
{
//This gets the Type of my Class Object ok.
Type mType = Type.GetType(GetType().Namespace + "." + classType, true);
var meContext = new ClsContext(_ConnectionString);
//This retrieves the correct primary key for my the Class Object.
string key = FncGetPrimaryKey(meContext, classType + "s");
//I've tried this as a PropertyInfo as well instead of a Var
var keyID = o.GetType().GetProperty(key);
//I've tried this as Var as well as Dynamic
dynamic obj = o;
//Now I'm stuck, because I want to evaluate the property,
//but I get an error "<MyClass> does not contain a reference for 'keyID'
if (obj.keyID == 0) //ERROR ON THIS LINE
{
meContext.Entry(o).State = EntityState.Added;
}
else
{
meContext.Entry(o).State = EntityState.Modified;
}
meContext.SaveChanges();
}
Although, I did not fully understand why do you want it like this, I think, I understood what you are trying to achieve. This is how I do it in case if I want to add some common object:
public void AddUpdate<T>(T obj) where T : IEntity
{
using(var ctx = new ClsContext(_ConnectionString))
{
if (obj.keyID == 0)
{
ctx.Entry(o).State = EntityState.Added;
}
else
{
ctx.Entry(o).State = EntityState.Modified;
}
ctx.SaveChanges();
}
}
public interface IEntity
{
int keyId {get;set;}
}
Try:
var keyValue = keyID.GetValue(o);
if (keyValue.Equals(0)) {...

Is there a way to iterate through an EF6 Entity?

I have a web form with around 50 fields that is used for crud operations on an Oracle DB, I am using EF6.
Currently, I accomplish this like so:
private GENERIC_FTP_SEND GetFields()
{
GENERIC_FTP_SEND ftpPartner = new GENERIC_FTP_SEND();
//Contact Info
ftpPartner.FTP_LOOKUP_ID = FTP_LOOKUP_IDTB.Text;
ftpPartner.PARTNER_NAME = PARTNER_NAMETB.Text;
ftpPartner.REMEDY_QUEUE = REMEDY_QUEUETB.Text;
ftpPartner.PRIORITY = PRIORITYBtns.SelectedValue;
ftpPartner.CONTACT_EMAIL = CONTACT_EMAILTB.Text;
ftpPartner.CONTACT_NAME = CONTACT_NAMETB.Text;
ftpPartner.CONTACT_PHONE = CONTACT_PHONETB.Text;
...
}
where GENERIC_FTP_SEND is the name of the virtual DbSet in my Model.context.cs.
This works fine but is not reusable in the least. What I would like to accomplish is to have some code that allows me to iterate through the attributes of ftpPartner and compare them to the field id for a match. Something like this:
var n =0;
foreach (Control cntrl in ControlList){
if(cntrl.ID == ftpPartner[n]){
ftpPartner[n] = cntrl.Text;
}
n++;
}
In case you need/want to see it here is my Model.context.cs
public partial class Entities : DbContext{
public Entities(): base("name=Entities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<GENERIC_FTP_SEND> GENERIC_FTP_SEND { get; set; }
}
I saw the question here but I am not sure how to implement that in my case.
Entity Framework 6: is there a way to iterate through a table without holding each row in memory
You can achieve that with reflection:
var type = typeof(GENERIC_FTP_SEND);
foreach (Control cntrl in ControlList){
Object value = null;
if (cntrl is TextBox){
value = (cntrl as TextBox).Text;
} else if (cntrl is GroupBox){
value = (cntrl as GroupBox).SelectedValue;
} //etc ...
PropertyInfo pInfo = type.GetProperty(cntrl.ID);
if (pInfo != null && value != null){
pInfo.SetValue(ftpPartner, value, null);
}
}
You can also use the Entity Framework context object as well to accomplish the same if you know you are going to insert.
var x = new GENERIC_FTP_SEND();
// Add it to your context immediately
ctx.GENERIC_FTP_SEND.Add(x);
// Then something along these lines
foreach (Control cntrl in ControlList)
{
ctx.Entry(x).Property(cntrl.Name).CurrentValue = ctrl.Text;
}

C# Generic parameter from a string variable

I have two classes, Customer and Country. Customer has a property called HomeCountry, which i have decorated with a custom attribute called "Lookup" and takes a string parameter "Country". The purpose is, when I am using the Customer class, the item in HomeCountry must exist in the Country class (which happens to be a list).
I am using reflection to iterate the Customer class, it finds the attribute and i want it to check the value in the list of country items. So far I have:
foreach (PropertyInfo _pi in object.GetType().GetProperties()) {
IEnumerable<Attribute> _attrs = _pi.GetCustomAttributes();
foreach (Attribute _a in _attrs) {
Object obj = Activator.CreateInstance(_type, null);
// what goes here?
}
}
I have a method:
public T Populate<T>(params string[] _parameters)
I think i want to do
List<obj> v = populate<obj>();
or
List<typeof(obj)> v = populate<typeof(obj)>();
but obviously nothing works! Can anybody help me ?
Thanks
OK i will try and provide a full example:
I have a CUSTOMER_ORDER class:
public class CUSTOMER_ORDER {
public CUSTOMER_ORDER() {}
[Key(0)]
public string OrderNumber {get;set;}
public MPCCOM_SHIP_VIA ShipVia {get;set;}
}
Then the MPCCOM_SHIP_VIA class:
public class MPCCOM_SHIP_VIA {
public MPCCOM_SHIP_VIA() {}
[Key(0)]
public string ID {get;set;}
public string Description {get;set;}
}
I have a method called Populate< T > which takes a class and then uses reflection to loop all the properties and build a select statement, executes it, and then returns the data and populates the object:
public T Populate<T>(params string[] #Parameters)
{
Type _t = typeof(T);
dynamic _o = Activator.CreateInstance(typeof(T), null);
SqlBuilder _sb = new SqlBuilder();
_sb.Table = string.Format("{0}.{1}", _Owner, _t.Name.ToString());
foreach (PropertyInfo p in _t.GetProperties(Utilities.BindingFlags))
{
if (p.GetMethod.IsPrivate == false) _sb.Fields.Add(p.Name.ToString());
IEnumerable<Attribute> _attrs = p.GetCustomAttributes();
foreach (Attribute _a in _attrs)
{
if (_a.TypeId.ToString().Equals(typeof(Key).FullName))
{
int _position = ((Key)_a).Position;
try
{
string _parameter = #Parameters[_position];
_sb.Where.Add(string.Format("{0} = '{1}'", p.Name, _parameter));
}
catch {}
}
}
}
using (OleDbCommand _cmd = new OleDbCommand())
{
_cmd.Connection = this._cn;
_cmd.CommandText = _sb.SQL;
if (_trn != null) _cmd.Transaction = _trn;
_cmd.CommandType = System.Data.CommandType.Text;
using (OleDbDataReader _reader = _cmd.ExecuteReader())
{
if (_reader.Read())
{
for (int x = 0; x < _reader.FieldCount; x++)
{
foreach (PropertyInfo p in _t.GetProperties(Utilities.BindingFlags))
{
if (p.GetMethod.IsPrivate == false)
{
if (p.Name.Equals(_reader.GetName(x).ToString()))
{
dynamic _val = _reader.GetValue(x);
if (p.ReflectedType.BaseType.Name.Equals(""))
{
// what goes here!
}
try
{
p.GetSetMethod(true).Invoke(_o, new object[] { _val });
}
catch { }
break;
}
}
}
}
}
else
{
throw new DatabaseObjectNotFound(_t.Name.ToString(), string.Join(",",#Parameters));
}
}
}
return (T)_o;
}
So, as i read an order, the source DB gets the key to the MPCCOM_SHIP_VIA in the respective field, i want to call the same Populate method against the MPCCOM_SHIP_VIA object with the key. I hope this makes more sense demonstrating what i want to do. And thanks
After some hunting around, this is the answer i was looking for...
MethodInfo method = typeof(class).GetMethod("Populate");
method = method.MakeGenericMethod(p.PropertyType);
_val = method.Invoke(class, new object[] { _prms });
I guess my issue was i was asking the wrong question!

Categories