可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
i need associate a entity property Address in my Person class entity with expressions linq in my FactoryEntities class using pattern factory idea, look this is 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*/
}
}
回答1:
This works:
The following helper method converts a getter expression into a setter delegate. If you want to return an Expression<Action<T,TProperty>>
instead of an Action<T,TProperty>
, just don't call the Compile()
method at the end.
Note: The code is from Ian Mercer's blog: 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:
You can set the property like this:
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 work only for properties, not fields, although adding support for fields should be easy.
But the code you have for getting the person won't work. If you want to keep the void
return type of AssociateWithEntity()
, you could do it like this:
var factory = new FactoryEntity<Person>();
factory.AssociateWithEntity(p => p.Address, address);
Person person = factory.InstanceEntity;
Another option is a fluent interface:
Person person = new FactoryEntity<Person>()
.AssociateWithEntity(p => p.Address, address)
.InstanceEntity;
回答3:
Another solution is to get property owner ant invoke property setter using reflection. The advantage of this solution that it does not use extension methods and can be call with 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);
回答4:
This is my solution that uses Expression.Assign
, but after looking more closely, the accepted answer is just as good.
// optionally or additionally put in a class<T> to capture the object type once
// and then you don't have to repeat it if you have a lot of properties
public Action<T, TProperty> GetSetter<T, TProperty>(
Expression<Func<T, TProperty>> 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<Action<T, TProperty>>(
assignment,
parameter1,
parameter2
);
return setter.Compile();
}
Another thing you can do is encapsulate them:
public sealed class StrongProperty<TObject, TProperty> {
readonly PropertyInfo mPropertyInfo;
public string Name => mPropertyInfo.Name;
public Func<TObject, TProperty> Get { get; }
public Action<TObject, TProperty> Set { get; }
// maybe other useful properties
internal StrongProperty(
PropertyInfo pPropertyInfo,
Func<TObject, TProperty> pGet,
Action<TObject, TProperty> pSet
) {
mPropertyInfo = pPropertyInfo;
Get = pGet;
Set = pSet;
}
}
And now you can pass these around, similar to delegates, and write code whose logic can vary by property. This gets around the fact that you can't pass properties by reference.
回答5:
That's the idea, i'm worked 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 the factory for me to build the 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 make a call to compile() method, what are saying?
thanks
回答6:
I've made mixed Rytis I solution and https://stackoverflow.com/a/12423256/254109
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 it
SetPropertyValue(() => myStuff.MyProp, newValue);