Cómo se establece el valor de un selector de propiedad Expresión <Func >

Necesito asociar una Dirección de propiedad de entidad en mi entidad de clase Person con expresiones linq en mi clase FactoryEntities usando idea de fábrica de patrones, mira esto es lo que tengo y quiero hacer:

Address address = new Address(); address.Country = "Chile"; address.City = "Santiago"; address.ZipCode = "43532"; //Factory instance creation object //This is idea Person person = new FactoryEntity().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 where TEntity : Entity { public void AssociateWithEntity(Expression<Func> 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*/ } } 

Esto funciona:

El siguiente método de ayuda convierte una expresión getter en un delegado setter. Si desea devolver una Expression> lugar de una Action , simplemente no llame al método Compile() al final.

Nota: El código es del blog de Ian Mercer: http://blog.abodit.com/2011/09/convert-a-property-getter-to-a-setter/

  ///  /// Convert a lambda expression for a getter into a setter ///  public static Action GetSetter(Expression> 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>( Expression.Call(parameterT, setMethod, parameterTProperty), parameterT, parameterTProperty ); return newExpression.Compile(); } 

Puede establecer la propiedad de esta manera:

 public void AssociateWithEntity( Expression> 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); } 

Esto funcionará solo para las propiedades, no para los campos, aunque agregar soporte para los campos debería ser fácil.

Pero el código que tiene para obtener a la persona no funcionará. Si desea mantener el tipo de retorno void de AssociateWithEntity() , puede hacerlo así:

 var factory = new FactoryEntity(); factory.AssociateWithEntity(p => p.Address, address); Person person = factory.InstanceEntity; 

Otra opción es una interfaz fluida:

 Person person = new FactoryEntity() .AssociateWithEntity(p => p.Address, address) .InstanceEntity; 

Otra solución es conseguir que el dueño de la propiedad y el organizador de la propiedad de invocación utilicen la reflexión. La ventaja de esta solución es que no usa métodos de extensión y puede llamarse con cualquier tipo de

 private void SetPropertyValue(Expression> 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); 

Esa es la idea, estoy trabajando para mí con este código, teniendo en cuenta la contribución de svick:

 public class FactoryEntity 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 AssociateWithEntity(Expression> foreignEntity, TProperty instanceEntity) where TProperty : Entity { if (instanceEntity == null || instanceEntity.IsTransient()) throw new ArgumentNullException(); SetObjectValue(_Entity, foreignEntity, instanceEntity); return this; } private void SetObjectValue(object target, Expression> 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); } } 

Aquí llamo a la fábrica para que construya el objeto Person en una versión válida

 Person person = new FactoryEntity().AssociateWithEntity(p=>p.Address, address).Build(); 

Pero no sé si este código es óptimo o no, al menos no hago una llamada al método comstackr (), ¿qué están diciendo?

Gracias

He creado una solución mixta de Rytis I y https://stackoverflow.com/a/12423256/254109

 private static void SetPropertyValue(Expression> 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); } 

Y llámalo

 SetPropertyValue(() => myStuff.MyProp, newValue); 

Esta es mi solución que usa Expression.Assign , pero después de mirar más de cerca, la respuesta aceptada es igual de buena.

 // optionally or additionally put in a class to capture the object type once // and then you don't have to repeat it if you have a lot of properties public Action GetSetter( Expression> pExpression ) { var parameter1 = Expression.Parameter(typeof(T)); var parameter2 = Expression.Parameter(typeof(TProperty)); // turning an expression body into a PropertyInfo is common enough // that it's a good idea to extract this to a reusable method var member = (MemberExpression)pExpression.Body; var propertyInfo = (PropertyInfo)member.Member; // use the PropertyInfo to make a property expression // for the first parameter (the object) var property = Expression.Property(parameter1, propertyInfo); // assignment expression that assigns the second parameter (value) to the property var assignment = Expression.Assign(property, parameter2); // then just build the lambda, which takes 2 parameters, and has the assignment // expression for its body var setter = Expression.Lambda>( assignment, parameter1, parameter2 ); return setter.Compile(); } 

Otra cosa que puedes hacer es encapsularlos:

 public sealed class StrongProperty { [NotNull] readonly PropertyInfo mPropertyInfo; [NotNull] public string Name => mPropertyInfo.Name; [NotNull] public Func Get { get; } [NotNull] public Action Set { get; } // maybe other useful properties internal StrongProperty( [NotNull] PropertyInfo pPropertyInfo, [NotNull] Func pGet, [NotNull] Action pSet ) { mPropertyInfo = pPropertyInfo; Get = pGet; Set = pSet; } } 

Y ahora puede pasar estos, similar a los delegates, y escribir código cuya lógica puede variar según la propiedad. Esto evita el hecho de que no puede pasar propiedades por referencia.