Reading properties of hierarchical classes generically using reflection in C# - c#

I have a set of classes that are generated via xsd.exe based on *.xsd files. Each set of classes generated for each set of *.xsd files are almost the same, except that there are a few properties different in each class. For example, in both set 1 and set 2, Shape class might have different property.
I want to use a method to read the value of specified properties (UPDATE) of all classes in one set at a time (Update). There could be many levels of class. The method (e.g. GetProperty method) should only know about its parameter type, and the string representation of all properties. The method only read property.
Is there a better way of doing this:
internal class DynPropertyTest
{
public static void Set1Test()
{
Shape shape = new Shape()
{
Name = "Shape",
Shape2 = new Shape2()
{
Name = "Shape2",
Shape3 = new Shape3() { Name = "Shape3" }
}
};
GetProperty(shape);
}
public static void Set2Test() {
...
}
UPDATE
I would like to know an alternative way to achieve the similar function to the the method below
//Should store all properties value in a collection, rather than display it
//And somehow store/know the type of the property.
public static void GetProperty(ShapeBase shape)
{
//shape.Name
Console.WriteLine(shape.GetType().GetProperty("Name").GetValue(shape, null));
//Shape.Shape2
object shape2 = shape.GetType().GetProperty("Shape2").GetValue(shape, null);
Console.WriteLine(shape2);
//Shape.Shape2.Name
object shape2Name = shape.GetType().GetProperty("Shape2").PropertyType.GetProperty("Name")
.GetValue(shape2, null);
Console.WriteLine(shape2Name);
//shape.shape2.shape3
object shape3 = shape2.GetType().GetProperty("Shape3").GetValue(shape2, null);
Console.WriteLine(shape3);
//shape.shape2.shape3.Name
object shape3Name = shape2.GetType().GetProperty("Shape3").PropertyType.GetProperty("Name")
.GetValue(shape3, null);
Console.WriteLine(shape3Name);
}
}
abstract class ShapeBase { }
//Example only.
namespace Set1
{
class Shape : ShapeBase
{
public string Name { get; set; }
public Shape2 Shape2 { get; set; }
}
class Shape2
{
public string Name { get; set; }
public Shape3 Shape3 { get; set; }
}
class Shape3
{
public string Name { get; set; }
}
}
namespace Set2
{
class Shape : ShapeBase{}
...
}

This is how we implement this functionality (this is for getting a String value for dynamic SQL, but you should be able to apply it to your situation:
public static string GetFieldValueForSQL(object oRecord, string sName)
{
PropertyInfo theProperty = null;
FieldInfo theField = null;
System.Type oType = null;
try
{
oType = oRecord.GetType();
// See if the column is a property in the record
theProperty = oType.GetProperty(sName, BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Public, null, null, new Type[0], null);
if (theProperty == null)
{
theField = oType.GetField(sName, BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Public);
if (theField != null)
{
return theField.GetValue(oRecord).ToString();
}
}
else
{
return theProperty.GetValue(oRecord, null).ToString();
}
}
catch (Exception theException)
{
// Do something with the exception
return string.empty;
}
}
Update
To process all of the properties, you can use code similar to this (note that this does not take into account IEnumerable implementors, but that should be fairly straightforward):
public static void GetAllProperties(object oRecord)
{
System.Type oType = null;
try
{
oType = oRecord.GetType();
PropertyInfo[] cProperties;
cProperties = oType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (PropertyInfo theProperty in cProperties)
{
if (theProperty.PropertyType.IsClass)
{
GetAllProperties(theProperty.GetValue(oRecord, null));
}
else
{
// use theProperty.GetValue(oRecord, null).ToString();
}
}
}
catch (Exception theException)
{
// Do something with the exception
}
}

Related

Get complex type properties from PropertyInfo

I trying to build a generic recursive function to iterate all properties / complex properties and return an array of all properties from the following structre:
public class Root
{
[FieldCodeItems(1, EnumFieldCode.INT, "ED", "0204")]
public int Prop1 { get; set; }
public Child Child { get; set; }
}
public class Child
{
[FieldCodeItems(1, EnumFieldCode.INT, "ED", "0208")]
public int PropChild1 { get; set; }
[FieldCodeItems(19, EnumFieldCode.ALPHANUMERIC, "ED", "0208")]
public string PropChild2 { get; set; }
public Child1 Child1 { get; set; }
}
public class Child1
{
[FieldCodeItems(1, EnumFieldCode.INT, "ED", "0211")]
public int PropChild3 { get; set; }
}
public class MyReturClass
{
public string FileCode { get; set; }
public string FieldCode { get; set; }
}
I can read all properties from Root class but I can't get the properties from the complex properties:
public static List<MyReturClass> GetItems<T>(T obj)
{
var ret = new List<MyReturClass>();
PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo property in properties)
{
IEnumerable<Attribute> attributes = property.GetCustomAttributes();
foreach (Attribute attribute in attributes)
{
//here I read values from a custom property
var tr = (FieldCodeItems)attribute;
var value = obj.GetType().GetProperty(property.Name).GetValue(obj, null);
if (value == null) continue;
ret.Add(new MyReturClass
{
FieldCode = tr.FieldCode,
FileCode = tr.FileCode
});
}
//If is complex object (Child, Child1 etc)
if (property.PropertyType.IsClass && property.PropertyType != typeof(string))
{
//I would like pass the complex property as parameter, but at this moment
//the property variable is a PropertyInfo and I need the complex type
//to get properties values from this object
GetItems(property); //Error. Also tried with GetItems(property.PropertyType);
}
}
return ret;
}
I would like pass the complex property as parameter, but at this moment the property variable is a PropertyInfo and I need the complex type to get properties values from this object.
How can I get this object?
I searched here, here and here but the solutions don't solve my problem.
Edit - 03-08-2018
Finally I found the awswer here
I added this code to solve the problem:
//If is complex object (Child, Child1 etc)
if (property.PropertyType.IsClass && property.PropertyType != typeof(string))
{
if (obj == null) { return null; }
Type type = obj.GetType();
PropertyInfo info = type.GetProperty(property.Name);
if (info == null) { return null; }
var v = info.GetValue(obj, null);
if (v != null)
GetItems(v);
}
have a look at https://learn.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/how-to-examine-and-instantiate-generic-types-with-reflection
I am not sure if what you are looking for is going to work because in you EF you are configuring the classes as ComplexType. I am not sure the class itself knows its a complexType. You might end up creating a method on the DBcontext...

Object must implement IConvertible when converting Array to Object Class [duplicate]

foreach (var filter in filters)
{
var filterType = typeof(Filters);
var method = filterType.GetMethod(filter, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Static);
if (method != null)
{
var parameters = method.GetParameters();
Type paramType = parameters[0].ParameterType;
value = (string)method.Invoke(null, new[] { value });
}
}
How can I cast value to paramType? value is a string, paramType will probably just be a basic type like int, string, or maybe float. I'm cool with it throwing an exception if no conversion is possible.
The types you are using all implement IConvertible. As such you can use ChangeType.
value = Convert.ChangeType(method.Invoke(null, new[] { value }), paramType);
You could go dynamic; for example:
using System;
namespace TypeCaster
{
class Program
{
internal static void Main(string[] args)
{
Parent p = new Parent() { name = "I am the parent", type = "TypeCaster.ChildA" };
dynamic a = Convert.ChangeType(new ChildA(p.name), Type.GetType(p.type));
Console.WriteLine(a.Name);
p.type = "TypeCaster.ChildB";
dynamic b = Convert.ChangeType(new ChildB(p.name), Type.GetType(p.type));
Console.WriteLine(b.Name);
}
}
internal class Parent
{
internal string type { get; set; }
internal string name { get; set; }
internal Parent() { }
}
internal class ChildA : Parent
{
internal ChildA(string name)
{
base.name = name + " in A";
}
public string Name
{
get { return base.name; }
}
}
internal class ChildB : Parent
{
internal ChildB(string name)
{
base.name = name + " in B";
}
public string Name
{
get { return base.name; }
}
}
}

Convert object by reflection

I want to convert an object A to object B. The classes A and B have the same properties, just the names are changed.
I use this method:
/// <summary>
internal static T objectMapper<T>(object objectSource, T objectTarget)
{
dynamic o = objectSource;
Type typeA = objectSource.GetType();
Type typeB = objectTarget.GetType();
IList<PropertyInfo> propsA = new List<PropertyInfo>(typeA.GetProperties());
IList<PropertyInfo> propsB = new List<PropertyInfo>(typeB.GetProperties());
dynamic s;
ArrayList listArray = new ArrayList();
foreach (var prop in propsA)
{
s = objectSource.GetType().GetProperty(prop.Name).GetValue(objectSource, null);
listArray.Add(s);
}
int i = 0;
foreach (var prop in propsB)
{
prop.SetValue(objectTarget, listArray[i], null);
i++;
}
return objectTarget;
}
How can I edit properties of objectB in the foreach loop? I want to use a generic method for different objects.
This solution provides both your reflection-way and an alternative way by defining and implementing a copy method CopyFrom. To reduce code you could make the interface a base-class so you don't need to implement CopyFrom in the sub-classes....
public interface MyInterface
{
int Prop1 { get; set; }
string Prop2 { get; set; }
void CopyFrom(MyInterface obj);
}
public class A: MyInterface
{
public int Prop1 { get; set; }
public string Prop2 { get; set; }
public void CopyFrom(MyInterface obj)
{
this.Prop1 = obj.Prop1;
this.Prop2 = obj.Prop2;
}
}
public class B: MyInterface
{
public int Prop1 { get; set; }
public string Prop2 { get; set; }
public void CopyFrom(MyInterface obj)
{
this.Prop1 = obj.Prop1;
this.Prop2 = obj.Prop2;
}
}
public static class CopyUtils
{
public static void Copy(MyInterface src, MyInterface dst)
{
var props = typeof(MyInterface).GetProperties();
foreach(var prop in props)
{
prop.SetValue(dst, prop.GetValue(src, null), null);
}
}
}
I feel there might be a deeper architecture issue here. I'm failing to imagine why would you want to "copy" the values of the properties from one object of a class to another of a different class with the same property names.
If you're trying to "shape" the object maybe just passing an interface will do the work
Anyhow, see if this helps:
public static class ObjectMorpher
{
public class InvalidMorphException : Exception
{
}
[AttributeUsage(AttributeTargets.Property)]
public class IgnoredOnMorphAttribute : Attribute
{
}
public static TargetType Morph<TargetType>(this object source, TargetType dest, Func<string, string> propertyMatcher = null, bool failOnNoMatch = false)
where TargetType : class
{
if (source == null || dest == null)
throw new ArgumentNullException();
foreach (var sourceProp in source.GetType().GetProperties().Where(x => x.GetCustomAttributes<IgnoredOnMorphAttribute>().Any() == false))
{
var destProp = typeof(TargetType).GetProperties().Where(x => x.Name == ((propertyMatcher == null) ? sourceProp.Name : propertyMatcher(sourceProp.Name))).FirstOrDefault();
//check property exists
if (destProp == null)
{
if (failOnNoMatch)
throw new InvalidMorphException();
else
continue;
}
//check value type is assignable
if (!destProp.GetType().IsAssignableFrom(sourceProp.GetType()))
{
if (failOnNoMatch)
throw new InvalidMorphException();
else
continue;
}
destProp.SetValue(dest, sourceProp.GetValue(source));
}
return dest;
}
}
Usage example:
var A = new ClassA();
var B = new ClassB();
B = A.Morph(B);
Optionally you can set a property match for the case when properties doesn't have the exact same name.
Also notice the use of the IgnoredOnMorph attribute to mark properties as not morph-able (like calculated properties)
You might find automapper of use here (see https://github.com/AutoMapper/AutoMapper/wiki/Getting-started).
You would need to create a line for each object mapping in a startup file to set it up but if the properties are the same this would be as simple as:
mapper.CreateMap<ClassA, ClassB>().ReverseMap();
And then a single line to resolve the mapping when needed
mapper.Map(objectOfClassA, new ClassB());

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.

How can I set a default value inside of a class as a property of another class?

I need to set a property inside of a class from another class that defines the first class as a property. I want to default a value inside the child class. An example of this would be:
public enum NamingConvention
{
Name1 = 1,
Name2
}
public class Class1
{
public Class1()
{
}
public int Id { get; set; }
public NamingConvention Naming{ get; set; }
}
public class Class2
{
public Class2()
{
}
public List<Class1> Name1s { get; set; }
}
public class Class3
{
public Class2()
{
}
public List<Class1> Name2s { get; set; }
}
I want to be able to put an attribute or something over the Class1 property inside of Class2 and Class3 so that in Class2, the Naming Property gets set to Name1 and and for Class3, it would be automatically set to Name2.
Hope that makes sense. I tried to make this as simple an example as possible. Any ideas out there? I am trying to avoid abstract classes because my real entities are tied to nHibernate and don't want to change the model at this time.
This can be accomplished with the use of the DefaultValueAttribute, a custom TypeConverter and Reflection. It seems unlikely this will perform better than what you are currently doing, but I'll leave that for you to evaluate.
Apply the TypeConverter attribute to Class 1
[TypeConverter(typeof(Class1Converter))]
public class Class1
{
public int Id { get; set; }
public NamingConvention Naming { get; set; }
}
public enum NamingConvention
{
Name1 = 1,
Name2,
Name3,
Name4
}
Define the Class1Converter. Note this simple converter only sets the value of the NamingConvention parameter.
public class Class1Converter: TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context,
Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
public override bool CanConvertTo(ITypeDescriptorContext context,
Type destinationType)
{
if(destinationType == typeof(Class1))
{
return true;
}
return base.CanConvertTo(context, destinationType);
}
public override object ConvertFrom(ITypeDescriptorContext context,
System.Globalization.CultureInfo culture,
object value)
{
var stringValue = value as string;
if(stringValue != null)
{
return new Class1
{
Naming = (NamingConvention)Enum.Parse(typeof(NamingConvention), stringValue)
};
}
return base.ConvertFrom(context, culture, value);
}
}
For convenience I am declaring this in an Extension Method, it could easily be set up as part of the classes with defaults...
public static class DefaultExtension
{
public static IEnumerable<PropertyInfo> GetProperties<T>(this Type type)
{
return type.GetProperties().Where(p => p.PropertyType == typeof (T));
}
public static void SetDefaults<T>(this T toDefault)
{
foreach (PropertyInfo p in toDefault.GetType().GetProperties())
{
foreach (var dv in p.GetCustomAttributes(true).OfType<DefaultValueAttribute>())
{
p.SetValue(toDefault, dv.Value, null);
}
}
}
}
Finally you declare place DefaultValue attributes on your properties. I am calling SetDefaults() from the constructors here for convenience again, in your case you would still need to call it after the instances are loaded from NHibernate.
public class Class2
{
public int X { get; set; }
[DefaultValue(typeof(Class1), "Name2")]
public Class1 Name2Class { get; set; }
public Class2()
{
this.SetDefaults();
}
}
public class Class3
{
public int Y { get; set; }
[DefaultValue(typeof(Class1), "Name3")]
public Class1 Name3Class { get; set; }
public Class3()
{
this.SetDefaults();
}
}
Unit test demonstrating validity...
[Test]
public void TestDefaultValueAttribute()
{
//Class2 have Name2 as the default value for the Naming property
var c2 = new Class2();
Assert.That(c2,Is.Not.Null);
Assert.That(c2.Name2Class, Is.Not.Null);
Assert.That(c2.Name2Class.Naming, Is.EqualTo(NamingConvention.Name2));
//Class3 have Name3 as the default value for the Naming Property
var c3 = new Class3();
Assert.That(c3, Is.Not.Null);
Assert.That(c3.Name3Class, Is.Not.Null);
Assert.That(c3.Name3Class.Naming, Is.EqualTo(NamingConvention.Name3));
//wipes out other properties of the Class1 attribute.
// to demonstrate, set properties to something other than the default then call
// SetDefaults again.
c3.Name3Class.Naming = NamingConvention.Name1;
c3.Name3Class.Id = 10;
c3.SetDefaults();
Assert.That(c3.Name3Class.Id, Is.EqualTo(0));
Assert.That(c3.Name3Class.Naming, Is.EqualTo(NamingConvention.Name3));
}
You will notice that this wipes out the Id property of Class1 If this is not desired, you could come up with a more targeted version of SetDefaults that only overwrote specific properties of Class1. At this point I don't know if I would really continue using DefaultValue, as use case deviates from the original and using this in combination with the above method would produce unexpected results. I would probably write a custom 'DefaultNaminingConventionAttribute for this purpose.
public static void SetDefaultNamingConvention<T>(this T toDefault)
{
foreach (PropertyInfo p in toDefault.GetType().GetProperties<Class1>())
{
foreach (var dv in p.GetCustomAttributes(true).OfType<DefaultValueAttribute>())
{
var pValue = p.GetValue(toDefault, null) as Class1;
if (pValue != null)
{
pValue.Naming = ((Class1)dv.Value).Naming;
}
else
{
p.SetValue(toDefault, dv.Value, null);
}
}
}
}
[Test]
public void SetDefaultNamingConventionDefaultShouldOnlyDefaultNamingProperty()
{
var c3 = new Class3();
c3.Name3Class.Naming = NamingConvention.Name1;
c3.Name3Class.Id = 20;
c3.SetDefaultNamingConvention();
Assert.That(c3.Name3Class.Id, Is.EqualTo(20));
Assert.That(c3.Name3Class.Naming, Is.EqualTo(NamingConvention.Name3));
}
EDIT: Updated to deal with setting defaults for list members
With this new SetListDefaults extension method, we now can apply the default to members of List<Class1>. Here I would almost definitely no longer use DefaultValue, but would define a custom attribute for use with collections. This is beyond the scope of the question though.
public static class DefaultExtension
{
public static IEnumerable<PropertyInfo> GetProperties<T>(this Type type)
{
return type.GetProperties().Where(p => p.PropertyType == typeof (T));
}
public static void SetListDefaults<T>(this T toDefault)
{
foreach (PropertyInfo p in toDefault.GetType().GetProperties<List<Class1>>())
{
foreach (var dv in p.GetCustomAttributes(true).OfType<DefaultValueAttribute>())
{
var pValue = p.GetValue(toDefault, null) as List<Class1>;
if (pValue != null)
{
foreach (var class1 in pValue)
{
class1.Naming = ((Class1) dv.Value).Naming;
}
}
}
}
}
}
Now provided a class with a List property...
public class Class4
{
public int Z { get; set; }
[DefaultValue(typeof (Class1), "Name4")]
public List<Class1> Name4Classes { get; set; }
}
And a unit test to verify only the Naming Property of each item in the list is modified.
[Test]
public void SetListDefaultsShouldResetNamingConventionOfEachListMember()
{
var c4 = new Class4
{
Z = 100,
Name4Classes = new List<Class1>
{
new Class1 {Id = 1, Naming = NamingConvention.Name1},
new Class1 {Id = 2, Naming = NamingConvention.Name2},
new Class1 {Id = 3, Naming = NamingConvention.Name3}
}
};
Assert.That(c4.Name4Classes, Is.Not.Empty);
Assert.That(c4.Name4Classes.Count, Is.EqualTo(3));
Assert.That(c4.Name4Classes.Any(c => c.Id == 0), Is.False);
Assert.That(c4.Name4Classes.Any(c => c.Naming == NamingConvention.Name4), Is.False);
c4.SetListDefaults();
Assert.That(c4.Name4Classes, Is.Not.Empty);
Assert.That(c4.Name4Classes.Count, Is.EqualTo(3));
Assert.That(c4.Name4Classes.Any(c=> c.Id == 0), Is.False);
Assert.That(c4.Name4Classes.All(c=> c.Naming == NamingConvention.Name4), Is.True);
}
I would use the constructors.
In Class2's constructor:
public Class2()
{
Name1Class = new Class1()
Name1Class.Naming = NamingConvention.Name1
}
In Class3's Constructor:
public Class3()
{
Name2Class = new Class1()
Name2Class.Naming = NamingConvention.Name2
}
If you want to get fancy you could put a parameter on the constructor in Class1 to allow you to set Naming when the object is created.

Categories

Resources