how to recursively call a generic method analyzing properties - c#

I'm creating a method that will analyze an instance of a class that I have created, checking each of the properties on that class for string types and then checking if those string properties are null or empty.
Code:
public class RootClass
{
public string RootString1 { get; set; }
public string RootString2 { get; set; }
public int RootInt1 { get; set; }
public Level1ChildClass1 RootLevel1ChildClass11 { get; set; }
public Level1ChildClass1 RootLevel1ChildClass12 { get; set; }
public Level1ChildClass2 RootLevel1ChildClass21 { get; set; }
}
public class Level1ChildClass1
{
public string Level1String1 { get; set; }
public string Level1String2 { get; set; }
public int Level1Int1 { get; set; }
}
public class Level1ChildClass2
{
public string Level1String1 { get; set; }
public string Level1String2 { get; set; }
public int Level1Int1 { get; set; }
public Level2ChildClass1 Level1Level2ChildClass11 { get; set; }
public Level2ChildClass1 Level1Level2ChildClass12 { get; set; }
public Level2ChildClass2 Level1Level2ChildClass22 { get; set; }
}
public class Level2ChildClass1
{
public string Level2String1 { get; set; }
public string Level2String2 { get; set; }
public int Level2Int1 { get; set; }
}
public class Level2ChildClass2
{
public string Level2String1 { get; set; }
public string Level2String2 { get; set; }
public int Level2Int1 { get; set; }
}
Not all the properties on the class are strings, some of them are instances of other classes, which have their own properties, which also need to be analyzed the same way. Basically, the method will return true if any of the properties are strings with a value on the RootClass or anywhere on sub-levels of the class (for example, if RootLevel1ChildClass11 has a string property with a value).
Here's what I have so far:
public static bool ObjectHasStringData<T>(this T obj)
{
var properties = typeof(T).GetProperties(BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var property in properties)
{
Type propertyType = property.PropertyType;
if (propertyType == typeof(string))
{
try
{
if (!String.IsNullOrEmpty(property.GetValue(obj, null) as string))
return true;
}
catch (NullReferenceException) { } // we want to ignore NullReferenceExceptions
}
else if (!propertyType.IsValueType)
{
try
{
if (ObjectHasStringData(property.GetValue(obj, null)))
return true;
}
catch (NullReferenceException) { } // we want to ignore NullReferenceExceptions
}
}
return false;
}
this works great on the first layer (so any string within the RootClass), but once I start using it recursively on the if (ObjectHasStringData(property.GetValue(obj, null))) line, the return value of property.GetValue() is object, so when calling the method recursively, T is object.
I can get the Type of the current object, but how do I convert the object returned from property.GetValue() to the actual type of the property?

I'd suggest not making that a generic method, just have it accept any object, and use GetType to get the type (unless it's null). The generics doesn't really seem to add anything of value here.
So, remove the type parameter, use obj.GetType(), and don't recurse if the object is null!
Also, (propertyType)obj) won't work, and if it would it would have no use. Casting is only for type safety and determining (at compile time) how to interact with an object. To System.Reflection it doesn't make any difference.
public static bool ObjectHasStringData( this object obj )
{
if( obj == null )
return false;
var properties = obj.GetType().GetProperties(BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var property in properties)
...
}

Related

How to loop thru a model and print without typing the name of properties

I have a Model that is filled with 20 Properties, for instance such as
public class SensorModel
{
public string Trigger1 { get; set; }
public string PathDoor1 { get; set; }
public string PathDoor2 { get; set; }
public string PathTrigger1 { get; set; }
public string PathTrigger2 { get; set; }
public string PathTrigger3 { get; set; }
public string PathTrigger4 { get; set; }
public string PathTrigger5 { get; set; }
public string PathTrigger6 { get; set; }
public string PathTrigger7 { get; set; }
public string PathTrigger8 { get; set; }
}
After declaring and setting their properties by doing such,
SensorModel sensorsData = new SensorModel();
How can I access sensorsData's properties using a loop?
Because I would like to logs all the data into a txt along with DateTime, I find manually accessing is a waste of time.
Is there any way to automate, for instance, using a loop and accessing it one by one?
You can use reflection to achieve your goal:
var model = new SensorModel() {
PathDoor1 = "Foo",
PathDoor2 = "Foo2",
PathTrigger1 = "Value of PT1",
PathTrigger2 = "Value of PT2",
};
foreach(var value in model.GetTriggerValues()) {
Console.WriteLine(value);
}
public class SensorModel
{
public string Trigger1 { get; set; }
public string PathDoor1 { get; set; }
public string PathDoor2 { get; set; }
public string PathTrigger1 { get; set; }
public string PathTrigger2 { get; set; }
/* ... */
public IEnumerable<string> GetTriggerValues() {
foreach(var prop in this.GetType().GetProperties().Where(x => x.Name.StartsWith("PathTrigger"))) {
yield return (string)prop.GetValue(this, null);
}
}
}
This example filters your properties by name, if you want or need a different result set, amend or remove the where clause.
You can use reflection to achieve this:
var obj = new SensorModel();
// ...
// Get all the properties of your class
var props = typeof(SensorModel).GetProperties();
foreach (var prop in props)
{
// Get the "Get" method and invoke it
var propValue = prop.GetGetMethod()?.Invoke(obj, null);
// Do something with the value
Console.Out.WriteLine("propValue = {0}", propValue);
}

Generate unique string key based on properties of object (cachekey)

I am simply trying to generate a unique cachekey that takes in the object type and property values
GetHashCode returns different results each time so that wont work, so I have to implement a solution but it has to be fast (I can go through the properties and and concat their values to a string but this might be slow and not the best way to go about it)
Nice To Have:
so if 2 different object types have the exact same properties and same values but they are different classes, they should be different cachekeys (chances of this happening are very slim but just in case)
Here is my code
public interface ICachableRequest
{
string GetCacheKey();
}
public class Object1 : ICachableRequest
{
public int IntValue1 { get; set; }
public double DoubleVal1 { get; set; }
public string StringVal1 { get; set; }
public string GetCacheKey()
{
throw new NotImplementedException();
}
}
public class Object2 : ICachableRequest
{
public int SomeIntValue1 { get; set; }
public double SomeOtherDoubleVal1 { get; set; }
public string MoreStringVal1 { get; set; }
public string MoreStringVal2 { get; set; }
public string MoreStringVal3 { get; set; }
public string MoreStringVal4 { get; set; }
public string GetCacheKey()
{
throw new NotImplementedException();
}
}

.net core C# use dynamic property name on EF Core Database first generated model class

I have a user details class
public partial class UserDetails
{
public int? Level { get; set; }
public string Unit { get; set; }
public string Bio { get; set; }
public bool? Gender { get; set; }
public int? Mobile { get; set; }
public string Photo { get; set; }
}
I am writing an update method:
public bool UpdateDetails(string userId, UserProperties updateProperty, string value)
{
switch(updateProperty)
{
case UserProperties.Unit:
details.Unit = value;
break;
case UserProperties.Photo:
details.Photo = value;
break;
default:
throw new Exception("Unknown User Detail property");
}
May I do something like dynamic property in JavaScript?
e.g.
var details = new UserDetails();
details["Unit"] = value;
Update
As of year 2019! How about try to use this new feature?!
DynamicObject
DynamicObject.TrySetMember(SetMemberBinder, Object) Method
I am trying to figure out how to write it.
You can do it via reflection for properties that exist on the object.
C# has a feature called Indexers. You could extend your code like this to allow for the behavior you are expecting.
public partial class UserDetails
{
public int? Level { get; set; }
public string Unit { get; set; }
public string Bio { get; set; }
public bool? Gender { get; set; }
public int? Mobile { get; set; }
public string Photo { get; set; }
// Define the indexer to allow client code to use [] notation.
public object this[string propertyName]
{
get {
PropertyInfo prop = this.GetType().GetProperty(propertyName);
return prop.GetValue(this);
}
set {
PropertyInfo prop = this.GetType().GetProperty(propertyName);
prop.SetValue(this, value);
}
}
}
Other than that, if you don't know the properties at runtime, you can use the dynamic type.
If you don't want to use reflection you can slightly tweak Alens solution to use dictionary to store data.
public class UserDetails
{
private Dictionary<string, object> Items { get; } = new Dictionary<string, object>();
public object this[string propertyName]
{
get => Items.TryGetValue(propertyName, out object obj) ? obj : null;
set => Items[propertyName] = value;
}
public int? Level
{
get => (int?)this["Level"];
set => this["Level"] = value;
}
}
The closest thing would be the ExpandoObject:
https://learn.microsoft.com/en-us/dotnet/api/system.dynamic.expandoobject?view=netframework-4.8
For example:
dynamic sampleObject = new ExpandoObject();
sampleObject.test = "Dynamic Property";
Console.WriteLine(sampleObject.test);

Get all properties from an object including properties of reference members

Example:
public class B
{
[IsSearchable]
[IsEncryptable]
public string bPropA { get; set; }
[IsSearchable]
[IsEncryptable]
public string bPropB { get; set; }
}
class A
{
[IsSearchable]
[IsEncryptable]
public string PropA { get; set; }
[IsSearchable]
[IsEncryptable]
public string PropB { get; set; }
[IsSearchable]
public int PropC { get; set; }
[IsSearchable]
public B PropD { get; set; }
}
So out of the example above I want all 6 properties when sending an object of class A to a function.
So far I've tried:
A a = new A();
a.GetType().GetAllProperties();
But it only returns the 4 properties and not it's members properties.
Looked around but couldn't find any question regarding this matter at least not specifically.
I guess you would need to do something similar to this:
(untested)
List<PropertyInfo> GetPropertiesRecursive(Type type)
{
var properties = new List<PropertyInfo>
foreach(var propertyInfo in type.GetProperties())
{
properties.Add(propertyInfo);
if(!propertyInfo.PropertyType.IsValueType)
{
properties.AddRange(GetPropertiesRecursive(propertyInfo.PropertyType));
}
}
return properties;
}
Note that value types can have properties too.

Compare Properties of classes to identify which have changed

In my web application, I want to notify user when certain things are changed through UI. For example my Project class looks like this
public class Project
{
public string Name { get; set; }
public TaskStatus Status { get; set; }
public string Planner { get; set; }
public DateTime ScheduleStart { get; set; }
public DateTime ScheduleEnd { get; set; }
public double EstimatedCost { get; set; }
public double ActualCost { get; set; }
public string AssignedTo { get; set; }
}
Now I have this information shown up on UI and a particular user having rights to change certain things (e.g. Status, schedule, cost etc.) can change this information. So what I want is that when something is changed by a user, then Emails should be sent to notify Project Manager lets say or anyone interested.
I have all other required code written to send emails and manage rights etc. Now I want to specifically see exactly what things changed for example If only Planner changed, or status changed then email should contain new and old value like TFS generates notifications.
P.S: Above code shows a very simple version of my Project class, actual class has more than 30 attributes. So I was thinking that instead of making comparison of each individual property there should be an easier and generic way that tells me which properties have changed, so that I can notify based on them.
A simple solution based on reflection. Note that it could be optimized, and it doesn't support (at this time) comparing inner collections/objects. The compared object must be POD (Plain Old Data)
public class Project
{
public string Name { get; set; }
public TaskStatus Status { get; set; }
public string Planner { get; set; }
public DateTime ScheduleStart { get; set; }
public DateTime ScheduleEnd { get; set; }
public double EstimatedCost { get; set; }
public double ActualCost { get; set; }
public string AssignedTo { get; set; }
public Project Clone()
{
// If your object has inner collections, or
// references to other objects, you'll have to deep
// clone them ***manually***!!!
return (Project)MemberwiseClone();
}
}
public static class SimpleComparer
{
// Item1: property name, Item2 current, Item3 original
public static List<Tuple<string, object, object>> Differences<T>(T current, T original)
{
var diffs = new List<Tuple<string, object, object>>();
MethodInfo areEqualMethod = typeof(SimpleComparer).GetMethod("AreEqual", BindingFlags.Static | BindingFlags.NonPublic);
foreach (PropertyInfo prop in typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
object x = prop.GetValue(current);
object y = prop.GetValue(original);
bool areEqual = (bool)areEqualMethod.MakeGenericMethod(prop.PropertyType).Invoke(null, new object[] { x, y });
if (!areEqual)
{
diffs.Add(Tuple.Create(prop.Name, x, y));
}
}
return diffs;
}
private static bool AreEqual<T>(T x, T y)
{
return EqualityComparer<T>.Default.Equals(x, y);
}
}
Now, you'll need a Clone() method:
public class Project
{
public string Name { get; set; }
public TaskStatus Status { get; set; }
public string Planner { get; set; }
public DateTime ScheduleStart { get; set; }
public DateTime ScheduleEnd { get; set; }
public double EstimatedCost { get; set; }
public double ActualCost { get; set; }
public string AssignedTo { get; set; }
public Project Clone()
{
// If your object has inner collections, you'll have to deep
// clone them ***manually***!!!
return (Project)MemberwiseClone();
}
}
and then...
var current = new Project();
var original = current.Clone();
current.ActualCost = 10000;
var diffs = SimpleComparer.Differences(current, original);
foreach (var diff in diffs)
{
Console.WriteLine("'{0}' changed from {1} to {2}", diff.Item1, diff.Item3, diff.Item2);
}
I'm assuming that you are referring to property values and not the actual class' properties. To be able to compare which property values have changed, there has to be two versions of the object, say old and updated. I would suggest implementing the IEquatable interface, this comes handy if you have complex objects which in your case a nested class TaskStatus which also have properties of its own that you need to compare. You can also let TaskStatus or other nested classes implement the IEquatable interface such that you don't have to worry about comparing their property values giving you the advantage of just doing a single call to Project's Equals() method. You can have the logic for getting the changes inside the overriden Equals() method.
If you don't want to hardcode each property for comparison, a little reflection would do. :)
public class Project : IEquatable<Project>
{
public string Name { get; set; }
public TaskStatus Status { get; set; }
public string Planner { get; set; }
public DateTime ScheduleStart { get; set; }
public DateTime ScheduleEnd { get; set; }
public double EstimatedCost { get; set; }
public double ActualCost { get; set; }
public string AssignedTo { get; set; }
public bool Equals(Project other)
{
bool flag = true;
if (this.Name != other.Name)
//--Do something
flag = false;
//TaskStatus otherTaskStatus = other.Status;
//flag = other.Status.Equals(otherTaskStatus);//compare nested classes here
return flag;
}
}
public class TaskStatus : IEquatable<TaskStatus>
{
public bool Equals(TaskStatus other)
{
throw new NotImplementedException();
}
}
You can use a class as follows which will store the old and new values of a property each time the property is changed even after updating it on the UI and then the object can be used to retrieve both the values. All you need to do is to create an instance of this class under set method of each property. You can also check whether the value is changed or not before creating the object.
public class PropertyChangingEventArgs : EventArgs
{
public PropertyChangingEventArgs()
{
}
public PropertyChangingEventArgs(string propName, object oldValue, object newValue)
{
PropertyName = propName;
OldValue = oldValue;
NewValue = newValue;
}
public string PropertyName { get; set; }
public object OldValue { get; set; }
public object NewValue { get; set; }
}
On property side, you can do this:
private string family;
public string Family
{
get { return family; }
set
{
if (family != value)
{
PropertyChangingEventArgs e = new PropertyChangingEventArgs("Family", family, value);
OnPropertyChanging(e);
family = value;
OnPropertyChanged("Family");
}
}
}
After updating, you can check which all properties have changed (or you can keep populating a list of changed properties each time a property is changed) and mail the list with old and new values.
Take a look at PropertyChangedEventHandler. I think it should do the trick if I am understanding your question correctly.
https://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.propertychanged(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1

Categories

Resources