Newtonsoft issue with IDynamicMetaObjectProvider implementing classes on deserialization - c#

currently I stumble upon a issue with Newtonsofts Json library, which is a total mystery to me.
I'm having a few classes, which are implementing the IDynamicMetaObjectProvider interface. Serialising several objects to json is no issue, I get exactly the json I expect from every instance of the object.
However deserialisation is giving me a headache. From what I have observed so far it seems like that the library is caching the value for every dynamic property it can't find and keeps this while the application is running. So as example I'm having following three Jsons:
{ "PropA": "1" }
{ "PropA": "2", "PropB": "1" }
{ "PropA": "3", "PropB": "2", "PropC": "1" }
Deserialising this the Jsons in a row will give me following .NET objects:
{ "PropA": "1" }
{ "PropA": "1", "PropB": "1" }
{ "PropA": "1", "PropB": "1", "PropC": "1" }
HOWEVER! If I change the target type from the one implementing IDynamicMetaObjectProvider to Dicitionary or simply dynamic, the deserialised object will have the properties set correctly.
My class is having an index property, setting a breakpoint on the setter, that already the setter is provided with the wrong value (so it is no issue with the implementation of my class).
public abstract class DynamicModelObject : IDynamicMetaObjectProvider //, IPropertyIndexer //, IDictionary<String, Object>
{
[NotMapped]
[JsonIgnore]
internal Dictionary<String, Object> properties = new Dictionary<String, Object>();
[IgnoreProperty]
[JsonIgnore]
public override Object this[String propertyName]
{
get
{
object val;
if (properties.TryGetValue(propertyName, out val)) {
return val;
}
var prop = this.GetType().GetProperty(propertyName);
if (prop != null && prop.CanRead) {
return prop.GetValue(this);
}
return null;
}
set
{
isDearty = true;
var prop = this.GetType().GetProperty(propertyName);
if (prop != null && prop.CanWrite) {
prop.SetValue(this, value);
} else {
properties[propertyName] = value;
}
var val = value as String;
if (value == null || (val != null && String.IsNullOrEmpty(val))) {
properties.Remove(propertyName);
}
}
}
public DynamicMetaObject GetMetaObject(System.Linq.Expressions.Expression parameter)
{
return new DynamicDictionaryPropertyStore<DynamicModelObject>(parameter, this);
}
[IgnoreProperty]
public IEnumerable<String> DynamicPropertyMemberNames
{
get
{
foreach (var key in properties.Keys) {
yield return key;
}
}
}
private List<String> staticProperties = null;
[IgnoreProperty]
private IEnumerable<String> StaticPropertyMemberNames
{
get
{
if (staticProperties == null) {
staticProperties = new List<String>();
foreach (var prop in this.GetType()
.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.GetProperty)) {
if (!Attribute.IsDefined(prop, typeof(IgnorePropertyAttribute)) && !Attribute.IsDefined(prop, typeof(ScriptIgnoreAttribute))) {
staticProperties.Add(prop.Name);
yield return prop.Name;
}
}
} else {
foreach (var prop in staticProperties) {
yield return prop;
}
}
}
}
[IgnoreProperty]
private IEnumerable<String> AllPropertyMemberNames
{
get
{
foreach (var prop in DynamicPropertyMemberNames.Concat(StaticPropertyMemberNames)) {
yield return prop;
}
}
}
private class DynamicDictionaryPropertyStore<T> : DynamicMetaObject where T : DynamicModelObject
{
private T target;
internal DynamicDictionaryPropertyStore(System.Linq.Expressions.Expression parameter, T target)
: base(parameter, BindingRestrictions.Empty, target)
{
this.target = target;
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return target.DynamicPropertyMemberNames;
}
public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value)
{
BindingRestrictions restrictions = BindingRestrictions.GetTypeRestriction(Expression, LimitType);
System.Linq.Expressions.Expression self = System.Linq.Expressions.Expression.Convert(Expression, LimitType);
if (binder == null) return null;
var body = System.Linq.Expressions.Expression.Property(self, "Item", System.Linq.Expressions.Expression.Constant(binder.Name));
var convert = System.Linq.Expressions.Expression.Convert(System.Linq.Expressions.Expression.Constant(value.Value), typeof(object));
var lambda = System.Linq.Expressions.Expression.Assign(body, convert);
return new DynamicMetaObject(lambda, restrictions);
}
public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
{
BindingRestrictions restrictions = BindingRestrictions.GetTypeRestriction(Expression, LimitType);
System.Linq.Expressions.Expression self = System.Linq.Expressions.Expression.Convert(Expression, LimitType);
if (binder == null) return null;
var body = System.Linq.Expressions.Expression.Property(self, "Item", System.Linq.Expressions.Expression.Constant(binder.Name));
return new DynamicMetaObject(body, restrictions);
}
}
}
Whats going on??

Your implementation of DynamicModelObject is not correct. In DynamicDictionaryPropertyStore child class you do the following:
public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) {
BindingRestrictions restrictions = BindingRestrictions.GetTypeRestriction(Expression, LimitType);
System.Linq.Expressions.Expression self = System.Linq.Expressions.Expression.Convert(Expression, LimitType);
if (binder == null) return null;
var body = System.Linq.Expressions.Expression.Property(self, "Item", System.Linq.Expressions.Expression.Constant(binder.Name));
var convert = System.Linq.Expressions.Expression.Convert(System.Linq.Expressions.Expression.Constant(value.Value), typeof(object));
var lambda = System.Linq.Expressions.Expression.Assign(body, convert);
return new DynamicMetaObject(lambda, restrictions);
}
If you will look at the resulting expression you have, you will see (for example for PropA):
Convert($arg0).Item["PropA"] = Convert("1")
So as a setter you return expression which calls your indexer and assigns constant value (1), regardless of what value was actully passed. This expression will be used later for all setters to PropA (cached). Hence your problem: all your setters will ignore passed values and will always assign value with which you called them for a first time. To fix, replace this line:
var convert = System.Linq.Expressions.Expression.Convert(System.Linq.Expressions.Expression.Constant(value.Value), typeof(object));
With this:
var convert = System.Linq.Expressions.Expression.Convert(value.Expression, typeof(object));
Resulting setter expression will be:
Convert($arg0).Item["PropA"] = Convert($arg1)
Note that no costants are there, just arguments. After that your problem will be solved.

Related

How to create a generic method to create an instance of class at n depth level?

I have created a generic method(NewIfNull) to create an instance of class if object is null. But currently I am supplying the property name as hard-coded which I don't want to do. Please help if there is any way to do this.
static void Main(string[] args)
{
RnD rnD = new RnD();
rnD.NewIfNull("A").A.NewIfNull("B").B.NewIfNull("C");
if(rnD.A.B.C != null)
{
}
}
class RnD
{
public A A { get; set; }
}
class A
{
public B B { get; set; }
}
class B
{
public C C { get; set; }
}
class C
{
}
public static class ExtensionClass1
{
public static T NewIfNull<T>(this T obj, string propName)
{
if (obj != null)
{
if (obj.GetType().GetProperty(propName) != null &&
obj.GetType().GetProperty(propName).GetValue(obj) == null)
{
Type type =
obj.GetType().GetProperty(propName).PropertyType;
if (type.IsClass)
{
var getobj = Activator.CreateInstance(type);
obj.GetType().GetProperty(propName).SetValue(obj,
getobj);
}
}
}
return obj;
}
}
I don't want to pass "A", "B" and "C" as hard-coded in below code.
rnD.NewIfNull("A").A.NewIfNull("B").B.NewIfNull("C");
A simple answer without trying to understand what you are actually trying to do is to use nameof:
rnD.NewIfNull(nameof(RnD.A)).A.NewIfNull(nameof(A.B)).B.NewIfNull(nameof(B.C));
You can use expressions to do this.
public static TProperty NewIfNull<TObj, TProperty>(this TObj obj, Expression<Func<TObj, TProperty>> selector)
{
if (!(selector.Body is MemberExpression memberExpression)
|| !(memberExpression.Member is PropertyInfo propertyInfo))
{
throw new ArgumentException("Expected a lambda in the form x => x.Property", nameof(selector));
}
var property = (TProperty)propertyInfo.GetValue(obj);
if (property == null)
{
// We already know that typeof(TProperty).IsClass is true - if it
// wasn't, then 'property' could not have been null above.
property = (TProperty)Activator.CreateInstance(typeof(TProperty));
propertyInfo.SetValue(obj, property);
}
return property;
}
Then you can call it like:
rnD.NewIfNull(x => x.A).NewIfNull(x => x.B).NewIfNull(x => x.C);
You can quite easily modify it to initialise all of your properties at the same time:
public static void NewIfNull<TObj, TProperty>(this TObj obj, Expression<Func<TObj, TProperty>> selector)
{
object subject = obj;
foreach (var member in GetMembers().Reverse())
{
if (!(member.Member is PropertyInfo propertyInfo))
{
throw new ArgumentException("Member was not a property", nameof(selector));
}
var property = propertyInfo.GetValue(subject);
if (property == null)
{
property = Activator.CreateInstance(propertyInfo.PropertyType);
propertyInfo.SetValue(subject, property);
}
subject = property;
}
IEnumerable<MemberExpression> GetMembers()
{
for (var member = GetMember(selector.Body); member != null; member = GetMember(member.Expression))
{
yield return member;
}
}
MemberExpression GetMember(Expression expr)
{
if (expr is ParameterExpression)
{
return null;
}
if (expr is MemberExpression member)
{
return member;
}
throw new ArgumentException("Expected a lambda in the form x => x.A.B.C", nameof(selector));
}
}
And call it like:
rnD.NewIfNull(x => x.A.B.C);

Dynamic dispatch by Type only known at runtime

I keep running across the need for the same pattern in my code, which frequently needs to validate the values of all the properties in an object. The pseudo code looks like this:
bool ValidateMe(object c) {
var properties = GetProperties(c);
foreach (var property in properties) {
var value = property.GetValue(c);
if (!IsValid(value)) {
return false;
}
}
return true;
}
bool IsValid(int value)
{
return value != int.MaxValue;
}
bool IsValid(double value)
{
return value != double.MaxValue;
}
bool IsValid(object value)
{
return value != null;
} // etc.
I want the code to dynamically dispatch the value to the correct method based on the object's Type (which can be found by calling property.PropertType or value.GetType() assuming value is not null).
The only way I have found to make this work is something like this:
interface IValidator {
bool IsValid(object value);
}
class PredicateValidator<T> : IValidator {
private Predicate<T> method;
PredicateValidator(Predicate<T> method) {
this.method = method;
}
bool IsValid(object value) {
return IsValid((T) value);
}
bool IsValid(T value) {
return method.invoke(value);
}
}
var validationsByType = new Dictionary<Type,IValidator>();
validationsByType[typeof(double)]=new PredicateValidator<double>(IsValid);
validationsByType[typeof(double)]=new PredicateValidator<int>(IsValid);
and then the Map enables dispatching the object to correct method by type:
object value = property.GetValue(c);
bool result = validationsByType[c.GetType()].IsValid(value);
Is there native C# (i.e. language feature) for doing this dynamic dispatch by Type at runtime?
The dynamic keyword will do the downcast correctly. So the above code change be changed to:
bool ValidateMe(object c) {
var properties = GetProperties(c);
foreach (var property in properties) {
var value = property.GetValue(c);
if (!IsValid((dynamic) value)) {
return false;
}
}
return true;
}
bool IsValid(int value)
{
return value != int.MaxValue;
}
bool IsValid(double value)
{
return value != double.MaxValue;
}
The .NET Runtime then searches for the most specific method signature to invoke. I had previously thought that dynamic only worked for Duck Typing, but it also works for dynamic dispatch to overloaded methods.
Your approach is good if you want to keep a "nominal reflection execution".
My recommandation is to avoid reflection as "normal" execution code.
It cause the need to work with extremely abstract objects (often System.Object).
You can always use generic and/or reflection to produce on "fire/init time" a delegate adapted to your need without any Type resolution in "nonimal execution" and use the delegate naturally.
a short example to illustrate it
static public Validation
{
//Simply call the delegate (generic method is easier to use than generic class)
static public bool Validate<T>(T value)
{
return Validation<T>.Validate(value);
}
}
Implementation of the delegate
static public Validation<T>
{
static public readony Fun<T, bool> Validate;
static Validation
{
if (typeof(T) == typeof(string))
{
Validation<T>.Validate = new Func<T, bool>(value =>
{
var _string = (string)(object)value;
//Do your test here
return true;
});
}
else if (typeof(T) == typeof(int))
{
Validation<T>.Validate = new Func<T, bool>(value =>
{
var _int32 = (int)(object)value;
//Do your test here
return true;
});
}
//...
else if (typeof(T).IsClass)
{
var validate = typeof(Validation).GetMethod("Validate");
var parameter = Expression.Parameter(typeof(T));
var properties = typeof(T).GetProperties();
if (properties.length < 0)
{
Validation<T>.Validate = new Func<T, bool>(value => true);
}
else
{
var body = Expression.Constant(true);
foreach (var property in properties)
{
body = Expression.Condition(Expression.Call(validate.MakeGenericMethod(property.PropertyType), parameter), body, Expression.Constant(false));
}
Validation<T>.Validate = Expression.Lambda<Func<T, bool>>(body, parameter).Compile();
}
}
}
}

"An object reference is required for the non-static field,method,or property ' RxCard.dataobjects.Pharmacy.Area.Get' "

I have a custom validation attribute that I need to pass in some properties to. However, my problem occurs when applying the attribute itself. I'm learning .net backwards so I tend to get stuck on the "simpler" problems. I already tried making the property a static but that messed up parts of my view. How can I approach this?
Attribute:
public class MinimumPhoneDigits : ValidationAttribute
{
public string[] _properties;
public int _expectedsize;
public MinimumPhoneDigits(int expectedsize, params string[] properties)
{
ErrorMessage = "Not the expected size!";
_properties = properties;
_expectedsize = expectedsize;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (_properties == null || _properties.Length < 1)
{
return new ValidationResult("WOAH! Not the right size.");
}
int totalsize = 0;
foreach (var property in _properties)
{
var propinfo = validationContext.ObjectType.GetProperty(property);
if (propinfo == null)
return new ValidationResult(string.Format("could not find {property}"));
var propvalue = propinfo.GetValue(validationContext.ObjectInstance, null) as string;
if (propvalue == null)
return new ValidationResult(string.Format("wrong property for {property}"));
totalsize += propvalue.Length;
}
if (totalsize != _expectedsize)
return new ValidationResult(ErrorMessage);
return ValidationResult.Success;
}
}
class:
public class Pharmacy
{
[MinimumPhoneDigits(10, Area)]
public string PhoneNumber
{
get
{
return _phoneNumber;
}
set
{
_phoneNumber = value;
}
}
private string _phoneNumber;
public string Area
{
get
{
try
{
return _phoneNumber.Split(new char[] { '(', ')', '-' }, StringSplitOptions.RemoveEmptyEntries)[0].Trim();
}
catch
{
return "";
}
}
}
}
Attributes are design-time. You can't pass values that are only known at runtime like Area
I think you might actually be intending to pass a string, like this
[MinimumPhoneDigits(10, "Area")]

Howto search through Properties of all kinds of types

I have a base class called Part and derived classes like Wire or Connector and many more that inherit from Part.
Now I want to implement a search function that searches all Properties of the derived classes for a string.
If necessary that string should be tried to be converted to the type of the Property. The Properties can also be Lists and should be searched on the first level.
class Part
{
public int Id { get; set; }
public string Name { get; set; }
}
class Wire : Part
{
public NumberWithUnit Diameter { get; set; }
public Weight Weight { get; set; }
}
class Connector : Part
{
public List<Part> ConnectedParts { get; set; }
}
I know how to generally search through the Properties of base types with Reflection like this
private bool SearchProperties<T>(T part, string searchString) where T : Part
{
var props = typeof(T).GetProperties();
foreach (var prop in props)
{
var value = prop.GetValue(part);
if (value is string)
{
if (string.Equals(value, searchString))
return true;
}
else if (value is int)
{
int v;
if (int.TryParse(searchString, out v))
{
if(v == (int) value)
return true;
}
}
}
return false;
}
But that would be a long list of types and I have Properties of Type Weight for instance and many more. Is there some kind of general way to search without casting all types?
Consider going the opposite direction with your conversion. Rather than converting your search string into each possible value, just convert the value into a string:
private bool SearchProperties<T>(T part, string searchString) where T : Part
{
var props = typeof(T).GetProperties();
foreach (var prop in props)
{
var value = prop.GetValue(part);
if (value is IEnumerable)
{
// special handling for collections
}
else if(value != null)
{
string valueString = value.ToString();
if (string.Equals(valueString, searchString))
return true;
}
}
return false;
}
Besides working pretty well for most built-in types, the only thing you have to do to get it to work for Weight, etc. is make sure they implement ToString().
Another solution would be to use TypeDescriptor:
private bool SearchProperties<T>(T part, string searchString) where T : Part
{
var props = typeof(T).GetProperties();
foreach (var prop in props)
{
var value = prop.GetValue(part);
if (value is IEnumerable)
{
// special handling for collections
}
else if(value != null)
{
object searchValue = null;
try
{
searchValue = TypeDescriptor.GetConverter(value).ConvertFromString(searchString);
} catch {}
if (searchValue != null && object.Equals(value, searchValue))
return true;
}
}
return false;
}
TypeDescriptor works well for most built-in types, but requires extra work if you're dealing with custom types.
I think the following should cover the most of the practical scenarios:
public static bool SearchProperties(object target, string searchString)
{
if (target == null) return false;
// Common types
var convertible = target as IConvertible;
if (convertible != null)
{
var typeCode = convertible.GetTypeCode();
if (typeCode == TypeCode.String) return target.ToString() == searchString;
if (typeCode == TypeCode.DBNull) return false;
if (typeCode != TypeCode.Object)
{
try
{
var value = Convert.ChangeType(searchString, typeCode);
return target.Equals(value);
}
catch { return false; }
}
}
if (target is DateTimeOffset)
{
DateTimeOffset value;
return DateTimeOffset.TryParse(searchString, out value) && value == (DateTimeOffset)target;
}
var enumerable = target as IEnumerable;
if (enumerable != null)
{
// Collection
foreach (var item in enumerable)
if (SearchProperties(item, searchString)) return true;
}
else
{
// Complex type
var properties = target.GetType().GetProperties();
foreach (var property in properties)
{
if (property.GetMethod == null || property.GetMethod.GetParameters().Length > 0) continue;
var value = property.GetValue(target);
if (SearchProperties(value, searchString)) return true;
}
}
return false;
}
I will give you one different idea to do it.
You could try something like that:
private bool SearchProperties<T, W>(T part, W searchValue) where T : Part
{
var props = typeof(T).GetProperties();
foreach (var prop in props)
{
if (typeof(W) == prop.PropertyType)
{
var value = prop.GetValue(part, null);
if (searchValue.Equals(value))
return true;
}
}
return false;
}
You need to call the method like this:
private void button12_Click(object sender, EventArgs e)
{
Part p = new Part();
p.Id = 2;
p.Name = "test";
p.bla = new Bla();
SearchProperties<Part, int>(p, 2);
}
And if you need to compare the complex properties (Weight, ...) by a different way from GetHashCode you could override the method Equals or the == operator.
class Weight
{
public int Id { get; set; }
public override bool Equals(object obj)
{
return Id == ((Weight)obj).Id;
}
}

Using reflection in C# to get properties of a nested object

Given the following objects:
public class Customer {
public String Name { get; set; }
public String Address { get; set; }
}
public class Invoice {
public String ID { get; set; }
public DateTime Date { get; set; }
public Customer BillTo { get; set; }
}
I'd like to use reflection to go through the Invoice to get the Name property of a Customer. Here's what I'm after, assuming this code would work:
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo.Address");
Object val = info.GetValue(inv, null);
Of course, this fails since "BillTo.Address" is not a valid property of the Invoice class.
So, I tried writing a method to split the string into pieces on the period, and walk the objects looking for the final value I was interested in. It works okay, but I'm not entirely comfortable with it:
public Object GetPropValue(String name, Object obj) {
foreach (String part in name.Split('.')) {
if (obj == null) { return null; }
Type type = obj.GetType();
PropertyInfo info = type.GetProperty(part);
if (info == null) { return null; }
obj = info.GetValue(obj, null);
}
return obj;
}
Any ideas on how to improve this method, or a better way to solve this problem?
EDIT after posting, I saw a few related posts... There doesn't seem to be an answer that specifically addresses this question, however. Also, I'd still like the feedback on my implementation.
I use following method to get the values from (nested classes) properties like
"Property"
"Address.Street"
"Address.Country.Name"
public static object GetPropertyValue(object src, string propName)
{
if (src == null) throw new ArgumentException("Value cannot be null.", "src");
if (propName == null) throw new ArgumentException("Value cannot be null.", "propName");
if(propName.Contains("."))//complex type nested
{
var temp = propName.Split(new char[] { '.' }, 2);
return GetPropertyValue(GetPropertyValue(src, temp[0]), temp[1]);
}
else
{
var prop = src.GetType().GetProperty(propName);
return prop != null ? prop.GetValue(src, null) : null;
}
}
Here is the Fiddle: https://dotnetfiddle.net/PvKRH0
I know I'm a bit late to the party, and as others said, your implementation is fine
...for simple use cases.
However, I've developed a library that solves exactly that use case, Pather.CSharp.
It is also available as Nuget Package.
Its main class is Resolver with its Resolve method.
You pass it an object and the property path, and it will return the desired value.
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice
var resolver = new Resolver();
object result = resolver.Resolve(inv, "BillTo.Address");
But it can also resolve more complex property paths, including array and dictionary access.
So, for example, if your Customer had multiple addresses
public class Customer {
public String Name { get; set; }
public IEnumerable<String> Addresses { get; set; }
}
you could access the second one using Addresses[1].
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice
var resolver = new Resolver();
object result = resolver.Resolve(inv, "BillTo.Addresses[1]");
I actually think your logic is fine. Personally, I would probably change it around so you pass the object as the first parameter (which is more inline with PropertyInfo.GetValue, so less surprising).
I also would probably call it something more like GetNestedPropertyValue, to make it obvious that it searches down the property stack.
You have to access the ACTUAL object that you need to use reflection on. Here is what I mean:
Instead of this:
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo.Address");
Object val = info.GetValue(inv, null);
Do this (edited based on comment):
Invoice inv = GetDesiredInvoice(); // magic method to get an invoice
PropertyInfo info = inv.GetType().GetProperty("BillTo");
Customer cust = (Customer)info.GetValue(inv, null);
PropertyInfo info2 = cust.GetType().GetProperty("Address");
Object val = info2.GetValue(cust, null);
Look at this post for more information:
Using reflection to set a property of a property of an object
In hopes of not sounding too late to the party, I would like to add my solution:
Definitely use recursion in this situation
public static Object GetPropValue(String name, object obj, Type type)
{
var parts = name.Split('.').ToList();
var currentPart = parts[0];
PropertyInfo info = type.GetProperty(currentPart);
if (info == null) { return null; }
if (name.IndexOf(".") > -1)
{
parts.Remove(currentPart);
return GetPropValue(String.Join(".", parts), info.GetValue(obj, null), info.PropertyType);
} else
{
return info.GetValue(obj, null).ToString();
}
}
You don't explain the source of your "discomfort," but your code basically looks sound to me.
The only thing I'd question is the error handling. You return null if the code tries to traverse through a null reference or if the property name doesn't exist. This hides errors: it's hard to know whether it returned null because there's no BillTo customer, or because you misspelled it "BilTo.Address"... or because there is a BillTo customer, and its Address is null! I'd let the method crash and burn in these cases -- just let the exception escape (or maybe wrap it in a friendlier one).
Here is another implementation that will skip a nested property if it is an enumerator and continue deeper. Properties of type string are not affected by the Enumeration Check.
public static class ReflectionMethods
{
public static bool IsNonStringEnumerable(this PropertyInfo pi)
{
return pi != null && pi.PropertyType.IsNonStringEnumerable();
}
public static bool IsNonStringEnumerable(this object instance)
{
return instance != null && instance.GetType().IsNonStringEnumerable();
}
public static bool IsNonStringEnumerable(this Type type)
{
if (type == null || type == typeof(string))
return false;
return typeof(IEnumerable).IsAssignableFrom(type);
}
public static Object GetPropValue(String name, Object obj)
{
foreach (String part in name.Split('.'))
{
if (obj == null) { return null; }
if (obj.IsNonStringEnumerable())
{
var toEnumerable = (IEnumerable)obj;
var iterator = toEnumerable.GetEnumerator();
if (!iterator.MoveNext())
{
return null;
}
obj = iterator.Current;
}
Type type = obj.GetType();
PropertyInfo info = type.GetProperty(part);
if (info == null) { return null; }
obj = info.GetValue(obj, null);
}
return obj;
}
}
based on this question and on
How to know if a PropertyInfo is a collection
by Berryl
I use this in a MVC project to dynamically Order my data by simply passing the Property to sort by
Example:
result = result.OrderBy((s) =>
{
return ReflectionMethods.GetPropValue("BookingItems.EventId", s);
}).ToList();
where BookingItems is a list of objects.
> Get Nest properties e.g., Developer.Project.Name
private static System.Reflection.PropertyInfo GetProperty(object t, string PropertName)
{
if (t.GetType().GetProperties().Count(p => p.Name == PropertName.Split('.')[0]) == 0)
throw new ArgumentNullException(string.Format("Property {0}, is not exists in object {1}", PropertName, t.ToString()));
if (PropertName.Split('.').Length == 1)
return t.GetType().GetProperty(PropertName);
else
return GetProperty(t.GetType().GetProperty(PropertName.Split('.')[0]).GetValue(t, null), PropertName.Split('.')[1]);
}
if (info == null) { /* throw exception instead*/ }
I would actually throw an exception if they request a property that doesn't exist. The way you have it coded, if I call GetPropValue and it returns null, I don't know if that means the property didn't exist, or the property did exist but it's value was null.
public static string GetObjectPropertyValue(object obj, string propertyName)
{
bool propertyHasDot = propertyName.IndexOf(".") > -1;
string firstPartBeforeDot;
string nextParts = "";
if (!propertyHasDot)
firstPartBeforeDot = propertyName.ToLower();
else
{
firstPartBeforeDot = propertyName.Substring(0, propertyName.IndexOf(".")).ToLower();
nextParts = propertyName.Substring(propertyName.IndexOf(".") + 1);
}
foreach (var property in obj.GetType().GetProperties())
if (property.Name.ToLower() == firstPartBeforeDot)
if (!propertyHasDot)
if (property.GetValue(obj, null) != null)
return property.GetValue(obj, null).ToString();
else
return DefaultValue(property.GetValue(obj, null), propertyName).ToString();
else
return GetObjectPropertyValue(property.GetValue(obj, null), nextParts);
throw new Exception("Property '" + propertyName.ToString() + "' not found in object '" + obj.ToString() + "'");
}
I wanted to share my solution although it may be too late. This solution is primarily to check if the nested property exists. But it can be easily tweaked to return the property value if needed.
private static PropertyInfo _GetPropertyInfo(Type type, string propertyName)
{
//***
//*** Check if the property name is a complex nested type
//***
if (propertyName.Contains("."))
{
//***
//*** Get the first property name of the complex type
//***
var tempPropertyName = propertyName.Split(".", 2);
//***
//*** Check if the property exists in the type
//***
var prop = _GetPropertyInfo(type, tempPropertyName[0]);
if (prop != null)
{
//***
//*** Drill down to check if the nested property exists in the complex type
//***
return _GetPropertyInfo(prop.PropertyType, tempPropertyName[1]);
}
else
{
return null;
}
}
else
{
return type.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
}
}
I had to refer to few posts to come up with this solution. I think this will work for multiple nested property types.
My internet connection was down when I need to solve the same problem, so I had to 're-invent the wheel':
static object GetPropertyValue(Object fromObject, string propertyName)
{
Type objectType = fromObject.GetType();
PropertyInfo propInfo = objectType.GetProperty(propertyName);
if (propInfo == null && propertyName.Contains('.'))
{
string firstProp = propertyName.Substring(0, propertyName.IndexOf('.'));
propInfo = objectType.GetProperty(firstProp);
if (propInfo == null)//property name is invalid
{
throw new ArgumentException(String.Format("Property {0} is not a valid property of {1}.", firstProp, fromObject.GetType().ToString()));
}
return GetPropertyValue(propInfo.GetValue(fromObject, null), propertyName.Substring(propertyName.IndexOf('.') + 1));
}
else
{
return propInfo.GetValue(fromObject, null);
}
}
Pretty sure this solves the problem for any string you use for property name, regardless of extent of nesting, as long as everything's a property.
Based on the original code from #jheddings, I have created a extension method version with generic type and verifications:
public static T GetPropertyValue<T>(this object sourceObject, string propertyName)
{
if (sourceObject == null) throw new ArgumentNullException(nameof(sourceObject));
if (string.IsNullOrWhiteSpace(propertyName)) throw new ArgumentException(nameof(propertyName));
foreach (string currentPropertyName in propertyName.Split('.'))
{
if (string.IsNullOrWhiteSpace(currentPropertyName)) throw new InvalidOperationException($"Invalid property '{propertyName}'");
PropertyInfo propertyInfo = sourceObject.GetType().GetProperty(currentPropertyName);
if (propertyInfo == null) throw new InvalidOperationException($"Property '{currentPropertyName}' not found");
sourceObject = propertyInfo.GetValue(sourceObject);
}
return sourceObject is T result ? result : default;
}
I wrote a method that received one object type as the argument from the input and returns dictionary<string,string>
public static Dictionary<string, string> GetProperties(Type placeHolderType)
{
var result = new Dictionary<string, string>();
var properties = placeHolderType.GetProperties();
foreach (var propertyInfo in properties)
{
string name = propertyInfo.Name;
string description = GetDescriptionTitle(propertyInfo);
if (IsNonString(propertyInfo.PropertyType))
{
var list = GetProperties(propertyInfo.PropertyType);
foreach (var item in list)
{
result.Add($"{propertyInfo.PropertyType.Name}_{item.Key}", item.Value);
}
}
else
{
result.Add(name, description);
}
}
return result;
}
public static bool IsNonString(Type type)
{
if (type == null || type == typeof(string))
return false;
return typeof(IPlaceHolder).IsAssignableFrom(type);
}
private static string GetDescriptionTitle(MemberInfo memberInfo)
{
if (Attribute.GetCustomAttribute(memberInfo, typeof(DescriptionAttribute)) is DescriptionAttribute descriptionAttribute)
{
return descriptionAttribute.Description;
}
return memberInfo.Name;
}
public static object GetPropertyValue(object src, string propName)
{
if (src == null) throw new ArgumentException("Value cannot be null.", "src");
if (propName == null) throw new ArgumentException("Value cannot be null.", "propName");
var prop = src.GetType().GetProperty(propName);
if (prop != null)
{
return prop.GetValue(src, null);
}
else
{
var props = src.GetType().GetProperties();
foreach (var property in props)
{
var propInfo = src.GetType().GetProperty(property.Name);
if (propInfo != null)
{
var propVal = propInfo.GetValue(src, null);
if (src.GetType().GetProperty(property.Name).PropertyType.IsClass)
{
return GetPropertyValue(propVal, propName);
}
return propVal;
}
}
return null;
}
usage: calling part
var emp = new Employee() { Person = new Person() { FirstName = "Ashwani" } };
var val = GetPropertyValue(emp, "FirstName");
above can search the property value at any level
Try inv.GetType().GetProperty("BillTo+Address");

Categories