Как заданное значение: селектор свойств Expression <Func <T, TResult >>
Мне нужно связать свойство объекта entity в объекте класса Person с выражением linq в моем классе FactoryEntities с использованием шаблона factory, посмотрите, что у меня есть, и я хочу сделать:
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*/
}
}
Ответы
Ответ 1
Это работает:
Следующий вспомогательный метод преобразует выражение-геттер в делегат сеттера. Если вы хотите вернуть Expression<Action<T,TProperty>>
вместо Action<T,TProperty>
, просто не вызывайте метод Compile()
в конце.
Примечание: Код из блога Ian Mercer: 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();
}
Ответ 2
Вы можете установить свойство следующим образом:
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);
}
Это будет работать только для свойств, а не для полей, хотя добавление поддержки полей должно быть легким.
Но код, который у вас есть для получения человека, не будет работать. Если вы хотите сохранить возвращаемый тип void
AssociateWithEntity()
, вы можете сделать это следующим образом:
var factory = new FactoryEntity<Person>();
factory.AssociateWithEntity(p => p.Address, address);
Person person = factory.InstanceEntity;
Другим вариантом является свободный интерфейс:
Person person = new FactoryEntity<Person>()
.AssociateWithEntity(p => p.Address, address)
.InstanceEntity;
Ответ 3
Другим решением является получение владельцем собственности ant invoke set setter с использованием отражения. Преимущество этого решения в том, что он не использует методы расширения и может быть вызван любым типом
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);
Ответ 4
Что идея, я работаю для меня с этим кодом, принимая во внимание вклад 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);
}
}
Здесь я вызываю factory для меня, чтобы создать объект Person в действительном
Person person = new FactoryEntity<Person>().AssociateWithEntity(p=>p.Address, address).Build();
Но я не знаю, является ли этот код оптимальным или нет, по крайней мере, я не вызываю метод compile(), что говорят?
спасибо
Ответ 5
Я сделал смешанное решение Rytis I и fooobar.com/info/121612/...
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);
}
И назовите его
SetPropertyValue(() => myStuff.MyProp, newValue);