As setpoint: property selector Expression <Func <T, TResult >>
I need to associate a property of an entity object in an object of class Person with a linq expression in my FactoryEntities class using the factory pattern, look what I have and I want to do:
Address address = new Address(); address.Country = "Chile"; address.City = "Santiago"; address.ZipCode = "43532"; //Factory instance creation object //This is idea Person person = new FactoryEntity<Person>().AssociateWithEntity(p=>p.Address, address); public class Person: Entity { public string Name{ get; set; } public string LastName{ get; set; } public Address Address{ get; set; } } public class Address: Entity { public string Country{ get; set; } public string City{ get; set; } public string ZipCode{ get; set; } } public class FactoryEntity<TEntity> where TEntity : Entity { public void AssociateWithEntity<TProperty>(Expression<Func<TEntity, TProperty>> entityExpression, TProperty newValueEntity) where TProperty : Entity { if (instanceEntity == null || instanceEntity.IsTransient()) throw new ArgumentNullException(); /*TODO: Logic the association and validation How set the newValueEntity into the property of entityExpression (x=>x.Direccion = direccion*/ } } It works:
The following helper method converts a getter expression to a setter delegate. If you want to return Expression<Action<T,TProperty>> instead of Action<T,TProperty> , just do not call the Compile() method at the end.
Note: Ian Mercer blog code: http://blog.abodit.com/2011/09/convert-a-property-getter-to-a-setter/
/// <summary> /// Convert a lambda expression for a getter into a setter /// </summary> public static Action<T, TProperty> GetSetter<T, TProperty>(Expression<Func<T, TProperty>> expression) { var memberExpression = (MemberExpression)expression.Body; var property = (PropertyInfo)memberExpression.Member; var setMethod = property.GetSetMethod(); var parameterT = Expression.Parameter(typeof(T), "x"); var parameterTProperty = Expression.Parameter(typeof(TProperty), "y"); var newExpression = Expression.Lambda<Action<T, TProperty>>( Expression.Call(parameterT, setMethod, parameterTProperty), parameterT, parameterTProperty ); return newExpression.Compile(); } You can set the property as follows:
public void AssociateWithEntity<TProperty>( Expression<Func<TEntity, TProperty>> entityExpression, TProperty newValueEntity) where TProperty : Entity { if (instanceEntity == null) throw new ArgumentNullException(); var memberExpression = (MemberExpression)entityExpression.Body; var property = (PropertyInfo)memberExpression.Member; property.SetValue(instanceEntity, newValueEntity, null); } This will only work for properties, not fields, although adding field support should be easy.
But the code that you have to get the person will not work. If you want to save the return type of void AssociateWithEntity() , you can do it like this:
var factory = new FactoryEntity<Person>(); factory.AssociateWithEntity(p => p.Address, address); Person person = factory.InstanceEntity; Another option is a free interface:
Person person = new FactoryEntity<Person>() .AssociateWithEntity(p => p.Address, address) .InstanceEntity; Another solution is to get the property owner ant invoke set setter using reflection. The advantage of this solution is that it does not use extension methods and can be called by any type
private void SetPropertyValue(Expression<Func<object, object>> lambda, object value) { var memberExpression = (MemberExpression)lambda.Body; var propertyInfo = (PropertyInfo)memberExpression.Member; var propertyOwnerExpression = (MemberExpression)memberExpression.Expression; var propertyOwner = Expression.Lambda(propertyOwnerExpression).Compile().DynamicInvoke(); propertyInfo.SetValue(propertyOwner, value, null); } ... SetPropertyValue(s => myStuff.MyPropy, newValue); What an idea, I work for me with this code, taking into account the contribution of svick:
public class FactoryEntity<TEntity> where TEntity : Entity, new() { private TEntity _Entity; public FactoryEntity() { _Entity = new TEntity(); } public TEntity Build() { if (_Entity.IsValid()) throw new Exception("_Entity.Id"); return _Entity; } public FactoryEntity<TEntity> AssociateWithEntity<TProperty>(Expression<Func<TEntity, TProperty>> foreignEntity, TProperty instanceEntity) where TProperty : Entity { if (instanceEntity == null || instanceEntity.IsTransient()) throw new ArgumentNullException(); SetObjectValue<TEntity, TProperty>(_Entity, foreignEntity, instanceEntity); return this; } private void SetObjectValue<T, TResult>(object target, Expression<Func<T, TResult>> expression, TResult value) { var memberExpression = (MemberExpression)expression.Body; var propertyInfo = (PropertyInfo)memberExpression.Member; var newValue = Convert.ChangeType(value, value.GetType()); propertyInfo.SetValue(target, newValue, null); } } Here I call factory for me to create a Person object in a valid
Person person = new FactoryEntity<Person>().AssociateWithEntity(p=>p.Address, address).Build(); But I do not know if this code is optimal or not, at least I do not call the compile () method, what do they say?
thank
I made a mixed solution to Rytis I and stack overflow
private static void SetPropertyValue<T>(Expression<Func<T>> lambda, object value) { var memberExpression = (MemberExpression)lambda.Body; var propertyInfo = (PropertyInfo)memberExpression.Member; var propertyOwnerExpression = (MemberExpression)memberExpression.Expression; var propertyOwner = Expression.Lambda(propertyOwnerExpression).Compile().DynamicInvoke(); propertyInfo.SetValue(propertyOwner, value, null); } And call him
SetPropertyValue(() => myStuff.MyProp, newValue);