I have 2 instances of the same objects, o1, and o2. If I am doing things like
if (o1.property1 != null) o1.property1 = o2.property1
for all the properties in the object. What would be the most efficient way to loop through all properties in an Object and do that? I saw people using PropertyInfo to check nulll of the properties but it seems like they could only get through the PropertyInfo collection but not link the operation of the properties.
Thanks.
You can do this with reflection:
public void CopyNonNullProperties(object source, object target)
{
// You could potentially relax this, e.g. making sure that the
// target was a subtype of the source.
if (source.GetType() != target.GetType())
{
throw new ArgumentException("Objects must be of the same type");
}
foreach (var prop in source.GetType()
.GetProperties(BindingFlags.Instance |
BindingFlags.Public)
.Where(p => !p.GetIndexParameters().Any())
.Where(p => p.CanRead && p.CanWrite))
{
var value = prop.GetValue(source, null);
if (value != null)
{
prop.SetValue(target, value, null);
}
}
}
Judging from your example i think your looking for something like this:
static void CopyTo<T>(T from, T to)
{
foreach (PropertyInfo property in typeof(T).GetProperties())
{
if (!property.CanRead || !property.CanWrite || (property.GetIndexParameters().Length > 0))
continue;
object value = property.GetValue(to, null);
if (value != null)
property.SetValue(to, property.GetValue(from, null), null);
}
}
If you are going to use this many times, you could use a compiled expression for better performance:
public static class Mapper<T>
{
static Mapper()
{
var from = Expression.Parameter(typeof(T), "from");
var to = Expression.Parameter(typeof(T), "to");
var setExpressions = typeof(T)
.GetProperties()
.Where(property => property.CanRead && property.CanWrite && !property.GetIndexParameters().Any())
.Select(property =>
{
var getExpression = Expression.Call(from, property.GetGetMethod());
var setExpression = Expression.Call(to, property.GetSetMethod(), getExpression);
var equalExpression = Expression.Equal(Expression.Convert(getExpression, typeof(object)), Expression.Constant(null));
return Expression.IfThen(Expression.Not(equalExpression), setExpression);
});
Map = Expression.Lambda<Action<T, T>>(Expression.Block(setExpressions), from, to).Compile();
}
public static Action<T, T> Map { get; private set; }
}
And use it like this:
Mapper<Entity>.Map(e1, e2);
Related
There is a filtering on the site that works to find a value by a public property:
? propertyName = SomeName & propertyValue = SomeValue;
Now I'm wondering what if, for example, if an incorrect name of this property (SomENAme1) is entered -> what would the web application still call filtering by the SomeName property? That is, whatever the case does not matter, and that the entered word is matched at least completely with the filter property?
public static IQueryable<T> ApplyFiltering<T>(this IQueryable<T> source, string propertyLabel, string propertyValue)
{
if (string.IsNullOrEmpty(propertyLabel))
{
return source;
}
var propertyNames = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(property => property.PropertyType == typeof(string) && property.Name == propertyLabel)
.Select(property => property.Name);
//var predicate = PredicateBuilder.New<T>();
Expression<Func<T, bool>> predicate = item => false;
foreach (var name in propertyNames)
{
// if (propertyLabel.Contains(name))
// {
//propertyLabel = name;
//}
predicate = predicate.Or(GetExpression<T>(name, propertyValue));
}
return source.Where(predicate);
}
I've triend in foreach loop:
if (propertyLabel.ToLower().Contains(name.ToLower()))
{
propertyLabel = name;
}
But it doesnt work, even just for lowercase...
var propertyNames = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(property => property.PropertyType == typeof(string)
&& propertyLabel.ToLower().Contains(property.Name.ToLower()))
.Select(property => property.Name);
Context:
I have a method to create a long Conjunction:
public static Conjunction GetLongConjunction()
{
Conjunction conjunction = new Conjunction();
conjunction.Add<Person>(p => p.Id > 0);
conjunction.Add<Person>(p => p.Age > 18);
Disjunction disjunction = new Disjunction();
disjunction.Add<Person>(p => p.Name == "John");
disjunction.Add<Person>(p => p.Name == "Alice");
conjunction.Add(disjunction);
// ...
return conjunction;
}
In another method, I'm using GetLongConjunction to get the conjunction:
public void AnotherMethod()
{
Conjunction newConjunction = GetLongConjunction();
// ...
}
The problem is: I want to edit (or remove) one condition in newConjunction.
What I tried:
I tried to get the List<NHibernate.Criterion.ICriterion> in criteria property from AbstractCriterion class. Conjunction extends it:
Conjunction: Junction: AbstractCriterion
But criteria is a private property, and I can't get it.
Question:
So, my question is:
How can I edit a NHibernate Junction? Is that possible?
Thanks!!
To keep it simple, you can use reflection:
public void AnotherMethod()
{
Conjunction newConjunction = GetLongConjunction();
IList<ICriterion> criteria = newConjunction.GetCriteria();
// Add or remove expressions
// var disjunction = (Disjunction) criteria.Last();
}
Using this extension methods:
public static class MyExtensions
{
public static IList<ICriterion> GetCriteria(this Junction juntion)
{
return juntion.GetPrivateFieldValue<IList<ICriterion>>("criteria");
}
public static T GetPrivateFieldValue<T>(this object obj, string propName)
{
if (obj == null) throw new ArgumentNullException("obj");
Type t = obj.GetType();
FieldInfo fi = null;
while (fi == null && t != null)
{
fi = t.GetField(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
t = t.BaseType;
}
if (fi == null) throw new ArgumentOutOfRangeException("propName", string.Format("Field {0} was not found in Type {1}", propName, obj.GetType().FullName));
return (T)fi.GetValue(obj);
}
}
Recently I had to use ExpandoObject in my application, So I want to know how could I use my old Mapper to also map from Dynamic ExpandoOnjects, because some how it does not map the fields, and properties from an Expando.
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
public class Mapper
{
private static readonly Dictionary<KeyValuePair<Type, Type>, object> Maps = new Dictionary<KeyValuePair<Type, Type>, object>();
private static PropertyInfo[] _fromProperties;
private static PropertyInfo[] _toProperties;
private static FieldInfo[] _fromFields;
private static FieldInfo[] _toFields;
// Rules...
private static readonly Func<PropertyInfo, PropertyInfo, bool> MatchingProps = (t1, t2) => t1.Name == t2.Name && t1.PropertyType.Name == t2.PropertyType.Name;
private static readonly Func<FieldInfo, FieldInfo, bool> MatchingFields = (t1, t2) => t1.Name == t2.Name && t1.FieldType.Name == t2.FieldType.Name;
private static readonly Func<PropertyInfo, FieldInfo, bool> MatchingPropertyToField = (t1, t2) => t1.Name == t2.Name && t1.PropertyType.Name == t2.FieldType.Name;
private static readonly Func<FieldInfo, PropertyInfo, bool> MatchingFieldToProperty = (t1, t2) => t1.Name == t2.Name && t1.FieldType.Name == t2.PropertyType.Name;
public static void AddMap<TFrom, TTo>(Action<TFrom, TTo> map = null)
where TFrom : class
where TTo : class { Maps.Add(new KeyValuePair<Type, Type>(typeof(TFrom), typeof(TTo)), map); }
public static void Map<TFromType, TOType>(TFromType #from, TOType to)
{
var key = new KeyValuePair<Type, Type>(typeof(TFromType), typeof(TOType));
var map = (Action<TFromType, TOType>)Maps[key];
bool hasMapping = Maps.Any(x => x.Key.Equals(key));
if (!hasMapping)
throw new Exception(string.Format("No map defined for {0} => {1}", typeof(TFromType).Name, typeof(TOType).Name));
Type tFrom = typeof(TFromType);
Type tTo = typeof(TOType);
_fromProperties = tFrom.GetProperties();
_fromFields = tFrom.GetFields();
_toProperties = tTo.GetProperties();
_toFields = tTo.GetFields();
SyncProperties(#from, to);
SyncFields(#from, to);
if (!Equals(map, null))
map(#from, to);
}
private static void SyncProperties<TFromType, TOType>(TFromType objFrom, TOType objTo)
{
PropertyInfo[] fromProperties = _fromProperties;
PropertyInfo[] toProperties = _toProperties;
FieldInfo[] toFields = _toFields;
if (fromProperties != null && fromProperties.Any()) {
foreach (PropertyInfo fromProperty in fromProperties) {
if (toProperties.Any(x => x.Name == fromProperty.Name)) {
PropertyInfo destinationProperty = toProperties.FirstOrDefault(x => x.Name == fromProperty.Name);
if (MatchingProps(fromProperty, destinationProperty)) {
object val = fromProperty.GetValue(objFrom, null);
if (Equals(val, null)) continue;
if (!Equals(destinationProperty, null)) destinationProperty.SetValue(objTo, Convert.ChangeType(val, fromProperty.PropertyType), null);
}
}
if (toFields.Any(x => x.Name == fromProperty.Name)) {
FieldInfo destinationField = toFields.FirstOrDefault(x => x.Name == fromProperty.Name);
if (MatchingPropertyToField(fromProperty, destinationField)) {
object val = fromProperty.GetValue(objFrom, null);
if (Equals(val, null)) continue;
if (!Equals(destinationField, null)) destinationField.SetValue(objTo, val);
}
}
}
}
}
private static void SyncFields<TFromType, TOType>(TFromType objFrom, TOType objTo)
{
FieldInfo[] fromFields = _fromFields;
FieldInfo[] toFields = _toFields;
PropertyInfo[] toProperties = _toProperties;
if (fromFields != null && fromFields.Any()) {
foreach (FieldInfo fromField in fromFields) {
if (toFields.Any(x => x.Name == fromField.Name)) {
FieldInfo destinationField = toFields.FirstOrDefault(x => x.Name == fromField.Name);
if (MatchingFields(fromField, destinationField)) {
object val = fromField.GetValue(objFrom);
if (Equals(val, null)) continue;
if (!Equals(destinationField, null)) destinationField.SetValue(objTo, val);
}
}
if (toProperties.Any(x => x.Name == fromField.Name)) {
PropertyInfo destinationProperty = toProperties.FirstOrDefault(x => x.Name == fromField.Name);
if (MatchingFieldToProperty(fromField, destinationProperty)) {
object val = fromField.GetValue(objFrom);
if (Equals(val, null)) continue;
if (!Equals(destinationProperty, null)) destinationProperty.SetValue(objTo, val, null);
}
}
}
}
}
}
Usage:
static void Main()
{
dynamic o = new ExpandoObject();
o.Name = "Pouce";
o.Age = 42;
o.Rank = new Rank
{
Name = Ranks.Major
};
o.Guid = new Guid();
Soldier soldier = new Soldier();
Mapper.AddMap<ExpandoObject, Soldier>();
Mapper.Map(o, soldier);
Console.ReadLine();
}
public class Soldier
{
public string Name { get; set; }
public int Age { get; set; }
public Rank Rank { get; set; }
public Guid Guid { get; set; }
}
public class Rank
{
public Ranks Name { get; set; }
}
public enum Ranks
{
Private,
Specialist,
Corporal,
Sergeant,
Captain,
Major,
Colonel,
General
}
[Q] : How could I use my mapper (presented above) to map from the
dynamic ExpandoObject.
[P] : The problem is that it works perfectly in normal <object,
object> mapping; However when provided with <ExpandoObject, object>
it does not map anything.
This is because the properties of ExpandoObject are not real .NET properties. By using ExpandoObject with dynamic keyword you are able to give any arbitrary properties to the object and this is handled by the DLR at runtime.
You cannot use the regular static type's methods GetProperties() and GetFields() on the dynamic instance of ExpandoObject.
To extend your Mapper to consume ExpandObject you will have to consider it as a special case.
See my answer here it may help you.
EDIT: Reflection with ExpandoObject is not difficult. However, you wont get a set of PropertyInfo or FieldInfo from it. You just get KeyValuePair<string, object>. So, you may have to add an array of such KeyValuePair's to store the info.
In your Map() method, you can check for ExpandoObject as special case:
if (tFrom == typeof(ExpandoObject)) {
_fromExpandoProperties = #from.Select(kvp => kvp).ToArray();
// where _fromExpandoProperties is of type KeyValuePair<string, object>[]
} else {
_fromProperties = tFrom.GetProperties();
}
To get the name and value of the property you can then use .Key and .Value instead of .PropertyType.Name and .GetValue().
You will have to factor this specialization in all your code.
I have to set a property inside an unknown object. The structure looks like this:
ObjA.ObjB().ObjC.PropA = propValue;
ObjA is from a referenced class. ObjB() is of type object and therefore ObjC is unknown. I thought about using Reflection but don't know how to use it correctly in this case.
object objB = ObjA.ObjB();
Type objBType = objB.GetType();
System.Reflection.XXXInfo objCInfo = objBType.GetXXX("ObjC");
Type objCType = objCInfo.GetType();
System.Reflection.PropertyInfo PropAInfo = objCType.GetProperty("PropA");
PropAInfo.SetValue(PropAInfo, propValue, null);
Answer (Thanks to BigM):
dynamic objAB = ObjA.ObjB();
objAB.ObjC.PropA = propValue;
This should probably work for you.
object objB = ObjA.ObjB();
Type objBType = objB.GetType();
System.Reflection.PropertyInfo objCInfo = objBType.GetProperty("ObjC");
object val = objCInfo.GetValue(objB);
Type objCType = val.GetType();
System.Reflection.PropertyInfo PropAInfo = objCType.GetProperty("PropA");
PropAInfo.SetValue(val, propValue, null);
However, I think a bit of re-architecting could be done here to make life a bit easier. For example, if you don't know anything about the types then you might consider using dynamic and returning dynamic types from ObjC and PropA - but there is a performance hit there.
On the other hand, if there is any way that you can use generics, that would make your life a lot easier. For example, the code here that sets the property value, if that method were generic it might likely be able to define the type of ObjC - but I can't really infer that with the current snippet.
Here are a couple of generic extension methods to help you get and set "unknown" properties by name:
public static class ReflectionHelpers
{
public static bool TrySetProperty<TValue>(this object obj, string propertyName, TValue value)
{
var property = obj.GetType()
.GetProperties()
.Where(p => p.CanWrite && p.PropertyType == typeof(TValue))
.FirstOrDefault(p => p.Name == propertyName);
if (property == null)
{
return false;
}
property.SetValue(obj, value);
return true;
}
public static bool TryGetPropertyValue<TProperty>(this object obj, string propertyName, out TProperty value)
{
var property = obj.GetType()
.GetProperties()
.Where(p => p.CanRead && p.PropertyType == typeof(TProperty))
.FirstOrDefault(p => p.Name == propertyName);
if (property == null)
{
value = default(TProperty);
return false;
}
value = (TProperty) property.GetValue(obj);
return true;
}
}
And a usage example:
public class Program
{
public static void Main()
{
var foo = new Foo
{
Bar = new Bar
{
HelloReflection = "Testing"
}
};
string currentValue;
if (foo.Bar.TryGetPropertyValue("HelloReflection", out currentValue))
{
Console.WriteLine(currentValue); // "Testing"
}
if (foo.Bar.TrySetProperty("HelloReflection", "123..."))
{
foo.Bar.TryGetPropertyValue("HelloReflection", out currentValue)
Console.WriteLine(currentValue); // "123.."
}
else
{
Console.WriteLine("Failed to set value");
}
}
}
public class Foo
{
public object Bar { get; set; }
}
public class Bar
{
public string HelloReflection { get; set; }
}
I have multiple large objects which each have about 60 strings. I have to trim all those strings, and I'd like to do so without having to go this.mystring = this.mystring.Trim(). Instead, I'm looking for a way to automatically have each object discover its own strings and then perform the operation.
I know a little bit about reflection, but not enough, but I think this is possible?
Also, I'm not sure if this matters, but some string properties are read-only (only have a getter), so those properties would have to be skipped.
Help?
Well, it's easy enough to get all the properties, and find out which ones are strings and writable. LINQ makes it even easier.
var props = instance.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
// Ignore non-string properties
.Where(prop => prop.PropertyType == typeof(string))
// Ignore indexers
.Where(prop => prop.GetIndexParameters().Length == 0)
// Must be both readable and writable
.Where(prop => prop.CanWrite && prop.CanRead);
foreach (PropertyInfo prop in props)
{
string value = (string) prop.GetValue(instance, null);
if (value != null)
{
value = value.Trim();
prop.SetValue(instance, value, null);
}
}
You may want to only set the property if trimming actually makes a difference, to avoid redundant computations for complex properties - or it may not be an issue for you.
There are various ways of improving the performance if necessary - things like:
Simply caching the relevant properties for each type
Using Delegate.CreateDelegate to build delegates for the getters and setters
Possibly using expression trees, although I'm not sure whether they'd help here
I wouldn't take any of those steps unless performance is actually a problem though.
Something like:
foreach (PropertyInfo prop in obj.GetType().GetProperties(
BindingFlags.Instance | BindingFlags.Public))
{
if (prop.CanRead && prop.CanWrite && prop.PropertyType == typeof(string)
&& (prop.GetIndexParameters().Length == 0)) // watch for indexers!
{
var s = (string)prop.GetValue(obj, null);
if (!string.IsNullOrEmpty(s)) s = s.Trim();
prop.SetValue(obj, s, null);
}
}
Not necessary to make IEnumerable check in the props-loop and if actual instance is a IEnumerable, props are ignored. Fix for IEnumerable part:
private void TrimWhitespace(object instance)
{
if (instance != null)
{
if (instance is IEnumerable)
{
foreach (var item in (IEnumerable)instance)
{
TrimWhitespace(item);
}
}
var props = instance.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
// Ignore indexers
.Where(prop => prop.GetIndexParameters().Length == 0)
// Must be both readable and writable
.Where(prop => prop.CanWrite && prop.CanRead);
foreach (PropertyInfo prop in props)
{
if (prop.GetValue(instance, null) is string)
{
string value = (string)prop.GetValue(instance, null);
if (value != null)
{
value = value.Trim();
prop.SetValue(instance, value, null);
}
}
else
TrimWhitespace(prop.GetValue(instance, null));
}
}
}
So to expand on this a little, I have a complex object with Lists of Lists and I wanted to traverse that and trim all of the child string objects as well. I'm posting what I did as of what I built on from #Jon did in his answer. I'm curious if there was a better way to do it or if I missed something obvious.
The objects I have are more complex than this but it should illustrate what I was trying.
public class Customer
{
public string Name { get; set; }
public List<Contact> Contacts { get; set; }
}
public class Contact
{
public string Name { get; set; }
public List<Email> EmailAddresses {get; set;}
}
public class Email
{
public string EmailAddress {get; set;}
}
private void TrimWhitespace(object instance)
{
if (instance != null)
{
var props = instance.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
// Ignore indexers
.Where(prop => prop.GetIndexParameters().Length == 0)
// Must be both readable and writable
.Where(prop => prop.CanWrite && prop.CanRead);
foreach (PropertyInfo prop in props)
{
if (instance is IEnumerable)
{
foreach (var item in (IEnumerable)instance)
{
TrimWhitespace(item);
}
}
else if (prop.GetValue(instance, null) is string)
{
string value = (string)prop.GetValue(instance, null);
if (value != null)
{
value = value.Trim();
prop.SetValue(instance, value, null);
}
}
else
TrimWhitespace(prop.GetValue(instance, null));
}
}
}
Thoughts?