How to get specific data from custom attribute on multiple properties - c#

I'm new to attributes so what I'm trying to achieve might not be possible. From my understanding, attributes are used to bind data at compile time to specific methods, properties, classes, etc.
My attribute is very simple and defined as follows:
public class NowThatsAnAttribute : Attribute
{
public string HeresMyString { get; set; }
}
I have two properties that are using the same custom attribute like so:
public class MyClass
{
[NowThatsAnAttribute(HeresMyString = "A" )]
public string ThingyA
{
get;
set;
}
[NowThatsAnAttribute(HeresMyString = "B" )]
public string ThingyB
{
get;
set;
}
}
That is all working fine and dandy, but I am trying to access the attributes of these properties from another method and set certain data based on the attribute's value.
Here is what I am doing right now:
private void MyMethod()
{
string something = "";
var props = typeof (MyClass).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(NowThatsAnAttribute)));
//this is where things get messy
//here's sudo code of what I want to do
//I KNOW THIS IS NOT VALID SYNTAX
foreach(prop in props)
{
//my first idea was this
if(prop.attribute(NowThatsAnAttribute).HeresMyString == "A")
{
something = "A";
}
else if(prop.attribute(NowThatsAnAttribute).HeresMyString == "B")
{
something = "B";
}
//my second idea was this
if(prop.Name == "ThingyA")
{
something = "A";
}
else if(prop.Name == "ThingyB")
{
something = "B";
}
}
//do stuff with something
}
The problem is that something is always being set to "B" which I know is due to looping through the properties. I'm just unsure how to access the value associated with the attribute of a specific property. I have a feeling there's a painfully obvious way of doing this that I'm just not seeing.
I've tried using the StackFrame but ended in near the same spot I'm at now.
NOTE: Using .Net 4.0.
NOTE2: MyMethod is part of an interface which I am unable to edit so I can't pass in any parameters or anything.
Any help is appreciated.

if you pass a name of specific property, you will be able to take correct attribute value:
private string MyMethod(string propName)
{
PropertyInfo pi = typeof (MyClass).GetProperty(propName);
if (pi == null)
return null;
var a = (NowThatsAnAttribute)pi.GetCustomAttribute(typeof(NowThatsAnAttribute));
if (a!=null)
return a.HeresMyString;
return null;
}
MyMethod("ThingyA") returns A

Reflection also gives you the names of the properties.
Type t = typeof(MyClass);
PropertyInfo[] props = t.GetProperties();
foreach (PropertyInfo p in props) {
NowThatsAnAttribute attr = p.GetCustomAttributes(false)
.OfType<NowThatsAnAttribute>()
.FirstOrDefault();
if (attr != null) {
string propName = p.Name;
string attrValue = attr.HeresMyString;
switch (propName) {
case "ThingyA":
//TODO: Do something for ThingyA
break;
case "ThingyB":
//TODO: Do something for ThingyB
break;
default:
break;
}
}
}
You could also add the info to a dictionary:
var dict = new Dictionary<string, string();
...
if (attr != null) {
string propName = p.Name;
string attrValue = attr.HeresMyString;
dict.Add(propName, attrValue);
}
...
Getting value of some prop
string value = dict["ThingyA"];

You could do a generic method like this:
static object GetAttributeValue<T, A>(string attribName, string propName = "") where A : Attribute
{
object[] attribs = null;
if (string.IsNullOrEmpty(propName))
attribs = typeof(T).GetCustomAttributes(true);
else
{
PropertyInfo pi = typeof(T).GetProperty(propName);
if (pi == null)
return null;
attribs = pi.GetCustomAttributes(true);
}
A a = null;
foreach (object attrib in attribs)
{
if (attrib is A)
{
a = attrib as A;
break;
}
}
if (a != null)
{
var prop = a.GetType().GetProperty(attribName);
return prop.GetValue(a, null);
}
return null;
}
You would then call it like this:
object value = GetPropertyAttributeValue<MyClass,NowThatsAnAttribute>("HeresMyString", "ThingyA");
So your "MyMethod" could look like this:
private void MyMethod()
{
string something = "";
var props = typeof (MyClass).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(NowThatsAnAttribute)));
//this is where things get messy
//here's sudo code of what I want to do
//I KNOW THIS IS NOT VALID SYNTAX
foreach(prop in props)
{
string attribVal = object value = GetPropertyAttributeValue<MyClass,NowThatsAnAttribute>("HeresMyString", prop.Name);
//my first idea was this
if(attribVal == "A")
{
something = "A";
}
else if(attribVal == "B")
{
something = "B";
}
}
//do stuff with something
}
This would allow you to reuse the same method to get attribute values for different attributes on different properties(or attributes directly on the class by skipping the second parameter) from different classes.

Related

Document Update Using Driver Ignoring _id

I'd like to be able to update a key/value pair within a Document in a Collection without regard to the _id. This SO response appears to provide a way to do what I want, but it requires a focus on the _id key.
Essentially, what can I do to get my code, as shown below, to work without adding the [BsonIgnoreExtraElements] attribute to the class?
Based on the code which I'll show below, I think I'm close, and I do have a possible workaround, using the attribute, which I'll also show. However, I'd like to do this without decorating the classes with any attributes if possible.
Why no _id? Personally, I find the _id field just gets in the way. It's not part of any of my classes, and MongoDB isn't relational, so I simply don't use it. I freely admit that I may be completely missing the intent and idea behind the use of the _id field, but I simply don't see a need for it in MongoDB. Nevertheless, if possible, I'd like to focus on working without a focus on the _id field.
With that said, I have methods for retrieving a single document or an entire collection along with inserting a single document or entire collection, generically, without regard to the _id field.
I found that the key to ignoring the _id in a "select" is to use a Projection. I'm not all that familiar with the driver, but here are the lines of code that do the magic:
public const string ELEMENT_ID = "_id";
ProjectionDefinition<T> projection = Builders<T>.Projection.Exclude(ELEMENT_ID);
IFindFluent<T, T> found = mongoCollection.Find(bsonDocument).Project<T>(projection);
For background info, here is a retrieval method, ignoring _id:
public T GetSingle<T>(string property, string value) where T : class, new()
{
T tObject = null;
try
{
if (MongoContext.MongoClient != null && MongoContext.MongoDatabase != null)
{
string className = typeof(T).ToString();
int lastPeriod = className.LastIndexOf('.');
int length = className.Length - lastPeriod;
className = className.Substring(lastPeriod + 1, length - 1);
if (!string.IsNullOrEmpty(className))
{
IMongoCollection<T> mongoCollection = MongoContext.MongoDatabase.GetCollection<T>(className);
if (mongoCollection != null)
{
BsonDocument bsonDocument = new BsonDocument();
ProjectionDefinition<T> projection = Builders<T>.Projection.Exclude(ELEMENT_ID);
PropertyInfo[] propertyInfo = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
if (propertyInfo != null && propertyInfo.Length > 0)
{
IEnumerable<PropertyInfo> piExisting = propertyInfo.Where(pi => pi.Name.Equals(property, StringComparison.CurrentCultureIgnoreCase));
if (piExisting != null && piExisting.Any())
{
BsonValue bsonValue = BsonValue.Create(value);
BsonElement bsonElement = new BsonElement(property, bsonValue);
if (bsonElement != null)
{
bsonDocument.Add(bsonElement);
}
IFindFluent<T, T> found = mongoCollection.Find(bsonDocument).Project<T>(projection);
if (found != null)
{
tObject = found.FirstOrDefault<T>();
}
}
}
}
}
}
}
catch (Exception ex)
{
Logger.WriteToLog(Logger.LoggerMessage(ex));
}
return tObject;
}
Similar to other another SO response, I've tried to use FindOneAndUpdate, but I receive the following error:
Element '_id' does not match any field or property of class
ClassThingy.Suffix.
If I could apply the Projection to the FindOneAndUpdate somehow, I think that might resolve my issue, but I'm not able to find a way to do that application.
Here is my code:
public T UpdateSingle<T>(T item, string property, object originalValue, object newValue) where T : class, new()
{
string className = string.Empty;
T updatedDocument = null;
try
{
if (MongoContext.MongoClient != null && MongoContext.MongoDatabase != null)
{
className = ClassUtility.GetClassNameFromObject<T>(item);
if (!string.IsNullOrEmpty(className))
{
IMongoCollection<T> mongoCollection = MongoContext.MongoDatabase.GetCollection<T>(className);
if (mongoCollection != null)
{
BsonDocument bsonDocument = new BsonDocument();
ProjectionDefinition<T> projection = Builders<T>.Projection.Exclude(ELEMENT_ID);
PropertyInfo[] propertyInfo = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
if (propertyInfo != null && propertyInfo.Length > 0)
{
IEnumerable<PropertyInfo> piExisting = propertyInfo.Where(pi => pi.Name.Equals(property, StringComparison.CurrentCultureIgnoreCase));
if (piExisting != null && piExisting.Any())
{
BsonValue bsonValue = BsonValue.Create(originalValue);
BsonElement bsonElement = new BsonElement(property, bsonValue);
if (bsonElement != null)
{
bsonDocument.Add(bsonElement);
}
IFindFluent<T, T> found = mongoCollection.Find(bsonDocument).Project<T>(projection);
if (found != null)
{
FilterDefinition<T> filterDefinition = Builders<T>.Filter.Eq(property, originalValue);
UpdateDefinition<T> updateDefinition = Builders<T>.Update.Set(property, newValue);
updatedDocument = mongoCollection.FindOneAndUpdate<T>(filterDefinition, updateDefinition);
}
}
}
}
}
}
}
catch (Exception ex)
{
Logger.WriteToLog(Logger.LoggerMessage(ex));
}
return updatedDocument;
}
Interestingly enough, while the FindOneAndUpdate method actually appears to succeed and the "Suffix" collection does, in fact, get modified. Also, the return doesn't contain the modification. Instead, it's the "original" (when I use the workaround as shown below).
More Info:
Suffix Class:
public class Suffix
{
public string Code { get; set; }
public string Description { get; set; }
}
Suffix suffix = new Suffix();
MongoRepository.MongoRepository mongoRepository = new MongoRepository.MongoRepository("MyDataBase");
mongoRepository.UpdateSingle<Suffix>(suffix, "Description", "Jr", "Junior");
Workaround:
[BsonIgnoreExtraElements]
public class Suffix
{
public string Code { get; set; }
public string Description { get; set; }
}
But, much rather not use the attribute if at all possible.
One thing you're missing here is the third parameter of .FindOneAndUpdate() method which is of type FindOneAndUpdateOptions<T,T>. That's the place where you can define if you want to get document After or Before the modification. Before is the default value Moreover you can specify the projection and exclude _id property. Try:
FilterDefinition<T> filterDefinition = Builders<T>.Filter.Eq(property, originalValue);
UpdateDefinition<T> updateDefinition = Builders<T>.Update.Set(property, newValue);
ProjectionDefinition<T, T> projection = Builders<T>.Projection.Exclude("_id");
var options = new FindOneAndUpdateOptions<T>()
{
Projection = projection,
ReturnDocument = ReturnDocument.After
};
updatedDocument = mongoCollection.FindOneAndUpdate<T>(filterDefinition, updateDefinition, options);

How to assign values to the nested objects dynamically?

I'm working on a project and I need to be able to do the following:
Say, We have Aa class that is configuration class:
public class Aa
{
public Bb BbName { set; get; }
public string Dd { set; get; }
}
public class Bb
{
public string bb { set; get; }
public string cc { set; get; }
}
I want to fetch variables from environment in order to assign them to the respective properties. Here is what I mean: as a simulation of Environment.GetEnvironmentVariable, let's use Dictionary<string, string>.
Setup will look like this:
var dic = new Dictionary<string, string>();
dic.Add("Dd", "236.154");
dic.Add("BbName.bb", "value_for_Bb.bb"); // as you can see, I'm using nested object's full
dic.Add("BbName.cc", "value_for_Bb.cc"); // path, in order to be able to identify it
Then I wrote a method, which looks like this:
private static void ReadPropertiesRecursive<T>(T obj, Type type, List<string> prefixes)
{
foreach (PropertyInfo property in type.GetProperties())
{
if (property.PropertyType.GetTypeInfo().IsClass && property.PropertyType != typeof(string))
{
prefixes.Add(property.Name);
ReadPropertiesRecursive(obj, property.PropertyType, prefixes);
prefixes.Remove(property.Name);
}
else
{
var propertyFullName = prefixes != null && prefixes.Count > 0 ? $"{prefixes.Aggregate((i, j) => i + "." + j)}.{property.Name}" : property.Name;
property.SetValue(obj, dic[propertyFullName]);
Console.WriteLine(propertyFullName); // just for debugging
}
}
}
So, using recursion, I'm able to reach all nested objects of the given Type and the only thing that is left is assigning corresponding values to them. I'm trying to do that on property.SetValue(obj, dic[propertyFullName]);, but it throws an error, and it makes sense: obj is a "root" object, not the one that I want to assign values to, directly. So, I probably need to pass here not obj, but rather an instance of property, which is obj's property. Is that correct? Will that work? If yes, how should I do that?
That did the trick:
private static void ReadPropertiesRecursive<Tt>(Tt obj, Type type, List<string> prefixes)
{
foreach (PropertyInfo property in type.GetProperties())
{
if (property.PropertyType.GetTypeInfo().IsClass && property.PropertyType != typeof(string))
{
prefixes.Add(property.Name);
var val = property.GetValue(obj);
if (val == null)
val = Activator.CreateInstance(property.PropertyType);
property.SetValue(obj, val);
ReadPropertiesRecursive(val, property.PropertyType, prefixes);
prefixes.Remove(property.Name);
}
else
{
var propertyFullName = prefixes != null && prefixes.Count > 0 ? $"{prefixes.Aggregate((i, j) => i + "." + j)}.{property.Name}" : property.Name;
property.SetValue(obj, dic[propertyFullName]);
Console.WriteLine(propertyFullName);
}
}
}

How can I get the value of properties in nested classes using Reflection? [duplicate]

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

Get Attribute info with generics

actually, I can make a relation between a table field and a variable by doing this inside my OE:
public class MyOE
{
[Column("AGE_FIELD")]
public int ageField { get; set; }
}
My OE class just need to use this other class:
[AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = true)]
public class ColumnAtt : Attribute
{
private string name;
public string Name
{
get { return name; }
}
public ColumnAtt (string name)
{
this.name = name;
}
}
Well, using the code above, Im doing a generic method that I will need to get the "Column" value. How I could do that?
Here is my method:
public void CompareTwoObjectsAndSaveChanges<TObjectType>(TObjectType objectA, TObjectType objectB )
{
if(objectA.GetType() == objectB.GetType())
{
foreach (var prop in objectA.GetType().GetProperties())
{
if(prop.GetValue(objectA, null) != prop.GetValue(objectB, null))
{
string colvalue = "";//Here I need to get the Column value of the attribute.
string nameOfPropertyThatChanges = prop.Name;
string objectAValue = prop.GetValue(objectA, null).ToString();
string objectBValue = prop.GetValue(objectB, null).ToString();
}
}
}
}
Use reflection:
private static void Main(string[] args)
{
MyOE zz = new MyOE { ageField = 45 };
foreach (PropertyInfo property in zz.GetType().GetProperties())
{
// Gets the first attribute of type ColumnAttribute for the property
// As you defined AllowMultiple as true, you should loop through all attributes instead.
var attribute = property.GetCustomAttributes(false).OfType<ColumnAttribute>().FirstOrDefault();
if (attribute != null)
{
Console.WriteLine(attribute.Name); // Prints AGE_FIELD
}
}
Console.ReadKey();
}
You need to use reflection to get the attributes applied to your object. If you know the attribute is always going to be ColumnAtt, then you can do something like this to get the value:
public void CompareTwoObjectsAndSaveChanges<TObjectType>(TObjectType objectA, TObjectType objectB )
{
if(objectA.GetType() == objectB.GetType())
{
foreach (var prop in objectA.GetType().GetProperties())
{
if(prop.GetValue(objectA, null) != prop.GetValue(objectB, null))
{
// Get the column attribute
ColumnAttr attr = (ColumnAttr)objectA.GetType().GetCustomAttributes(typeof(ColumnAttr), false).First();
string colValue = attr.Name;
string nameOfPropertyThatChanges = prop.Name;
string objectAValue = prop.GetValue(objectA, null).ToString();
string objectBValue = prop.GetValue(objectB, null).ToString();
}
}
}
}
This makes use of the GetCustomAttributes(...) method.
Try this:
var columnAtt = prop.GetCustomAttributes(typeof(CustomAtt),true).Cast<ColumnAtt>().FirstOrDefault();
(fixed)

Get property value from string using reflection

I am trying implement the Data transformation using Reflection1 example in my code.
The GetSourceValue function has a switch comparing various types, but I want to remove these types and properties and have GetSourceValue get the value of the property using only a single string as the parameter. I want to pass a class and property in the string and resolve the value of the property.
Is this possible?
1 Web Archive version of original blog post
public static object GetPropValue(object src, string propName)
{
return src.GetType().GetProperty(propName).GetValue(src, null);
}
Of course, you will want to add validation and whatnot, but that is the gist of it.
How about something like this:
public static Object GetPropValue(this Object obj, String name) {
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;
}
public static T GetPropValue<T>(this Object obj, String name) {
Object retval = GetPropValue(obj, name);
if (retval == null) { return default(T); }
// throws InvalidCastException if types are incompatible
return (T) retval;
}
This will allow you to descend into properties using a single string, like this:
DateTime now = DateTime.Now;
int min = GetPropValue<int>(now, "TimeOfDay.Minutes");
int hrs = now.GetPropValue<int>("TimeOfDay.Hours");
You can either use these methods as static methods or extensions.
Add to any Class:
public class Foo
{
public object this[string propertyName]
{
get { return this.GetType().GetProperty(propertyName).GetValue(this, null); }
set { this.GetType().GetProperty(propertyName).SetValue(this, value, null); }
}
public string Bar { get; set; }
}
Then, you can use as:
Foo f = new Foo();
// Set
f["Bar"] = "asdf";
// Get
string s = (string)f["Bar"];
What about using the CallByName of the Microsoft.VisualBasic namespace (Microsoft.VisualBasic.dll)? It uses reflection to get properties, fields, and methods of normal objects, COM objects, and even dynamic objects.
using Microsoft.VisualBasic;
using Microsoft.VisualBasic.CompilerServices;
and then
Versioned.CallByName(this, "method/function/prop name", CallType.Get).ToString();
Great answer by jheddings. I would like to improve it by allowing referencing of aggregated arrays or collections of objects, so that propertyName could be property1.property2[X].property3:
public static object GetPropertyValue(object srcobj, string propertyName)
{
if (srcobj == null)
return null;
object obj = srcobj;
// Split property name to parts (propertyName could be hierarchical, like obj.subobj.subobj.property
string[] propertyNameParts = propertyName.Split('.');
foreach (string propertyNamePart in propertyNameParts)
{
if (obj == null) return null;
// propertyNamePart could contain reference to specific
// element (by index) inside a collection
if (!propertyNamePart.Contains("["))
{
PropertyInfo pi = obj.GetType().GetProperty(propertyNamePart);
if (pi == null) return null;
obj = pi.GetValue(obj, null);
}
else
{ // propertyNamePart is areference to specific element
// (by index) inside a collection
// like AggregatedCollection[123]
// get collection name and element index
int indexStart = propertyNamePart.IndexOf("[")+1;
string collectionPropertyName = propertyNamePart.Substring(0, indexStart-1);
int collectionElementIndex = Int32.Parse(propertyNamePart.Substring(indexStart, propertyNamePart.Length-indexStart-1));
// get collection object
PropertyInfo pi = obj.GetType().GetProperty(collectionPropertyName);
if (pi == null) return null;
object unknownCollection = pi.GetValue(obj, null);
// try to process the collection as array
if (unknownCollection.GetType().IsArray)
{
object[] collectionAsArray = unknownCollection as object[];
obj = collectionAsArray[collectionElementIndex];
}
else
{
// try to process the collection as IList
System.Collections.IList collectionAsList = unknownCollection as System.Collections.IList;
if (collectionAsList != null)
{
obj = collectionAsList[collectionElementIndex];
}
else
{
// ??? Unsupported collection type
}
}
}
}
return obj;
}
If I use the code from Ed S. I get
'ReflectionExtensions.GetProperty(Type, string)' is inaccessible due to its protection level
It seems that GetProperty() is not available in Xamarin.Forms. TargetFrameworkProfile is Profile7 in my Portable Class Library (.NET Framework 4.5, Windows 8, ASP.NET Core 1.0, Xamarin.Android, Xamarin.iOS, Xamarin.iOS Classic).
Now I found a working solution:
using System.Linq;
using System.Reflection;
public static object GetPropValue(object source, string propertyName)
{
var property = source.GetType().GetRuntimeProperties().FirstOrDefault(p => string.Equals(p.Name, propertyName, StringComparison.OrdinalIgnoreCase));
return property?.GetValue(source);
}
Source
About the nested properties discussion, you can avoid all the reflection stuff if you use the DataBinder.Eval Method (Object, String) as below:
var value = DataBinder.Eval(DateTime.Now, "TimeOfDay.Hours");
Of course, you'll need to add a reference to the System.Web assembly, but this probably isn't a big deal.
The method to call has changed in .NET Standard (as of 1.6). Also we can use C# 6's null conditional operator.
using System.Reflection;
public static object GetPropValue(object src, string propName)
{
return src.GetType().GetRuntimeProperty(propName)?.GetValue(src);
}
The below method works perfect for me:
class MyClass {
public string prop1 { set; get; }
public object this[string propertyName]
{
get { return this.GetType().GetProperty(propertyName).GetValue(this, null); }
set { this.GetType().GetProperty(propertyName).SetValue(this, value, null); }
}
}
To get the property value:
MyClass t1 = new MyClass();
...
string value = t1["prop1"].ToString();
To set the property value:
t1["prop1"] = value;
public static List<KeyValuePair<string, string>> GetProperties(object item) //where T : class
{
var result = new List<KeyValuePair<string, string>>();
if (item != null)
{
var type = item.GetType();
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var pi in properties)
{
var selfValue = type.GetProperty(pi.Name).GetValue(item, null);
if (selfValue != null)
{
result.Add(new KeyValuePair<string, string>(pi.Name, selfValue.ToString()));
}
else
{
result.Add(new KeyValuePair<string, string>(pi.Name, null));
}
}
}
return result;
}
This is a way to get all properties with their values in a List.
Using PropertyInfo of the System.Reflection namespace. Reflection compiles just fine no matter what property we try to access. The error will come up during run-time.
public static object GetObjProperty(object obj, string property)
{
Type t = obj.GetType();
PropertyInfo p = t.GetProperty("Location");
Point location = (Point)p.GetValue(obj, null);
return location;
}
It works fine to get the Location property of an object
Label1.Text = GetObjProperty(button1, "Location").ToString();
We'll get the Location : {X=71,Y=27}
We can also return location.X or location.Y on the same way.
public class YourClass
{
//Add below line in your class
public object this[string propertyName] => GetType().GetProperty(propertyName)?.GetValue(this, null);
public string SampleProperty { get; set; }
}
//And you can get value of any property like this.
var value = YourClass["SampleProperty"];
The following code is a Recursive method for displaying the entire hierarchy of all of the Property Names and Values contained in an object's instance. This method uses a simplified version of AlexD's GetPropertyValue() answer above in this thread. Thanks to this discussion thread, I was able to figure out how to do this!
For example, I use this method to show an explosion or dump of all of the properties in a WebService response by calling the method as follows:
PropertyValues_byRecursion("Response", response, false);
public static object GetPropertyValue(object srcObj, string propertyName)
{
if (srcObj == null)
{
return null;
}
PropertyInfo pi = srcObj.GetType().GetProperty(propertyName.Replace("[]", ""));
if (pi == null)
{
return null;
}
return pi.GetValue(srcObj);
}
public static void PropertyValues_byRecursion(string parentPath, object parentObj, bool showNullValues)
{
/// Processes all of the objects contained in the parent object.
/// If an object has a Property Value, then the value is written to the Console
/// Else if the object is a container, then this method is called recursively
/// using the current path and current object as parameters
// Note: If you do not want to see null values, set showNullValues = false
foreach (PropertyInfo pi in parentObj.GetType().GetTypeInfo().GetProperties())
{
// Build the current object property's namespace path.
// Recursion extends this to be the property's full namespace path.
string currentPath = parentPath + "." + pi.Name;
// Get the selected property's value as an object
object myPropertyValue = GetPropertyValue(parentObj, pi.Name);
if (myPropertyValue == null)
{
// Instance of Property does not exist
if (showNullValues)
{
Console.WriteLine(currentPath + " = null");
// Note: If you are replacing these Console.Write... methods callback methods,
// consider passing DBNull.Value instead of null in any method object parameters.
}
}
else if (myPropertyValue.GetType().IsArray)
{
// myPropertyValue is an object instance of an Array of business objects.
// Initialize an array index variable so we can show NamespacePath[idx] in the results.
int idx = 0;
foreach (object business in (Array)myPropertyValue)
{
if (business == null)
{
// Instance of Property does not exist
// Not sure if this is possible in this context.
if (showNullValues)
{
Console.WriteLine(currentPath + "[" + idx.ToString() + "]" + " = null");
}
}
else if (business.GetType().IsArray)
{
// myPropertyValue[idx] is another Array!
// Let recursion process it.
PropertyValues_byRecursion(currentPath + "[" + idx.ToString() + "]", business, showNullValues);
}
else if (business.GetType().IsSealed)
{
// Display the Full Property Path and its Value
Console.WriteLine(currentPath + "[" + idx.ToString() + "] = " + business.ToString());
}
else
{
// Unsealed Type Properties can contain child objects.
// Recurse into my property value object to process its properties and child objects.
PropertyValues_byRecursion(currentPath + "[" + idx.ToString() + "]", business, showNullValues);
}
idx++;
}
}
else if (myPropertyValue.GetType().IsSealed)
{
// myPropertyValue is a simple value
Console.WriteLine(currentPath + " = " + myPropertyValue.ToString());
}
else
{
// Unsealed Type Properties can contain child objects.
// Recurse into my property value object to process its properties and child objects.
PropertyValues_byRecursion(currentPath, myPropertyValue, showNullValues);
}
}
}
public static TValue GetFieldValue<TValue>(this object instance, string name)
{
var type = instance.GetType();
var field = type.GetFields(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).FirstOrDefault(e => typeof(TValue).IsAssignableFrom(e.FieldType) && e.Name == name);
return (TValue)field?.GetValue(instance);
}
public static TValue GetPropertyValue<TValue>(this object instance, string name)
{
var type = instance.GetType();
var field = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).FirstOrDefault(e => typeof(TValue).IsAssignableFrom(e.PropertyType) && e.Name == name);
return (TValue)field?.GetValue(instance);
}
Dim NewHandle As YourType = CType(Microsoft.VisualBasic.CallByName(ObjectThatContainsYourVariable, "YourVariableName", CallType), YourType)
Here is another way to find a nested property that doesn't require the string to tell you the nesting path. Credit to Ed S. for the single property method.
public static T FindNestedPropertyValue<T, N>(N model, string propName) {
T retVal = default(T);
bool found = false;
PropertyInfo[] properties = typeof(N).GetProperties();
foreach (PropertyInfo property in properties) {
var currentProperty = property.GetValue(model, null);
if (!found) {
try {
retVal = GetPropValue<T>(currentProperty, propName);
found = true;
} catch { }
}
}
if (!found) {
throw new Exception("Unable to find property: " + propName);
}
return retVal;
}
public static T GetPropValue<T>(object srcObject, string propName) {
return (T)srcObject.GetType().GetProperty(propName).GetValue(srcObject, null);
}
You never mention what object you are inspecting, and since you are rejecting ones that reference a given object, I will assume you mean a static one.
using System.Reflection;
public object GetPropValue(string prop)
{
int splitPoint = prop.LastIndexOf('.');
Type type = Assembly.GetEntryAssembly().GetType(prop.Substring(0, splitPoint));
object obj = null;
return type.GetProperty(prop.Substring(splitPoint + 1)).GetValue(obj, null);
}
Note that I marked the object that is being inspected with the local variable obj. null means static, otherwise set it to what you want. Also note that the GetEntryAssembly() is one of a few available methods to get the "running" assembly, you may want to play around with it if you are having a hard time loading the type.
Have a look at the Heleonix.Reflection library. You can get/set/invoke members by paths, or create a getter/setter (lambda compiled into a delegate) which is faster than reflection. For example:
var success = Reflector.Get(DateTime.Now, null, "Date.Year", out int value);
Or create a getter once and cache for reuse (this is more performant but might throw NullReferenceException if an intermediate member is null):
var getter = Reflector.CreateGetter<DateTime, int>("Date.Year", typeof(DateTime));
getter(DateTime.Now);
Or if you want to create a List<Action<object, object>> of different getters, just specify base types for compiled delegates (type conversions will be added into compiled lambdas):
var getter = Reflector.CreateGetter<object, object>("Date.Year", typeof(DateTime));
getter(DateTime.Now);
Although the original question was about how to get the value of the property using only a single string as the parameter, it makes a lot of sense here to use an Expression rather than simply a string to ensure that the caller never uses a hard coded property name. Here is a one line version with usage:
public static class Utils
...
public static TVal GetPropertyValue<T, TVal>(T t, Expression<Func<T, TVal>> x)
=> (TVal)((x.Body as MemberExpression)?.Member as PropertyInfo)!.GetValue(t);
...
var val = Utils.GetPropertyValue(foo, p => p.Bar);
Here is a slightly better version in terms of readability a error handling:
public static TVal GetPropertyValue<T, TVal>(T t, Expression<Func<T, TVal>> x)
{
var m = (x.Body as MemberExpression)?.Member;
var p = m as PropertyInfo;
if (null == p)
throw new ArgumentException($"Unknown property: {typeof(T).Name}.{(m?.Name??"???")}");
return (TVal)p.GetValue(t);
}
In short you pass in a lambda expression reading a property. The body of the lambda - the part on the right of the fat arrow - is a member expression from which you can get the member name and which you can cast to a PropertyInfo, provided the member is actually a Property and not, for instance, a method.
In the short version, the null forgiving operator - the ! in the expression - tells the compiler that the PropertyInfo will not be null. This is a big lie and you will get a NullReferenceException at runtime. The longer version gives you the name of the property if it manages to get it.
PS: Thanks to Oleg G. for the initial version of this code :)
shorter way ....
var a = new Test { Id = 1 , Name = "A" , date = DateTime.Now};
var b = new Test { Id = 1 , Name = "AXXX", date = DateTime.Now };
var compare = string.Join("",a.GetType().GetProperties().Select(x => x.GetValue(a)).ToArray())==
string.Join("",b.GetType().GetProperties().Select(x => x.GetValue(b)).ToArray());
jheddings and AlexD both wrote excellent answers on how to resolve property strings. I'd like to throw mine in the mix, since I wrote a dedicated library exactly for that purpose.
Pather.CSharp's main class is Resolver. Per default it can resolve properties, array and dictionary entries.
So, for example, if you have an object like this
var o = new { Property1 = new { Property2 = "value" } };
and want to get Property2, you can do it like this:
IResolver resolver = new Resolver();
var path = "Property1.Property2";
object result = r.Resolve(o, path);
//=> "value"
This is the most basic example of the paths it can resolve. If you want to see what else it can, or how you can extend it, just head to its Github page.
Here's what I got based on other answers. A little overkill on getting so specific with the error handling.
public static T GetPropertyValue<T>(object sourceInstance, string targetPropertyName, bool throwExceptionIfNotExists = false)
{
string errorMsg = null;
try
{
if (sourceInstance == null || string.IsNullOrWhiteSpace(targetPropertyName))
{
errorMsg = $"Source object is null or property name is null or whitespace. '{targetPropertyName}'";
Log.Warn(errorMsg);
if (throwExceptionIfNotExists)
throw new ArgumentException(errorMsg);
else
return default(T);
}
Type returnType = typeof(T);
Type sourceType = sourceInstance.GetType();
PropertyInfo propertyInfo = sourceType.GetProperty(targetPropertyName, returnType);
if (propertyInfo == null)
{
errorMsg = $"Property name '{targetPropertyName}' of type '{returnType}' not found for source object of type '{sourceType}'";
Log.Warn(errorMsg);
if (throwExceptionIfNotExists)
throw new ArgumentException(errorMsg);
else
return default(T);
}
return (T)propertyInfo.GetValue(sourceInstance, null);
}
catch(Exception ex)
{
errorMsg = $"Problem getting property name '{targetPropertyName}' from source instance.";
Log.Error(errorMsg, ex);
if (throwExceptionIfNotExists)
throw;
}
return default(T);
}
Here is my solution. It works also with COM objects and allows to access collection/array items from COM objects.
public static object GetPropValue(this object obj, string name)
{
foreach (string part in name.Split('.'))
{
if (obj == null) { return null; }
Type type = obj.GetType();
if (type.Name == "__ComObject")
{
if (part.Contains('['))
{
string partWithoundIndex = part;
int index = ParseIndexFromPropertyName(ref partWithoundIndex);
obj = Versioned.CallByName(obj, partWithoundIndex, CallType.Get, index);
}
else
{
obj = Versioned.CallByName(obj, part, CallType.Get);
}
}
else
{
PropertyInfo info = type.GetProperty(part);
if (info == null) { return null; }
obj = info.GetValue(obj, null);
}
}
return obj;
}
private static int ParseIndexFromPropertyName(ref string name)
{
int index = -1;
int s = name.IndexOf('[') + 1;
int e = name.IndexOf(']');
if (e < s)
{
throw new ArgumentException();
}
string tmp = name.Substring(s, e - s);
index = Convert.ToInt32(tmp);
name = name.Substring(0, s - 1);
return index;
}
Whenever you want to loop over all properties in on an object and then use each value of the property must use this piece of code:
foreach (var property in request.GetType().GetProperties())
{
var valueOfProperty = property.GetValue(properties, null);
}

Categories

Resources