Iterate recursively on properties of an object - c#

I want to get all properties of an object during runtime and save it on a batabase together with its values. I am doing this recursively i.e. whenever a property is also on object, I will call the same method and pass the property as a parameter.
See my code below:
private void SaveProperties(object entity) {
PropertyInfo[] propertyInfos = GetAllProperties(entity);
Array.Sort(propertyInfos,
delegate(PropertyInfo propertyInfo1, PropertyInfo propertyInfo2)
{ return propertyInfo1.Name.CompareTo(propertyInfo2.Name); });
_CurrentType = entity.GetType().Name;
foreach (PropertyInfo propertyInfo in propertyInfos) {
if (propertyInfo.GetValue(entity, null) != null) {
if (propertyInfo.PropertyType.BaseType != typeof(BaseEntity)) {
SaveProperty((BaseEntity)entity, propertyInfo);
}
else {
// **here**
SaveProperties(Activator.CreateInstance(propertyInfo.PropertyType));
}
}
}
}
However, The problem with my current code is I create a new instance for property objects (see emphasis) thus losing all data that was on the original object. How can I recursively iterate on all the properties of an object? Is this possible?
Please help. Thanks in advance.

Use this instead to replace your emphasized line:
SaveProperties (propertyInfo.GetValue (entity, null));
I would also do something like this to make sure GetValue() is applicable and to avoid multiple calls to it:
object v = propertyInfo.CanRead ? propertyInfo.GetValue (entity, null) : null;
if (v != null) {
if (...) {
} else {
SaveProperties (v);
}
}

Related

'System.Boolean' cannot be converted to type 'System.Reflection.RuntimePropertyInfo'

I have a class which is created dynamically .I have another existing class which has data so i am trying to map existing class data to to dynamicaly created class propeties.
But all the fileds in dynamic class are showing their types as System.Reflection.RuntimePropertyInfo.
Can anybody help me to understand why the dynamic class properties are showing as System.Reflection.RuntimePropertyInfo even though we add specified type while creating.
public object FillData<TresultType>(TresultType result)
{
PropertyInfo[] pi = result.GetType().GetProperties();
foreach (var property in pi)
{
var TargetProperty = this.GetType().GetProperty(property.Name);
if (TargetProperty!=null)
{
TargetProperty.SetValue( this, property.GetValue(result, null), null );
}
}
return this;
}
In the above code this object is the one newly created dynamic object.The line causing problem is
TargetProperty.SetValue( this, property.GetValue(result, null), null );
My problem is i cannot convert the existing class property type(here Boolean) to Target property type which is showing as System.Reflection.RuntimePropertyInfo
Here is my function which creates dynamic object
public object GetViewModel<TresultType, TviewModelType>(TresultType result, TviewModelType ViewModel)
{
if (DynamicType.asmBuilder == null)
DynamicType.GenerateAssemblyAndModule();
var finalType = DynamicType.modBuilder.GetType("Beacon11");
TypeBuilder tb = DynamicType.CreateType(DynamicType.modBuilder, ViewModel.GetType().ToString());
tb.SetParent(typeof(ResultViewModelVersionable));
var sourceType = result.GetType();
var targetType = tb.GetType();
foreach (var property in sourceType.GetProperties())
{
var targetProperty = targetType.GetProperty(property.Name);
if (targetProperty == null)
{
DynamicType.CreateProperty(tb, property.Name, property.GetType());
}
}
finalType = tb.CreateType();
var Methods = tb.GetMethods();
Object obj = Activator.CreateInstance(finalType);
return obj;
}
This function creates model which is of type "TviewModelType" and adds the fields in "TresultType" and also data from it.
ResultViewModelVersionable Versionable = new ResultViewModelVersionable();
var objModel=obj.GetViewModel(vModel,Versionable);
Type myType = vModel.GetType();
MethodInfo magicMethod = typ.GetMethod("FillData");
MethodInfo generic = magicMethod.MakeGenericMethod(myType);
object magicValue = generic.Invoke(objModel,new object[]{vModel});
Please let me know if i can follow any different approch.
Regards,
Mohan
If PropertyInfo.PropertyType == typeof(System.Reflection.RuntimePropertyInfo) then your property has type System.Reflection.RuntimePropertyInfo.
You say that you have created this type dynamically (using reflection emit, probably). There is probably a bug in that code.
The error lies HERE:
foreach (var property in sourceType.GetProperties())
{
var targetProperty = targetType.GetProperty(property.Name);
if (targetProperty == null)
{
DynamicType.CreateProperty(tb, property.Name, property.GetType());
}
}
You are passing the CreateProperty method a type of RuntimePropertyInfo. Try THIS:
foreach (var property in sourceType.GetProperties())
{
var targetProperty = targetType.GetProperty(property.Name);
if (targetProperty == null)
{
DynamicType.CreateProperty(tb, property.Name, property.PropertyType);
}
}
Can't test this, but I suspect that should solve the problem.

Reflection Initialize Property Values of an object Instance Using

I have an Object instance. In the Object's Constructor, I would like to allow user to pass in a Dictionary with which to initialize, some, if not all of the properties of the object. Now, instead of using a conditional, what I would like to do is using reflection, reflect over the properties contained in the object instance, and if the property name, maps to a key in the dictionary, update the property value with the corresponding value in the Dictionary.
In working on this, I have the following code, but it does not update the value of my object instance. Would appreciate some help getting this to work please?
public void Initialize()
{
if (Report.GlobalParameters != null)
{
PropertyInfo[] propertyInfos = this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo propertyInfo in propertyInfos)
{
if (Report.GlobalParameters.ContainsKey(propertyInfo.Name))
{
Type type = this.GetType();
PropertyInfo property = type.GetProperty(propertyInfo.Name);
property.SetValue(this, Report.GlobalParameters[propertyInfo.Name], null);
}
}
}
}
First, have you attached a debugger to inspect whether or not you're getting inside the most nested if? And if you're not getting inside the most nested if, can you figure out why by comparing what you expect to be happening to what is actually happening when you inspect in the debugger?
Second, inside the most nested if, you can remove the first two lines, and replace property with propertyInfo in the third line (which will be the only line remaining when you remove the first two). You already have the PropertyInfo with the given name in hand, why are you looking it up again?
Beyond that, it looks like what you have should work. Thus, there error lies elsewhere, meaning you aren't passing in the right values, or something else that you're not telling us about is going on.
Here is a small working example of what you have that should help you:
using System;
using System.Collections.Generic;
class Foo {
public int Bar { get; set; }
public Foo(Dictionary<string, object> values) {
var propertyInfo = this.GetType().GetProperties();
foreach(var property in propertyInfo) {
if(values.ContainsKey(property.Name)) {
property.SetValue(this, values[property.Name], null);
}
}
}
}
class Program {
public static void Main(String[] args) {
Dictionary<string, object> values = new Dictionary<string, object>();
values.Add("Bar", 42);
Foo foo = new Foo(values);
Console.WriteLine(foo.Bar); // expect 42
}
}
Notice that it's exactly your logic and it works. Does this help?
Does it work any better if you switch it up and do?
public void Initialize()
{
if (Report.GlobalParameters != null)
{
foreach (KeyValuePair<string, object> kvp in Report.GlobalParameters)
{
PropertyInfo pi = this.GetType().GetProperty(kvp.Key, BindingFlags.Public | BindingFlags.Instance);
if (pi != null)
{
try
{
pi.SetValue(this, kvp.Value, null);
}
catch (Exception ex)
{
MessageBox.Show(kvp.Key + Environment.NewLine + ex.ToString(), "Error Setting Property Value");
}
}
else
{
MessageBox.Show(kvp.Key, "Property Not Found");
}
}
}
}

Get PropertyInfo of a parameter passed as lambda expression | Compact Framework, C#

Say I have following Asset class:
class Asset
{
public int Id { get; set; }
public string Name { get; set; }
}
Now I want to write a method GetPropertyInfo(a=>a.Name); and this method gives me the PropertyInfo of Asset.Name. I should be able to call this method like:
EDIT Example Method Call
PropertyInfo propInfo = GetPropertyInfo(a=>a.Name);
I have a List<PropertyInfo> so I want to match a given lambda expression with those in my list.
if(Possible on Compact Framework 3.5 && using C#)
How?
else
Please Notify
Thanks.
This can be done under .NETCF 3.5.
private List<Asset> m_list;
private Asset[] GetPropertyInfo(string name) {
var items = m_list.Where(a => a.Name == name);
if (items != null) {
return items.ToArray();
} else {
return null;
}
}
You will, however, need to initialize the m_list and fill it with your data first.
UPDATE:
So, your list is of type PropertyInfo and you want a call to get the type that matches a particular Asset object.
If that is correct, you could simply edit the code above to be as follows:
private List<PropertyInfo> m_list;
private PropertyInfo GetPropertyInfo(Asset a) {
return m_list.FirstOrDefault(x => x.Name == a.Name);
}
I am not sure how you are getting the List<PropertyInfo>, though. I was able to pull a single PropertyInfo object using the code below:
private PropertyInfo GetPropertyInfo() {
var t = Type.GetType("System.Reflection.MemberInfo");
return t.GetProperty("Name");
}
There was nothing useful in this item.

Copy properties between objects using reflection and extesnion method

This is my code where I create a "copy" of one object (Entity) into a custom object.
It copies just properties with the same name in both source and target.
My problem is when an Entity has a navgiaton to another Entity, for this case I added a custom attribute that I add above the property in the custom class.
For example the custom class looks like:
public class CourseModel:BaseDataItemModel
{
public int CourseNumber { get; set; }
public string Name { get; set; }
LecturerModel lecturer;
[PropertySubEntity]
public LecturerModel Lecturer
{
get { return lecturer; }
set { lecturer = value; }
}
public CourseModel()
{
lecturer = new LecturerModel();
}
}
The problem is in targetProp.CopyPropertiesFrom(sourceProp); line, when I try to call extension method again (to copy the nested object) ,because the type is determined on run time, extension method couldn't resolved on compile time.
Maybe I am missing something...
public static void CopyPropertiesFrom(this BaseDataItemModel targetObject, object source)
{
PropertyInfo[] allProporties = source.GetType().GetProperties();
PropertyInfo targetProperty;
foreach (PropertyInfo fromProp in allProporties)
{
targetProperty = targetObject.GetType().GetProperty(fromProp.Name);
if (targetProperty == null) continue;
if (!targetProperty.CanWrite) continue;
//check if property in target class marked with SkipProperty Attribute
if (targetProperty.GetCustomAttributes(typeof(SkipPropertyAttribute), true).Length != 0) continue;
if (targetProperty.GetCustomAttributes(typeof(PropertySubEntity), true).Length != 0)
{
//Type pType = targetProperty.PropertyType;
var targetProp = targetProperty.GetValue(targetObject, null);
var sourceProp = fromProp.GetValue(source, null);
targetProp.CopyPropertiesFrom(sourceProp); // <== PROBLEM HERE
//targetProperty.SetValue(targetObject, sourceEntity, null);
}
else
targetProperty.SetValue(targetObject, fromProp.GetValue(source, null), null);
}
}
You'll have to cast first.
((BaseDataItemModel)targetProp).CopyPropertiesFrom(sourceProp);
Either you need to cast targetProperty to BaseDataItemModel so you can call the extension method on it (edit: as in agent-j's answer), or otherwise you could just forget about that base class. Why does your reflection algorithm need it? It could work on any class, and is directed purely by the attributes on properties.
And if it works on any object, it shouldn't be an extension method.

Using reflection read properties of an object containing array of another object

How can I read the properties of an object that contains an element of array type using reflection in c#. If I have a method called GetMyProperties and I determine that the object is a custom type then how can I read the properties of an array and the values within. IsCustomType is method to determine if the type is custom type or not.
public void GetMyProperties(object obj)
{
foreach (PropertyInfo pinfo in obj.GetType().GetProperties())
{
if (!Helper.IsCustomType(pinfo.PropertyType))
{
string s = pinfo.GetValue(obj, null).ToString();
propArray.Add(s);
}
else
{
object o = pinfo.GetValue(obj, null);
GetMyProperties(o);
}
}
}
The scenario is, I have an object of ArrayClass and ArrayClass has two properties:
-string Id
-DeptArray[] depts
DeptArray is another class with 2 properties:
-string code
-string value
So, this methods gets an object of ArrayClass. I want to read all the properties to top-to-bottom and store name/value pair in a dictionary/list item. I am able to do it for value, custom, enum type. I got stuck with array of objects. Not sure how to do it.
Try this code:
public static void GetMyProperties(object obj)
{
foreach (PropertyInfo pinfo in obj.GetType().GetProperties())
{
var getMethod = pinfo.GetGetMethod();
if (getMethod.ReturnType.IsArray)
{
var arrayObject = getMethod.Invoke(obj, null);
foreach (object element in (Array) arrayObject)
{
foreach (PropertyInfo arrayObjPinfo in element.GetType().GetProperties())
{
Console.WriteLine(arrayObjPinfo.Name + ":" + arrayObjPinfo.GetGetMethod().Invoke(element, null).ToString());
}
}
}
}
}
I've tested this code and it resolves arrays through reflection correctly.
You'll need to retrieve the property value object and then call GetType() on it. Then you can do something like this:
var type = pinfo.GetGetMethod().Invoke(obj, new object[0]).GetType();
if (type.IsArray)
{
Array a = (Array)obj;
foreach (object arrayVal in a)
{
// reflect on arrayVal now
var elementType = arrayVal.GetType();
}
}
FYI -- I pulled this code from a recursive object formatting method (I would use JSON serialization for it now).

Categories

Resources