Related
I am attempting a Generic Deep Clone Routine for code first Entity Framework entities.
I've cracked it for the standard System property types but am having trouble with Proxy Entities (defined with virtual) i.e.
[EntityLookup]
public virtual Person { get; set; }
[EntityLookup] is one of my own attributes that helps further define the Association.
If I remove the "virtual" keyword, my routine can update the destination entity property no problem (but I lose the additional EF functionality)
With virtual I get the following error;
System.Reflection.TargetException: 'Object does not match target type.'
I believe it's all to do with EF's Proxy class but I'm not sure how to cast the original entity so I can set it on the destination.
Below are the essentials of the Clone routine for this issue;
public static void CloneProperties<T>(T Original, T Destination)
{
PropertyInfo[] props = Original.GetType().GetProperties();
foreach (var propertyInfo in props)
{
if (propertyInfo.PropertyType.Namespace == "System" || propertyInfo.PropertyType.IsEnum)....
else
{
if (Destination.PropertyHasCustomAttribute (propertyInfo.Name, typeof(EntityLookupAttribute)))
{
var pv = propertyInfo.GetValue(Original, null);
propertyInfo.SetValue(Destination, pv, null);
}
}
}
}
It's the "propertyInfo.SetValue(Destination, pv, null);" that generates the error when the entity is declared virtual.
Any help on getting it to work will be gratefully accepted
Best Regards
Lance
In addition and in a similar vein I am now attempting to clone the child collections in my entity.
I'm iterating over the source property collection and need to add missing records to the destination properties collection
the a.Add(targetEntity); line is giving the following error;
"The best overloaded method match for 'System.Collections.ObjectModel.Collection<FmCosting.Entities.CsJobDetail>.Add(FmCosting.Entities.CsJobDetail)' has some invalid arguments"
The relevant code is;
if (dest.PropertyHasCustomAttribute(propertyInfo.Name, typeof(EntityChildCollectionAttribute)))
{
var source = propertyInfo.GetValue(original, null) as ICollection;
var target = propertyInfo.GetValue(dest, null) as ICollection;
foreach (dynamic sourceEntity in source)
{
var found = false;
object targetEntity = null;
foreach (dynamic tEntity in target)
{
if (sourceEntity.IdentityGuid == tEntity.IdentityGuid)
{
found = true;
targetEntity = tEntity;
continue;
}
}
if (!found)
{
var t = sourceEntity.GetType();
targetEntity = Activator.CreateInstance(t);
}
sourceEntity.CloneMeToProvidedEntity(targetEntity);
if (!found)
{
dynamic a = target;
a.Add(targetEntity);
}
}
//propertyInfo.SetValue(Destination, pv, null);
}
Any further help will be gratefully received
Best Regards
Lance
The concrete type for your destination object may be different from T, for this reason you have to use a PropertyInfo of destination and not of original:
public static void CloneProperties<T>(T original, T destination)
{
var originalType = original.GetType();
var destinationType = destination.GetType();
PropertyInfo[] props = originalType.GetProperties();
foreach (var propertyInfo in props)
{
if (propertyInfo.PropertyType.Namespace == "System" || propertyInfo.PropertyType.IsEnum)
{
// ....
}
else
{
if (destination.PropertyHasCustomAttribute (propertyInfo.Name, typeof(EntityLookupAttribute)))
{
var pv = propertyInfo.GetValue(original, null);
var destinationProperty = destinationType.GetProperty(propertyInfo.Name);
destinationProperty.SetValue(destination, pv, null);
}
}
}
}
Note:
Another option is to revert to compile time types, so both objects uses properties of T avoiding derived types that may be returned from GetType():
public static void CloneProperties<T>(T original, T destination)
{
PropertyInfo[] props = typeof(T).GetProperties();
foreach (var propertyInfo in props)
{
if (propertyInfo.PropertyType.Namespace == "System" || propertyInfo.PropertyType.IsEnum)
{
// ....
}
else
{
if (destination.PropertyHasCustomAttribute (propertyInfo.Name, typeof(EntityLookupAttribute)))
{
var pv = propertyInfo.GetValue(original, null);
propertyInfo.SetValue(destination, pv, null);
}
}
}
}
I got the collection clone working using IList, here is the relevant code if it helps anybody else.
if (dest.PropertyHasCustomAttribute(propertyInfo.Name, typeof(EntityChildCollectionAttribute)))
{
var source = propertyInfo.GetValue(original, null) as IList;
var target = propertyInfo.GetValue(dest, null) as IList;
foreach (dynamic sourceEntity in source)
{
var found = false;
object targetEntity = null;
foreach (dynamic tEntity in target)
{
if (sourceEntity.IdentityGuid != tEntity.IdentityGuid) continue;
found = true;
targetEntity = tEntity;
break;
}
if (!found)
{
var b = propertyInfo.PropertyType.GetGenericArguments()[0];
targetEntity = Activator.CreateInstance(b);
}
sourceEntity.CloneMeToProvidedEntity(targetEntity);
if (!found)
{
target.Add(targetEntity);
}
}
}
I have the problem, that i don't know how to call the this property of an object instance over reflection.
I have the following method:
public object GetValue(SOP sop, object value)
{
// 1. find the this getter/setter method for the value parameter
// 2. Invoke the method and pass the result of GetIndexArray(sop) as a parameter
// 3. return the result of the method
}
which should call the this property of the value-instance and return the value the property returns.
The problem is, that i don't know the type of the value-instance, which is passed as a parameter.
The index which should be passed to the this property is given over the following method:
private object[] GetIndexArray(SOP sop)
But i don't know how to call the this-property. The object-instance can be anything, a string, Dictionary, ...
Does any one has an idea, how to solve the problem?
EDIT:
The GetValue method should do the follwing task, but dynamically over reflection:
public object GetValue(SOP sop, object value)
{
// Non relfection, lets say 'value' is a Dicitionary<string, string>
// return value["key"];
// The type of 'value' is unknown, and the index-parameter (the keys) are accessable over GetIndexArray()
}
EDIT2:
In c# every getter and setter can be invoked via reflection as a method. Is there a way, to get the methods for the "this" property? If it's possible, the problem can be solved with invoking the mesthods.
Call of the GetValue method
object pInst = parentInstance.GetType().GetProperty(VariableName, System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic).GetValue(parentInstance, null);
var indexChldren = FindChildrenOfType<IndexNode>();
if (indexChldren.Count > 0)
{
pInst = indexChldren[0].GetValue(sop, pInst);
}
I decided to remake the post, since my previous one didn't correctly do what it was supposed to do.
To check which indexers are on an object, you can use the following, which will
Return null when object is null
Return an empty IList when no indexers are found
Return a list of MethodInfos with the found indexers
The trick to check it, is to check if a property has any GetIndexParameters();
public IList<MethodInfo> GetIndexProperties(object obj)
{
if (obj == null)
{
return null;
}
var type = obj.GetType();
IList<MethodInfo> results = new List<MethodInfo>();
try
{
var props = type.GetProperties(System.Reflection.BindingFlags.Default |
System.Reflection.BindingFlags.Public |
System.Reflection.BindingFlags.Instance);
if (props != null)
{
foreach (var prop in props)
{
var indexParameters = prop.GetIndexParameters();
if (indexParameters == null || indexParameters.Length == 0)
{
continue;
}
var getMethod = prop.GetGetMethod();
if (getMethod == null)
{
continue;
}
results.Add(getMethod);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return results;
}
This works with a List, Dictionary, string, object with indexers, indexers defined with IndexerNameAttribute
As an example, i used this MainMethod, to try several possibilities
object[] exampleHayStack = new object[] {
"This is a test of an indexer",
new TestIndexer(),
null,
string.Empty,
new ClassIndexer(),
new Dictionary<string, string>() { { "key", "value" } },
new List<string>() { "A", "B", "C", "D", "E", "F", "G" } };
ClassIndexer myIndexer = new ClassIndexer();
foreach (var obj in exampleHayStack)
{
var methods = myIndexer.GetIndexProperties(obj);
if (methods == null || methods.Count == 0)
{
Console.WriteLine("{0} doesn't have any indexers", obj);
continue;
}
Console.WriteLine("Testing {0}", obj);
foreach (MethodInfo mi in methods)
{
IList<object> indexParams = new List<object>();
var requiredParams = mi.GetParameters();
foreach (var par in requiredParams)
{
indexParams.Add(myIndexer.ParamForObject(obj, par));
}
try
{
var result = mi.Invoke(obj, indexParams.ToArray());
Console.WriteLine("Result of requesting ({0}) = {1}", string.Join(",", indexParams), result);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Console.ReadLine();
Which then results into:
Testing This is a test of an indexer
Result of requesting (21) = i
Testing TestReflection.Program+TestIndexer
Result of requesting (53) = 5
Result of requesting (Key) = Key item
doesn't have any indexers
Testing
Exception has been thrown by the target of an invocation.
TestReflection.Program+ClassIndexer doesn't have any indexers
Testing System.Collections.Generic.Dictionary`2[System.String,System.String]
Result of requesting (key) = value
Testing System.Collections.Generic.List`1[System.String]
Result of requesting (5) = F
The complete implementation of the ClassIndexer you can find here, it contains an extra method for getting possible key values (but those you have already)
public class ClassIndexer
{
Random pNext = new Random();
public IList<MethodInfo> GetIndexProperties(object obj)
{
if (obj == null)
{
return null;
}
var type = obj.GetType();
IList<MethodInfo> results = new List<MethodInfo>();
try
{
var props = type.GetProperties(System.Reflection.BindingFlags.Default |
System.Reflection.BindingFlags.Public |
System.Reflection.BindingFlags.Instance);
if (props != null)
{
foreach (var prop in props)
{
var indexParameters = prop.GetIndexParameters();
if (indexParameters == null || indexParameters.Length == 0)
{
continue;
}
var getMethod = prop.GetGetMethod();
if (getMethod == null)
{
continue;
}
results.Add(getMethod);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return results;
}
public object ParamForObject(object obj, ParameterInfo pi)
{
if (obj is IDictionary)
{
int maxNumber = ((IDictionary)obj).Keys.Count;
if (pi.ParameterType.Equals(typeof(int)))
{
return pNext.Next(maxNumber);
}
if (pi.ParameterType.Equals(typeof(string)))
{
int target = pNext.Next(maxNumber);
foreach (var key in ((IDictionary)obj).Keys)
{
target--;
if (target <= 0)
{
return key;
}
}
return null;
}
}
if (obj is string)
{
if (pi.ParameterType.Equals(typeof(int)))
{
return pNext.Next((obj as string).Length);
}
}
if (obj is IList)
{
return pNext.Next(((IList)obj).Count);
}
if (pi.ParameterType.Equals(typeof(string)))
{
return "Key";
}
if (pi.ParameterType.Equals(typeof(int)))
{
return pNext.Next(100);
}
return null;
}
public ClassIndexer()
{
}
}
For the rest, this was a good research, thanks for the question !
I created a DataRow on my project:
DataRow datarow;
I want to convert this DataRow to any Type of Object.
How could I do it?
This is a pretty cool way I use it.
public static T ToObject<T>(this DataRow dataRow)
where T : new()
{
T item = new T();
foreach (DataColumn column in dataRow.Table.Columns)
{
PropertyInfo property = GetProperty(typeof(T), column.ColumnName);
if (property != null && dataRow[column] != DBNull.Value && dataRow[column].ToString() != "NULL")
{
property.SetValue(item, ChangeType(dataRow[column], property.PropertyType), null);
}
}
return item;
}
private static PropertyInfo GetProperty(Type type, string attributeName)
{
PropertyInfo property = type.GetProperty(attributeName);
if (property != null)
{
return property;
}
return type.GetProperties()
.Where(p => p.IsDefined(typeof(DisplayAttribute), false) && p.GetCustomAttributes(typeof(DisplayAttribute), false).Cast<DisplayAttribute>().Single().Name == attributeName)
.FirstOrDefault();
}
public static object ChangeType(object value, Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
if (value == null)
{
return null;
}
return Convert.ChangeType(value, Nullable.GetUnderlyingType(type));
}
return Convert.ChangeType(value, type);
}
I Have found one solution for my application.
// function that creates an object from the given data row
public static T CreateItemFromRow<T>(DataRow row) where T : new()
{
// create a new object
T item = new T();
// set the item
SetItemFromRow(item, row);
// return
return item;
}
public static void SetItemFromRow<T>(T item, DataRow row) where T : new()
{
// go through each column
foreach (DataColumn c in row.Table.Columns)
{
// find the property for the column
PropertyInfo p = item.GetType().GetProperty(c.ColumnName);
// if exists, set the value
if (p != null && row[c] != DBNull.Value)
{
p.SetValue(item, row[c], null);
}
}
}
This will map your DataRow to ViewModel, Like below.
Your_ViewModel model = CreateItemFromRow<Your_ViewModel>(row);
class Person{
public string FirstName{get;set;}
public string LastName{get;set;}
}
Person person = new Person();
person.FirstName = dataRow["FirstName"] ;
person.LastName = dataRow["LastName"] ;
or
Person person = new Person();
person.FirstName = dataRow.Field<string>("FirstName");
person.LastName = dataRow.Field<string>("LastName");
Similar to some of the previous approaches, I created this extension method for DataRow which takes an argument object to be populated. Main difference is that in addition to populating object's Properties, it also populates Fields of given object. This should also work for simpler structures (Though I only tested on objects).
public static T ToObject<T>( this DataRow dataRow )
where T : new() {
T item = new T();
foreach( DataColumn column in dataRow.Table.Columns ) {
if( dataRow[column] != DBNull.Value ) {
PropertyInfo prop = item.GetType().GetProperty( column.ColumnName );
if( prop != null ) {
object result = Convert.ChangeType( dataRow[column], prop.PropertyType );
prop.SetValue( item, result, null );
continue;
}
else {
FieldInfo fld = item.GetType().GetField( column.ColumnName );
if( fld != null ) {
object result = Convert.ChangeType( dataRow[column], fld.FieldType );
fld.SetValue( item, result );
}
}
}
}
return item;
}
You can put this code in your current class or in a global static class.
It needs following namespaces...
using System;
using System.Data;
using System.Reflection;
Usage is as simple as...
MyClassName obj = dataRow.ToObject<MyClassName>()
Here is an extension method that would allow you to convert a DataRow to a given object.
public static class DataRowExtensions
{
public static T Cast<T>(this DataRow dataRow) where T : new()
{
T item = new T();
IEnumerable<PropertyInfo> properties = item.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(x => x.CanWrite);
foreach (DataColumn column in dataRow.Table.Columns)
{
if (dataRow[column] == DBNull.Value)
{
continue;
}
PropertyInfo property = properties.FirstOrDefault(x => column.ColumnName.Equals(x.Name, StringComparison.OrdinalIgnoreCase));
if (property == null)
{
continue;
}
try
{
Type t = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
object safeValue = (dataRow[column] == null) ? null : Convert.ChangeType(dataRow[column], t);
property.SetValue(item, safeValue, null);
}
catch
{
throw new Exception($"The value '{dataRow[column]}' cannot be mapped to the property '{property.Name}'!");
}
}
return item;
}
}
And you can use the above extension method like so
foreach (DataRow row in dataTable.Rows)
{
SomeClassType obj = row.Cast<SomeClassType>();
// do something with your object
}
Given Converter<TIn, TOut> is a delegate, then the following should work:
List<Person> personList = new List<Person>();
personList = ConvertDataRowToList(ds, (row) => {
return new Person
{
FirstName = row["FirstName"],
LastName = row["LastName"]
// Rest of properties should assign here...
};
});
https://learn.microsoft.com/en-us/dotnet/api/system.converter-2
Apart from the manual method Avi shows, you can use a mapping system like AutoMapper to do the transformation for you. This is particularly useful in the case where you have a lot of columns/properties to map.
Check out this article on how to use AutoMapper to convert a DataTable to a list of objects.
DataRow has a property ItemArray, which contains an array of object values. You can work with this array and create any custom type with the values from your DataRow.
With less complications ;), two steps will solve the task:
1. cast to dictionary (ToDictionary).
2. map dictionary to entity (MapToEntity).
public static IDictionary<string, object> ToDictionary(
this DataRow content
)
{
var values = content.ItemArray;
var columns = content
.Table
.Columns
.Cast<DataColumn>()
.Select(x => x.ColumnName);
return values
.Select((v, m) => new { v, m })
.ToDictionary(
x => columns.ElementAt(x.m)
, x => (x.v == DBNull.Value ? null : x.v)
);
}
public static T MapToEntity<T>(
this IDictionary<string, object> source
)
where T : class, new()
{
// t - target
T t_object = new T();
Type t_type = t_object.GetType();
foreach (var kvp in source)
{
PropertyInfo t_property = t_type.GetProperty(kvp.Key);
if (t_property != null)
{
t_property.SetValue(t_object, kvp.Value);
}
}
return t_object;
}
...and the usage would be:
DataRow dr = getSomeDataRow(someArgs);
ABC result = dr.ToDictionary()
.MapToEntity<ABC>();
You could convert the whole Data table into a list Object like the code below. Of course, you can take the specific object which you want with the index or the field value.
/// <summary>
/// convert a datatable to list Object
/// </summary>
/// <typeparam name="T">object model</typeparam>
/// <param name="dataTable"></param>
/// <returns>ex ussage: List<User> listTbl = CommonFunc.convertDatatblToListObj<User>(dataTable);</returns>
public static List<T> convertDatatableToListObject<T>(DataTable dataTable)
{
List<T> res = new List<T>();
try
{
string tblJson = JsonConvert.SerializeObject(dataTable);
res = JsonConvert.DeserializeObject<List<T>>(tblJson);
}
catch (Exception ex)
{
string exStr = ex.Message;
}
return res;
}
With these changes worked fine for me, for fields int, long, int? and long?
// function that creates an object from the given data row
public static T CreateItemFromRow<T>(DataRow row) where T : new()
{
// create a new object
T item = new T();
// set the item
SetItemFromRow(item, row);
// return
return item;
}
public static void SetItemFromRow<T>(T item, DataRow row) where T : new()
{
// go through each column
foreach (DataColumn c in row.Table.Columns)
{
// find the property for the column
PropertyInfo p = item.GetType().GetProperty(c.ColumnName);
// if exists, set the value
if (p != null && row[c] != DBNull.Value)
{
if (p.PropertyType.Name == "Int64")
{
p.SetValue(item, long.Parse(row[c].ToString()), null);
}
else if (p.PropertyType.Name == "Int32")
{
p.SetValue(item, int.Parse(row[c].ToString()), null);
}
else if (p.PropertyType.FullName.StartsWith("System.Nullable`1[[System.Int32"))
{
p.SetValue(item, (int?)int.Parse(row[c].ToString()), null);
}
else if (p.PropertyType.FullName.StartsWith("System.Nullable`1[[System.Int64"))
{
p.SetValue(item, (long?)long.Parse(row[c].ToString()), null);
}
else
{
p.SetValue(item, row[c], null);
}
}
}
}
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");
I am writing a Clone method using reflection. How do I detect that a property is an indexed property using reflection? For example:
public string[] Items
{
get;
set;
}
My method so far:
public static T Clone<T>(T from, List<string> propertiesToIgnore) where T : new()
{
T to = new T();
Type myType = from.GetType();
PropertyInfo[] myProperties = myType.GetProperties();
for (int i = 0; i < myProperties.Length; i++)
{
if (myProperties[i].CanWrite && !propertiesToIgnore.Contains(myProperties[i].Name))
{
myProperties[i].SetValue(to,myProperties[i].GetValue(from,null),null);
}
}
return to;
}
if (propertyInfo.GetIndexParameters().Length > 0)
{
// Property is an indexer
}
Sorry, but
public string[] Items { get; set; }
is not an indexed property, it's merely of an array type!
However the following is:
public string this[int index]
{
get { ... }
set { ... }
}
What you want is the GetIndexParameters() method. If the array that it returns has more than 0 items, that means it's an indexed property.
See the MSDN documentation for more details.
If you call property.GetValue(obj,null), and the property IS indexed, then you will get a parameter count mismatch exception. Better to check whether the property is indexed using GetIndexParameters() and then decide what to do.
Here is some code that worked for me:
foreach (PropertyInfo property in obj.GetType().GetProperties())
{
object value = property.GetValue(obj, null);
if (value is object[])
{
....
}
}
P.S. .GetIndexParameters().Length > 0) works for the case described in this article: http://msdn.microsoft.com/en-us/library/b05d59ty.aspx
So if you care about the property named Chars for a value of type string, use that, but it does not work for most of the arrays I was interested in, including, I am pretty sure, a string array from the original question.
You can convert the indexer to IEnumerable
public static IEnumerable<T> AsEnumerable<T>(this object o) where T : class {
var list = new List<T>();
System.Reflection.PropertyInfo indexerProperty = null;
foreach (System.Reflection.PropertyInfo pi in o.GetType().GetProperties()) {
if (pi.GetIndexParameters().Length > 0) {
indexerProperty = pi;
break;
}
}
if (indexerProperty.IsNotNull()) {
var len = o.GetPropertyValue<int>("Length");
for (int i = 0; i < len; i++) {
var item = indexerProperty.GetValue(o, new object[]{i});
if (item.IsNotNull()) {
var itemObject = item as T;
if (itemObject.IsNotNull()) {
list.Add(itemObject);
}
}
}
}
return list;
}
public static bool IsNotNull(this object o) {
return o != null;
}
public static T GetPropertyValue<T>(this object source, string property) {
if (source == null)
throw new ArgumentNullException("source");
var sourceType = source.GetType();
var sourceProperties = sourceType.GetProperties();
var properties = sourceProperties
.Where(s => s.Name.Equals(property));
if (properties.Count() == 0) {
sourceProperties = sourceType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic);
properties = sourceProperties.Where(s => s.Name.Equals(property));
}
if (properties.Count() > 0) {
var propertyValue = properties
.Select(s => s.GetValue(source, null))
.FirstOrDefault();
return propertyValue != null ? (T)propertyValue : default(T);
}
return default(T);
}