Skip to content

Instantly share code, notes, and snippets.

@nazrhyn
Created April 23, 2012 21:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nazrhyn/31e4c46cdfbd9865f5a0 to your computer and use it in GitHub Desktop.
Save nazrhyn/31e4c46cdfbd9865f5a0 to your computer and use it in GitHub Desktop.
/// <summary>
/// Registers a property with an initial value and a changed value.
/// </summary>
/// <typeparam name="TProperty">The type of the property.</typeparam>
/// <param name="property">A lambda expression indicating what property to register to.</param>
/// <param name="initialValue">The initial value of the property.</param>
/// <param name="changedValue">The value to change the property to.</param>
protected void RegisterPropertyMutation<TProperty>(Expression<Func<TEntity, TProperty>> property, TProperty initialValue, TProperty changedValue)
{
// check property expression composition
ValidatePropertyExpression(property);
// Func<TEntity, TProperty> guarantees one parameter to the lambda
ParameterExpression getEntityParam = property.Parameters[0];
// construct the equivalent of this -or- this
// object Method(TEntity entity) e => (object)e.Property;
// {
// return (object)entity.Property;
// }
Expression<Func<TEntity, object>> boxedPropertyGet = Expression.Lambda<Func<TEntity, object>>(
Expression.Convert( // performs the cast from TProperty to object; the result of this is implicitly returned (one-line lambda)
property.Body,
typeof(object)
),
getEntityParam // uses the same parameter as before
);
// create the parameters for the Action<TEntity, object>
ParameterExpression setEntityParam = property.Parameters[0]; // we have to re-use this one as we're re-using the body
ParameterExpression setBoxedValueParam = Expression.Parameter(typeof(object), "setBoxedValueParam"); // the input is now object
// construct the equivalent of
// void Method(TEntity entity, object value)
// {
// entity.Property = (TProperty)value;
// }
Expression<Action<TEntity, object>> boxedPropertySet = Expression.Lambda<Action<TEntity, object>>(
Expression.Assign( // performs the assign from the cast value to the property access
property.Body, // the property access copied from the original
Expression.Convert( // performs the convert from the object input to the type of the property
setBoxedValueParam,
typeof(TProperty)
)
), // the lambda takes two parameters
setEntityParam, // the entity, as before
setBoxedValueParam // and the value, boxed to object
);
RegisterPropertyMutation(
new PropertyValueMutation
{
PropertyName = GetFullPropertyPath(property),
PropertyGet = boxedPropertyGet.Compile(),
PropertySet = boxedPropertySet.Compile(),
InitialValue = initialValue,
ChangedValue = changedValue
}
);
}
/// <summary>
/// Registers a property with an initial value and a changed value and a method by which the property should be assigned.
/// </summary>
/// <typeparam name="TProperty">The type of the property.</typeparam>
/// <param name="property">A lambda expression indicating what property to register to.</param>
/// <param name="initialValue">The initial value of the property.</param>
/// <param name="changedValue">The value to change the property to.</param>
/// <param name="propertySet">An expression that will be used to assign the property value in a non-standard way.</param>
protected void RegisterPropertyMutation<TProperty>(Expression<Func<TEntity, TProperty>> property, TProperty initialValue, TProperty changedValue, Expression<Action<TEntity, TProperty>> propertySet)
{
// check property expression composition
ValidatePropertyExpression(property);
// Func<TEntity, TProperty> guarantees one parameter to the lambda
ParameterExpression getEntityParam = property.Parameters[0];
// construct the equivalent of this -or- this
// object Method(TEntity entity) e => (object)e.Property;
// {
// return (object)entity.Property;
// }
Expression<Func<TEntity, object>> boxedPropertyGet = Expression.Lambda<Func<TEntity, object>>(
Expression.Convert( // performs the cast from TProperty to object; the result of this is implicitly returned (one-line lambda)
property.Body,
typeof(object)
),
getEntityParam // uses the same parameter as before
);
// Action<TEntity, TProperty> guarantees two parameters to the lambda
ParameterExpression setEntityParam = propertySet.Parameters[0];
ParameterExpression setTypedValueParam = propertySet.Parameters[1];
// create a new object parameter for the new lambda
ParameterExpression setBoxedValueParam = Expression.Parameter(typeof(object), "setBoxedValueParam");
// construct the equivalent of
// void Method(TEntity entity, object value)
// {
// TProperty typed = (TProperty)value;
// <original-body>(entity, typed);
// }
Expression<Action<TEntity, object>> boxedPropertySet = Expression.Lambda<Action<TEntity, object>>(
Expression.Block( // contains the variable assignment and the original body
new[] { setTypedValueParam }, // declares the typed variable as in scope for this body
Expression.Assign( // performs the assignment to the typed variable
setTypedValueParam,
Expression.Convert( // performs the cast from object to TProperty for assignment to the typed variable
setBoxedValueParam,
typeof(TProperty)
)
),
propertySet.Body // copies the old body that already uses the typed variable for the assignment
),
setEntityParam, // redefines the parameters to the action as the previous entity parameter
setBoxedValueParam // and a new object-type parameter that we un-box above
);
RegisterPropertyMutation(
new PropertyValueMutation
{
PropertyName = GetFullPropertyPath(property),
PropertyGet = boxedPropertyGet.Compile(),
PropertySet = boxedPropertySet.Compile(),
InitialValue = initialValue,
ChangedValue = changedValue
}
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment