Recursive reflection through object - c#

I have method that walks through a class and checks if 2 objects has the same values.
private bool LoopThroughObjects<T>(T ob1, T ob2)
{
Type type = ob1.GetType();
System.Reflection.PropertyInfo[] props = type.GetProperties();
foreach (var prop in props)
{
object ent1value = prop.GetValue(ob1, new object[] { });
object ent2value = prop.GetValue(ob2, new object[] { });
//If object is in the same namespace it's a nested element
if(IsSameNameSpace(type.Namespace, prop.PropertyType.Namespace))
{
_depth++;
ValidateEntity_AttributesNotSame(ent1value, ent2value);
}
else
ComparerOutput.Add(_conjunction + prop.Name, ent1value.Equals(ent2value));
if (BreakOnUnequally)
if (!ent1value.Equals(ent2value))
return false;
}
return true;
}
A example of an object that I can send:
public class RefelctionTestEntity
{
public int Id { get; set; }
public String Firstname { get; set; }
public String Lastname { get; set; }
public int Age { get; set; }
public RefelctionTestEntity Child { get; set;}
public Vehicle.Bikes Bike { get; set; }
}
I walk recursively through the object so I will also check the Child en the Bike. My question is what's the best methode to check these inner objects?
What I do at this moment is check the namespace of the object against the parent namespace of the inner object:
private bool IsSameNameSpace(String baseNamespace, String propertyNamespace)
{
if (String.IsNullOrEmpty(baseNamespace) || String.IsNullOrEmpty(propertyNamespace))
return true;
if (String.IsNullOrEmpty(baseNamespace) || String.IsNullOrEmpty(propertyNamespace))
return false;
String[] part = propertyNamespace.Split('.');
return part[0].Equals(baseNamespace);
}

//If object is in the same namespace it's a nested element
Using such a rule could quickly become a problem in future.
It would be much more convenient if you flag the recursive item property with an interface and check whether the current property implements that interface. If so just go one level deeper.
Otherwise check if the values of the property you are currently looking at match for both objects.
var interf = Text.GetType().GetInterface("MyInterface",false);
Another approach would be to use an attribute over the item to flag it.
The check for attribute would look like this :
var att = Text.GetType().GetCustomAttributes(typeof(RecursiveTypeAttribute),false);
I wouldn't check if the current property is a reference type since you could easily introduce a reference type property in the future.

Related

Creating an object with a subset of properties and their values from another object

Consider the following class -
public class User
{
[Selected]
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
[Selected]
public int Code { get; set; }
public object GetValue()
{
// need to do something here
}
}
[Selected] here is nothing but a marker attribute. I want GetValue method to return an object which will have the [Selected]-marked properties with corresponding values. That is, in the code below -
private static void Execute()
{
User user = new User
{
Name = "alice",
Email = "alice#liddell.com",
Password = "123456",
Code = 1234
};
var value = user.GetValue();
}
value should be an object with two properties Name and Code which should have the values "alice" and 1234 respectively.
After some searching I tried ExpandoObject (which I never used before) -
public object GetValue()
{
var dictionary = this.GetType().GetProperties().Where(p => p.GetCustomAttribute(typeof(Selected), false) != null).ToDictionary(p => p.Name);
dynamic expando = new ExpandoObject();
foreach (var item in dictionary)
{
object value = item.Value.GetValue(this);
((IDictionary<string, object>)expando).Add(item.Key, value);
}
return expando;
}
But it didn't serve my purpose - the client/consumer of value object somehow couldn't read/access the property values.
Any suggestions?
Edit :
There might be a lot of classes like User and the GetValue method will be called from within a generic method. So, at runtime I have no way to know what type the object is and which properties are marked.
To access the fields by name it is easier to cast the returned object to IDictionary:
var value = (IDictionary<string, object>) user.GetValue();
Console.WriteLine(value["Name"]);
Simplify your method to this:
public Dictionary<string, object> GetValue()
{
var dictionary = this.GetType()
.GetProperties()
.Where(p => p.GetCustomAttribute(typeof(Selected), false) != null)
.ToDictionary(p => p.Name, p => p.GetValue(this));
return dictionary;
}
Use:
var value = user.GetValue(); // value is Dictionary
foreach (var kvp in value)
{
Console.WriteLine(kvp);
}
If you wish POCO, then you can do it like follows
public class User
{
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public int Code { get; set; }
public SelectedUserProperties GetValue()
{
return new SelectedUserProperties
{
Name = Name,
Code = Code
};
}
}
public class SelectedUserProperties
{
public string Name { get; set; }
public int Code { get; set; }
}
It is assumed that the selected properties are known in advance, before compilation.
This makes the marker attribute unnecessary and can be completely removed.
What you did works well, you just have to use the keyword dynamic when you call the method GetValue.
dynamic value = user.GetValue();
If you are using var, value will be of the same type as the return type of your function (i.e object) at compile time. Therefore, if you try to do value.Name your compiler won't allow it because the class Object doesn't have any attribute Name.
dynamic tells your program to do the type checking at runtime.

How to set Object's properties and its value if that Object is a property of Object that is inside of the List using Reflections

I had a similar question before, but this one will need a different solution.
I have object on my Model and object on my service.
I need to set value of Model's object property to a value of properties coming from the service's List<TicketReportPropertyEntity> if both objects' properties are the same.
This is a Model:
public class MyModel{
public ObjectAEntity ObjectAData { get; set; }
public ObjectBEntity ObjectBData { get; set; }
}
ObjectAEntity has a property called "SalesAmount"
This is a service:
public class MyScreenClass
{
public List<TicketReportPropertyEntity> TicketReportPropertyEntities { get; set; }
}
public class TicketReportPropertyEntity
{
public decimal Amount{get;set;}
public ReportPropertyEntity ReportProperty {get;set;}
}
public class ReportPropertyEntity
{
public string ReportGroup { get; set; }
public string PropertyName { get; set; }
}
All the properties, their values and which section(ReportGroup) on the screen they belong to (ObjectAData to the LeftSection and ObjectBData to the RightSection) I'm getting using a reflection from List<TicketReportPropertyEntity> in the following method:
private void SetValues(MyModel m, ObjectAEntity bo, object objectType)
{
string leftSection = "LeftSection";
string rightSection = "RightSection";
m.ObjectAData.SaleAmount = bo.ObjectAData.SaleAmount;
foreach (var ticketReportEntity in mol.TicketReportPropertyEntities)
{
var type = ticketReportEntity.GetType();
PropertyInfo reportProperty = type.GetProperty("ReportProperty");
PropertyInfo reportPropertyName = typeof(ReportPropertyEntity).GetProperty("PropertyName");
PropertyInfo reportPropertyReportGroup = typeof(ReportPropertyEntity).GetProperty("ReportGroup");
PropertyInfo amountProperty = type.GetProperty("Amount");
ReportPropertyEntity reportPropertyValue = (ReportPropertyEntity)reportProperty.GetValue(ticketReportEntity, null);
string reportPropertyNameValue = (string)reportPropertyName.GetValue(reportPropertyValue, null);
decimal value = (decimal)amountProperty.GetValue(ticketReportEntity, null);
//here I need to see if Model's object has the same property as in `ReportProperty` class.
//here I need to find out if the ObjectAEntity has the same property as ReportProperty
if (has)
{
//need to set the value of the Model's `ObjectAEntity` property
}
}
How can I do something like that?
To accomplish this, you would attempt to get the property by the string value stored in the current TicketReportPropertyEntity.ReportPropertyEntity.PropertyName. Since you already have a lot of this setup, it only takes a couple more lines of code.
//here I need to find out if the ObjectAEntity has the same property as ReportProperty
//Attempt to grab the PropertyInfo that you want to set
var objectAEntityReportProperty = bo.GetType().GetProperty(reportPropertyNameValue);
//If it is not null, you have found a match
var has = objectAEntityReportProperty != null;
if (has)
{
//need to set the value of the Model's `ObjectAEntity` property
//Then, set the value
objectAEntityReportProperty.SetValue(bo, ticketReportEntity.Amount);
}

System.Reflection.TargetException on employee.GetType().GetProperty(property.Name)

I have come upon an issue I can't seem to figure out. I'm sure there's a simple explanation to this, but I don't understand why I get a System.Reflection.TargetException: 'Object does not match target type' when I try to get a property from (in this case) the employee object.
employee.GetType().GetProperty(property.Name)
Searching for the error returns many results describing problems with calling the Set/GetValue methods, but I haven't found a solution to this one.
I've set a breakpoint where the exception is thrown and it shows that property.Name is indeed a value - and a real property of the object. I've also tried manually specifying a property I know exists. Still the same.
Any suggestions?
EDIT: Tried the following instead:
Type type = typeof (Employee); //Throws the TargetException
PropertyInfo theProperty = type.GetProperty(property.Name);
And now the same exception is thrown at the first line above instead.
EDIT: Added code and more details about the application I'm building.
Class definition for Employee (to simplify mapping to the JSON data this class "represents", the class/fields are in Norwegian - which is the format/language the data comes in, sorry :-).)
"Ansatt" = Employee. "Ansattnummer" = EmployeeNo.
[JsonObject]
public class Ansatt
{
public int Ansattnummer { get; set; }
public string Fornavn { get; set; }
public string Etternavn { get; set; }
public int Pin { get; set; }
public string Adresse { get; set; }
public int Postnummer { get; set; }
public string Poststed { get; set; }
public int TlfPrivat { get; set; }
public int MobilTlf { get; set; }
public string EpostAdresse { get; set; }
public DateTime Fodt { get; set; }
}
My application retrieves a given dataset from a web service - it could be employees, projects or a few other possible datasets. What data to fetch is determined at runtime - by the user. The user can also specify via URL-query which portions, e.g. columns, of the dataset he/she wants. The program then creates a csv-file with the selected data.
Here's the code I use for this:
if (records != null && records.Count != 0) //records contains the chosen dataset - in this case Employees (Ansatt).
{
if (records.GetType() == typeof (List<Ansatt>))
{
foreach (var model in records as List<Ansatt>)
{
var temp = new Ansatt();
foreach (var property in model.GetType().GetProperties())
{
var currentProperty = model.GetType().GetProperty(property.Name);
if (currentProperty != null)
{
Type type = typeof (Ansatt); //Throws System.Reflection.TargetException: 'Object does not match target type'
PropertyInfo tempProperty = type.GetProperty(property.Name);
tempProperty.SetValue(temp, currentProperty.GetValue(property.Name));
}
}
csv.WriteRecord(temp);
}
}
}
You need to specify the name of the property
PropertyInfo value = employee.GetType().GetProperty("Name");
As MSDN goes, you should use it this way:
class MyClass {
private int myProperty;
// Declare MyProperty.
public int MyProperty {
get {
return myProperty;
}
set {
myProperty = value;
}
}
}
public class MyTypeClass {
public static void Main(string[] args) {
try {
// Get the Type object corresponding to MyClass.
Type myType = typeof(MyClass);
// Get the PropertyInfo object by passing the property name.
PropertyInfo myPropInfo = myType.GetProperty("MyProperty");
// Display the property name.
Console.WriteLine("The {0} property exists in MyClass.", myPropInfo.Name);
// Instantiate MyClass
var myObject = new MyClass()
{
MyProperty = 5
};
// Get value using reflection
Console.WriteLine("My property value for my object is {0}.", myPropInfo.GetValue(myObject));
} catch (NullReferenceException e) {
Console.WriteLine("The property does not exist in MyClass." + e.Message);
}
}
}
For your code, when you want to get the property value of an object instance, you should pass object as reference to PropertyInfo.GetValue(object) function.
Instead of this:
tempProperty.SetValue(temp, currentProperty.GetValue(property.Name));
Do this:
tempProperty.SetValue(temp, currentProperty.GetValue(model));
For you to get the property of object through reflection, make it sure that the property name is public with getter and setter else it will return null.
Ex.
public class Employee
{
public string YouProperty { get; set; }
}
var employee = new Employee();
var result = employee.GetType().GetProperty("YouProperty");
// The result is property info
Kindly read some information here.

Using Reflection and GetValue problems

I have an abstract class that looks like so:
public abstract class PageObjectsBase
{
public abstract string FriendlyName { get; }
public abstract string PageObjectKeyPrefix { get; }
public abstract string CollectionProperty { get; }
}
And a class that derives from PageObjectsBase:
public class PageRatingList : PageObjectsBase
{
public IList<PageRating> PageRatings { get; set; }
public PageRatingList()
{
this.PageRatings = new List<PageRating>();
}
public override string CollectionProperty
{
get
{
var collectionProperty = typeof(PageRatingList).GetProperties().FirstOrDefault(p => p.Name == "PageRatings");
return (collectionProperty != null) ? collectionProperty.Name : string.Empty;
}
}
public override string FriendlyName
{
get
{
return "Page feedback/rating";
}
}
public override string PageObjectKeyPrefix
{
get
{
return "pagerating-";
}
}
}
And a PageRating class which PageRatingList.PageRatings is holding a collection of:
public class PageRating : PageObjectBase
{
public int Score { get; set; }
public string Comment { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
The PageRatingList is being stored in a database (EPiServer's Dynamic Data Store, more specifically using the Page Object Manager). I need to create some reporting functionality and am essentially loading all reports that derive from PageObjectBase. When it comes to returning the data, the code will never know at compile time what type of data it is to load, so I am using Reflection. In my reporting class I have:
//this gives me the right type
var type = Type.GetType("MyNameSpace.PageRatingList", true);
var startPageData = this._contentRepository.Get<PageData>(startPage);
PageObjectManager pageObjectManager = new PageObjectManager(startPageData);
//this loads the instances from the DB
var props = pageObjectManager.LoadAllMetaObjects()
.FirstOrDefault(o => o.StoreName == "Sigma.CitizensAdvice.Web.Business.CustomEntity.PageRatingList");
//this gives me 4 PropertyInfo objects (IList: PageRatings, string : CollectionProperty, string :FriendlyName, string : PageObjectKeyPrefix)
var properties = props.Value.GetType().GetProperties();
I can then iterate through the PropertyInfo objects using:
foreach (var property in properties)
{
//extract property value here
}
The issue I am having is that I cannot figure out how to get the value of each of the propertyinfo objects. In addition, one of those properties is type List and again we wont know the type of T until runtime. So I also need some logic that checks if one of the PropertyInfo objects is of type List and then provides access to each of the properties in the List - the List being of type PageRating.
Can anyone help here? I've not really used reflection in the past so I am winging my way through it, rightly or wrongly!
Many thanks
Al
I may be missunderstanding the problem, but i think you may use something like this:
var props = new PageRatingList(); /*actual instanse of the object, in your case, i think "props.Value" */
var properties = typeof(PageRatingList).GetProperties();
foreach (var property in properties)
{
if (property.PropertyType == typeof(IList<PageRating>))
{
IList<PageRating> list = (IList<PageRating>)property.GetValue(props);
/* do */
}
else
{
object val = property.GetValue(props);
}
}
Hope this helps to find your solution.

How to diff Property Values of two objects using GetType GetValue?

I have the following classes:
public class Person
{
public String FirstName { set; get; }
public String LastName { set; get; }
public Role Role { set; get; }
}
public class Role
{
public String Description { set; get; }
public Double Salary { set; get; }
public Boolean HasBonus { set; get; }
}
I want to be able to automatically extract the property value diferences between Person1 and Person2, example as below:
public static List<String> DiffObjectsProperties(T a, T b)
{
List<String> differences = new List<String>();
foreach (var p in a.GetType().GetProperties())
{
var v1 = p.GetValue(a, null);
var v2 = b.GetType().GetProperty(p.Name).GetValue(b, null);
/* What happens if property type is a class e.g. Role???
* How do we extract property values of Role?
* Need to come up a better way than using .Namespace != "System"
*/
if (!v1.GetType()
.Namespace
.Equals("System", StringComparison.OrdinalIgnoreCase))
continue;
//add values to differences List
}
return differences;
}
How can I extract property values of Role in Person???
public static List<String> DiffObjectsProperties(object a, object b)
{
Type type = a.GetType();
List<String> differences = new List<String>();
foreach (PropertyInfo p in type.GetProperties())
{
object aValue = p.GetValue(a, null);
object bValue = p.GetValue(b, null);
if (p.PropertyType.IsPrimitive || p.PropertyType == typeof(string))
{
if (!aValue.Equals(bValue))
differences.Add(
String.Format("{0}:{1}!={2}",p.Name, aValue, bValue)
);
}
else
differences.AddRange(DiffObjectsProperties(aValue, bValue));
}
return differences;
}
If the properties aren't value types, why not just call DiffObjectProperties recursively on them and append the result to the current list? Presumably, you'd need to iterate through them and prepend the name of the property in dot-notation so that you could see what is different -- or it may be enough to know that if the list is non-empty the current properties differ.
Because I don't know how to tell if:
var v1 = p.GetValue(a, null);
is String FirstName or Role Role. I have been trying to find out how to tell if v1 is a String such as FirstName or a class Role. Therefore I won't know when to recursively pass the object property (Role) back to DiffObjectsProperties to iterate its property values.

Categories