Get complex type properties from PropertyInfo - c#

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...

Related

.Net reflection to get nested property with custom attribute

I want to know the best way to get the prop info and value using reflection for a nested class by its custom attribute name.
With below code I can get the prop info via recursion. But is there a better way or using LINQ. Note that I do not want to hard code the class type as similar to other solution
I also want to get the property value by custom attribute
e.g var propValue = ?????
public class PlanetRoot
{
public void GetNeighborMoon()
{
Planet planet = new Planet();
Product product = new Product();
Neighbor neighbor = new Neighbor();
neighbor.Moons = 10;
neighbor.RingColor = "Red";
product.Neighbors = new List<Neighbor>();
product.Neighbors.Add(neighbor);
planet.Product = product;
//1. Get the RingColor property info of neighbor with attribute MyDBField(Name = "NeighborRing") . Is there a better way
PropertyInfo propInfo = null;
DoRecursiveGetProperty(planet.GetType(), "NeighborRing", out propInfo );
//2. Get the RingColor property value of neighbor with attribute MyDBField(Name = "NeighborRing")
//var propValue = GetPropertyValue(????);
}
}
private static PropertyInfo DoRecursiveGetProperty(Type type, string attribName, out PropertyInfo propInfo)
{
PropertyInfo[] pi = type.GetProperties();
propInfo= null;
foreach (PropertyInfo p in pi)
{
var dbFieldAttribute = (MyDBFieldAttribute)Attribute.GetCustomAttribute(p, typeof(MyDBFieldAttribute));
if (dbFieldAttribute != null && attribName.ToUpper() == dbFieldAttribute.Name.ToUpper())
{
propInfo= p;
//Console.WriteLine(p.Name + " : " + (dbFieldAttribute != null && dbFieldAttribute.Name != null ? dbFieldAttribute.Name : "****"));
return true;
}
if (p.PropertyType.IsClass && !p.PropertyType.IsValueType && !p.PropertyType.IsPrimitive
&& p.PropertyType.FullName != "System.String")
if (propInfo != null) return true;
else DoRecursiveGetProperty(p.PropertyType, attribName, out propInfo);
}
return false;
}
public class Planet
{
public string PlanetId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public Product Product { get; set; }
[MyDBField(Name="PubDate")]
public string Publishdate { get; set; }
}
public class Product
{
public string ProductId { get; set; }
public List<Neighbor> Neighbors { get; set; }
}
public class Neighbor
{
[MyDBField(Name = "NeighborRing")]
public string RingColor { get; set; }
public int Moons { get; set; }
}
public class MyDBFieldAttribute : System.Attribute
{
public string Name { get; set; }
}
In order to get the value for a nested member that might be in a collection, you need to iterate the collections, and track the current object.
Assuming you don't need the resulting PropInfo for other reasons, just try to get the value:
private static bool TryRecursiveGetValueWithMyDBFieldName(object startObject, string attribName, out object propValue) {
PropertyInfo[] pi = startObject.GetType().GetProperties();
foreach (PropertyInfo p in pi) {
var dbFieldAttribute = (MyDBFieldAttribute)Attribute.GetCustomAttribute(p, typeof(MyDBFieldAttribute));
if (dbFieldAttribute != null && dbFieldAttribute.Name.Equals(attribName, StringComparison.CurrentCultureIgnoreCase)) {
//Console.WriteLine(p.Name + " : " + (dbFieldAttribute != null && dbFieldAttribute.Name != null ? dbFieldAttribute.Name : "****"));
propValue = p.GetValue(startObject);
return true;
}
if (p.PropertyType.IsClass && !p.PropertyType.IsValueType && !p.PropertyType.IsPrimitive &&
!p.PropertyType.FullName.StartsWith("System.")) {
var tryObject = p.GetValue(startObject);
if (tryObject != null && TryRecursiveGetValueWithMyDBFieldName(tryObject, attribName, out propValue))
return true;
}
if (p.PropertyType.IsClass && p.GetValue(startObject) is IEnumerable ip) {
foreach (var obj in ip) {
if (obj != null && TryRecursiveGetValueWithMyDBFieldName(obj, attribName, out propValue))
return true;
}
}
}
propValue = default;
return false;
}
To use it, call with the initial object:
var foundAttrib = TryRecursiveGetValueWithMyDBFieldName(planet, "NeighborRing", out var propValue);
NOTE: This will return the value of the first object with a matching attribute, as e.g. every member of the List<Neighbor> member will have the MyDBField attribute with the Name property of NeighborRing.

How can I get the value of specific key inside a model dynamically?

I have here my model:
public class RoleModel
{
public int ID { get; set; }
public string RoleName { get; set; }
public UserRoleModel TrackAndTrace { get; set; }
public UserRoleModel MailDelivery { get; set; }
public UserRoleModel MailAccounting { get; set; }
public UserRoleModel PerformanceMonitoring { get; set; }
public UserRoleModel AdminSettings { get; set; }
}
I want to get the value of any key of 'RoleModel'
public bool SaveRoleModule(RoleModel role)
{
PropertyInfo[] properties = typeof(RoleModel).GetProperties();
foreach (PropertyInfo property in properties)
{
if(property.Name != "ID" && property.Name != "RoleName")
{
Console.WriteLine(role[property.Name]);//(this doesn't work) I want it dynamic
Console.WriteLine(role.TrackAndTrace); //not like this
}
}
return true;
}
I used loop to shorten my code.
You can use this method here: Get property value from string using reflection in C# like this:
public bool SaveRoleModule(RoleModel role)
{
PropertyInfo[] properties = typeof(RoleModel).GetProperties();
foreach (PropertyInfo property in properties)
{
if(property.Name != "ID" && property.Name != "RoleName")
{
var value = GetPropValue(role, property.Name);
Console.WriteLine(value);//(this doesn't work) I want it dynamic
Console.WriteLine(role.TrackAndTrace); //not like this
}
}
return true;
}
public static object GetPropValue(object src, string propName)
{
return src.GetType().GetProperty(propName).GetValue(src, null);
}
You could try something like this:
public bool SaveRoleModule(RoleModel role)
{
PropertyInfo[] properties = typeof(RoleModel).GetProperties();
foreach (PropertyInfo property in properties)
{
if(property.Name != "ID" && property.Name != "RoleName")
{
Type t = role.GetType();
PropertyInfo p = t.GetProperty(property.Name);
UserRoleModel urm = ((UserRoleModel)p.GetValue(role, null));
// do something with urm
}
}
return true;
}
Though Fabio is right, this does seem strange and potentially based on faulty reasoning.

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

How to iterate through nested properties of an object

I am trying to loop through all properties in an object including nested objects and objects in collections, to check if the property is of DateTime data type. If it is, convert the value to UTC time leaving everything intact including the structure and the value of other properties untouched.
The structure of my classes as follows:
public class Custom1 {
public int Id { get; set; }
public DateTime? ExpiryDate { get; set; }
public string Remark { get; set; }
public Custom2 obj2 { get; set; }
}
public class Custom2 {
public int Id { get; set; }
public string Name { get; set; }
public Custom3 obj3 { get; set; }
}
public class Custom3 {
public int Id { get; set; }
public DateTime DOB { get; set; }
public IEnumerable<Custom1> obj1Collection { get; set; }
}
public static void Main() {
Custom1 obj1 = GetCopyFromRepository<Custom1>();
// this only loops through the properties of Custom1 but doesn't go into properties in Custom2 and Custom3
var myType = typeof(Custom1);
foreach (var property in myType.GetProperties()) {
// ...
}
}
How do I loop through the properties in obj1 and traverse further down obj2 then obj3 then obj1Collection? The function have to be generic enough because the Type passed to the function cannot be determined at design/compile time. Conditional statements to test for Types should be avoided since they might be class Custom100
//avoid conditional statements like this
if (obj is Custom1) {
//do something
} else if (obj is Custom2) {
//do something else
} else if (obj is Custom3) {
//do something else
} else if ......
This is not a complete answer but I would start from here.
var myType = typeof(Custom1);
ReadPropertiesRecursive(myType);
private static void ReadPropertiesRecursive(Type type)
{
foreach (PropertyInfo property in type.GetProperties())
{
if (property.PropertyType == typeof(DateTime) || property.PropertyType == typeof(DateTime?))
{
Console.WriteLine("test");
}
if (property.PropertyType.IsClass)
{
ReadPropertiesRecursive(property.PropertyType);
}
}
}
I think it's far from the original question but ReadPropertiesRecursive above fall in infinite loop on
class Chain { public Chain Next { get; set; } }
I'd rather propose version with accumulation
private static void ReadPropertiesRecursive(Type type, IEnumerable<Type> visited)
{
foreach (PropertyInfo property in type.GetProperties())
{
if (property.PropertyType == typeof(DateTime) || property.PropertyType == typeof(DateTime?))
{
Console.WriteLine("test");
}
else if (property.PropertyType.IsClass && !visited.Contains(property.PropertyType))
{
ReadPropertiesRecursive(property.PropertyType, visited.Union(new Type[] { property.PropertyType }));
}
}
}
Check myType and if IsClass is true, then traverse into it. You'll probably want to make this recursive.
public static class ReadPropertiesExtension
{
public static void ReadPropertiesRecursive<T>(object value, Func<T, T> func)
{
if (value is null)
return;
if (value is IEnumerable)
{
IList collection = (IList)value;
foreach (var val in collection)
ReadPropertiesRecursive(val, func);
return;
}
var type = value.GetType();
foreach (PropertyInfo property in type.GetProperties())
{
if (property.PropertyType == type)
continue;
if (!property.CanRead)
continue;
var val = property.GetValue(value);
if (property.CanWrite && val is T param)
{
property.SetValue(value, func(param));
}
else if (property.PropertyType.IsClass && property.PropertyType != typeof(string))
{
ReadPropertiesRecursive(val, func);
}
}
}
}
Here is a recursive function which also supports Enumerables.
It takes a parameter func and executes this method anticipating that func takes parameter T and returns same param.
You can call it like- ReadPropertiesExtension.ReadPropertiesRecursive<double>(value, func1);
Note: Currently, it doesn't support objects which contain dictionary in it.

Reflection Cast Each Property to List<T>

I am trying to use Reflection to loop through an objects properties and cast each property to a List<T>.
class MyObj
{
public List<A> As { get; set; }
public List<B> Bs { get; set; }
public List<C> Cs { get; set; }
public List<D> Ds { get; set; }
public List<E> Es { get; set; }
}
I have a method that takes an object and I am trying to cast the value of each property to a List<T> but it is not working.
void ProcessObject(object o)
{
foreach (PropertyInfo propertyInfo in this.GetType().GetProperties())
{
Type propertyType = propertyInfo.PropertyType;
Type propertyListType;
if (TryListOfWhat(propertyType, out propertyListType))
{
var propertyValue = (List<propertyListType>)propertyInfo.GetValue(this);
}
}
}
public static bool TryListOfWhat(Type type, out Type innerType)
{
Contract.Requires(type != null);
var interfaceTest = new Func<Type, Type>(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IList<>) ? i.GetGenericArguments().Single() : null);
innerType = interfaceTest(type);
if (innerType != null)
{
return true;
}
foreach (var i in type.GetInterfaces())
{
innerType = interfaceTest(i);
if (innerType != null)
{
return true;
}
}
return false;
}
Now that I know the property is a List<T> and I have the type of T, how do I cast the property object value to List<T>?
I created another question to explain what I am actually trying to accomplish here: https://stackoverflow.com/questions/22975430/serialize-deserialize-inmemorydatabase
You could try using "dynamic" instead of "var" and keep the property as an object, then it should be the same as calling the List members as always without compile time checking.

Categories

Resources