I am working on an MVC project and would like to pass the Html.TextboxFor method the name of a property. This is my viewmodel
public class RuleViewModel<T> where T : class, IValidatableObject
{
private T _rule;
public T RuleModel
{
get
{
return _rule
?? (_rule = Activator.CreateInstance<T>());
}
}
public RuleMetadata Metadata { get; set; }
public Expression<Func<RuleViewModel<T>, Object>> GetParameterByName(PropertyInfo pi)
{
var fieldName = Expression.Parameter(typeof(RuleViewModel<T>), "x");
var fieldExpression = Expression.PropertyOrField(Expression.Property(fieldName, "RuleModel"), pi.Name);
var exp = Expression.Lambda<Func<RuleViewModel<T>, Object>>(fieldExpression, fieldName);
return exp;
}
}
Then in the view I do this
#foreach (var prop in Model.RuleModel.GetType().GetProperties())
{
var result = Model.Metadata.Columns.SingleOrDefault(m => m.ColumnName == prop.Name);
if (result != null)
{
<td>
#Html.TextBoxFor(Model.GetParameterByName(prop))
</td>
}
}
The problem is that when the property is of type decimal?, I get a cannot convert nullable decimal to object error. I looked around and found that you can use Expression.Convert to fix this but when I do that I get an error on the view
Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.
Any help would be appreciated. This is a proof of concept project that I'm working on and without this piece it is dead in the water.
The problem is that you can't just use object as TProperty when calling TextBoxFor<TModel, TProperty>(). It expects a lambda expression of the form Func<TModel, TProperty>, and the variance rules of C# are such that a Func<TModel, decimal?> is not assignment compatible with Func<TModel, object>. You also can't simply use Convert(), because the MVC internals won't accept a lambda whose body is a Convert expression.
What you can do is use dynamic binding to invoke TextBoxFor<TModel, TProperty>() with the correct type arguments:
public Expression GetParameterByName(PropertyInfo pi)
{
var fieldName = Expression.Parameter(typeof(RuleViewModel<T>), "x");
var fieldExpression = Expression.PropertyOrField(
Expression.Property(fieldName, "RuleModel"),
pi.Name);
var exp = Expression.Lambda(
typeof(Func<,>).MakeGenericType(typeof(RuleViewModel<T>), fieldExpression.Type),
fieldExpression,
fieldName);
return exp;
}
// ...
#InputExtensions.TextBoxFor(Html, (dynamic)Model.GetParameterByName(prop))
Related
I am defining a collection of mappings in code at design time and then executing those mappings at run time after doing some data extraction :
public class FormExtractionMap<T>
{
public Expression<Func<T>> Destination { get; set; }
public string Source { get; set; }
}
Mapping Code:
var extractionRequest = new ExtractionRequest<PlanningApplication>
{
Mapping = new List<FormExtractionMap<PlanningApplication>>
{
new FormExtractionMap<PlanningApplication> {Destination = x => x.Site.Address.MapCoordinate.Eastings, Source = "site_address_easting"},
new FormExtractionMap<PlanningApplication> {Destination = x => x.Site.Address.MapCoordinate.Northings, Source = "site_address_northing"},
}
};
and then I'm looking through each of the mapping expressions to go and get the Source value (any Type) and then assign it to the Destination Expression (any Type).
foreach (var extractionMap in extractionRequest.Mapping)
{
extractionRequest.ExtractTo.Set(extractionMap.Destination, form.GetValue(extractionMap.Source));
}
I then have Expression extensions to create a setter, compile and do the property assignment.
public static TEntity Set<TEntity, TProperty>(
this TEntity obj,
Expression<Func<TEntity, TProperty>> selector,
TProperty value)
{
var setterExpr = CreateSetter(selector);
setterExpr.Compile()(obj, value);
return obj;
}
private static Expression<Action<TEntity, TProperty>> CreateSetter<TEntity, TProperty>(Expression<Func<TEntity, TProperty>> selector)
{
ParameterExpression valueParameterExpression = Expression.Parameter(typeof(TProperty), "value");
Expression targetExpression = selector.Body is UnaryExpression ? ((UnaryExpression)selector.Body).Operand : selector.Body;
var newValue = Expression.Parameter(selector.Body.Type);
return Expression.Lambda<Action<TEntity, TProperty>>
(
Expression.Assign(targetExpression, Expression.Convert(valueParameterExpression, targetExpression.Type)),
selector.Parameters.Single(),
valueParameterExpression
);
}
if Source is a string and the Destination is a string the value is assigned fine. If the Destination is a double on setterExpr.Compile()(obj, value); I get :
System.InvalidCastException : Unable to cast object of type
'System.String' to type 'System.Double'.
I thought the "Expression.Convert" was handling the type conversion but clearly not. What am I doing wrong please ?
So, I did eventually solve this. Expression.Convert is akin to an explicit cast (Foo)Bar, not as "Convert" implies. I ended up with:
private static Expression<Action<TEntity, TProperty>> CreateSetter<TEntity, TProperty>(
Expression<Func<TEntity, TProperty>> selector, Type valueParameterType)
{
ParameterExpression valueParameterExpression = Expression.Parameter(typeof(object), "value");
Expression targetExpression = selector.Body is UnaryExpression ? ((UnaryExpression)selector.Body).Operand : selector.Body;
var resultBody = ConvertToDestination(valueParameterExpression, valueParameterType, targetExpression);
return Expression.Lambda<Action<TEntity, TProperty>>
(
Expression.Assign(targetExpression, resultBody),
selector.Parameters.Single(),
valueParameterExpression
);
}
private static Expression ConvertToDestination(ParameterExpression valueParameterExpression, Type valueParameterType, Expression targetExpression)
{
if (valueParameterType == typeof(string))
{
switch (targetExpression.Type)
{
case Type _ when targetExpression.Type == typeof(double):
return Expression.Call(typeof(Convert), "ToDouble", null, valueParameterExpression);
case Type _ when targetExpression.Type == typeof(int):
return Expression.Call(typeof(Convert), "ToInt", null, valueParameterExpression);
default:
return Expression.Convert(valueParameterExpression, targetExpression.Type);
}
}
return Expression.Convert(valueParameterExpression, targetExpression.Type);
}
However, I thought it was messy, verbose and frankly unnecessary as I was able to implement similar functionality using AutoMapper in a few hours. Automapper does a better job of type conversion, caching the maps etc. So the real solution was re-factor and don't re-invent the wheel.
I have some properties in many classes and want to compile getter/setter of them into lambda expressions so that I can use reflections with better performance.
(Profiled, and using Reflection's getValue/setValue takes up about 78% of total running time...)
However It seems that Expression.Lambda() Only supports one collection of parameter and will not convert parameter type automatically.
using Exp = System.Linq.Expressions.Expression;
...
public class A { public int propA { get; set; } }
public class B { public int propB { get; set; } }
static Func<object, int> BuildFunc(Type type, string propName)
{
var param = Exp.Parameter(prop.DeclaringType, "x");
var exBody = Exp.Call(param, prop.GetGetMethod());
return Exp.Lambda<Func<object, int>>(exBody, param).Compile();
}
...
var a = new A();
var b = new B();
var fA = BuildFunc(typeof(A).GetProperty("propA"));
var fB = BuildFunc(typeof(B).GetProperty("propB"));
fA(a);
fB(b);
It will thorw an exception:
ParameterExpression of type __Main__+A cannot be used for delegate parameter of type System.Object
If I change the expression into Exp.Lambda<Func<A, int>>(...) it will work with class A but will not work with class B.
If I use Expression.Convert to convert types, it will throw ArgumentException that tells me the method cannot invoke on instance of System.Object.
So what can I do to compile this expression just like below, which supports any type of object and corresponding method?
lambda = (object obj, MethodInfo method, ...) => { method.Invoke(obj, ...) }
There are better expression APIs available for invoking property getter/setter methods. You definitely don't have to resort to MethodInfo.Invoke.
The conversion from/to object is handled by Expression.Convert. You just need to plug it in the right spot.
Here is a getter/setter delegate compilation example where T is the type of your container (A or B in your example).
static Func<T, object> CompileGetter<T>(PropertyInfo property)
{
// Target expression: (T obj) => (object)obj.Property;
ParameterExpression objParam = Expression.Parameter(typeof(T), "obj");
Expression body = Expression.Convert(Expression.MakeMemberAccess(objParam, property), typeof(object));
return Expression
.Lambda<Func<T, object>>(body, objParam)
.Compile();
}
static Action<T, object> CompileSetter<T>(PropertyInfo property)
{
// Target expression: (T obj, object value) => obj.Property = (TProperty)value;
ParameterExpression objParam = Expression.Parameter(typeof(T), "obj");
ParameterExpression valueParam = Expression.Parameter(typeof(object), "value");
Expression body = Expression.Assign(
Expression.MakeMemberAccess(objParam, property),
Expression.Convert(valueParam, property.PropertyType)
);
return Expression
.Lambda<Action<T, object>>(body, objParam, valueParam)
.Compile();
}
Proof:
class Dummy
{
public int Value { get; set; }
}
...
PropertyInfo prop = typeof(Dummy).GetProperty("Value");
Func<Dummy, object> getter = CompileGetter<Dummy>(prop);
Action<Dummy, object> setter = CompileSetter<Dummy>(prop);
Dummy d = new Dummy { Value = 123 };
Assert.AreEqual(123, getter(d));
setter(d, 321);
Assert.AreEqual(321, d.Value);
Please note: LambdaExpression.Compile is an extremely CPU-intensive operation, so once you create your getter/setter delegate, you must cache it in order to get that performance boost you're looking for.
I am new to c# and lambda expressions; in this tutorial I cannot understand what the code does with this Lambda expression:
public ViewModel()
{
base.AddRule(() => Aid, () =>
Aid.Length >= (5 * 2) &&
Aid.Length <= (16 * 2) &&
Aid.Length % 2 == 0, "Invalid AID.");
}
And this is the AddRule Method which the tutorial says it adds the rule to the rule dictionary:
public void AddRule<T>(Expression<Func<T>> expression, Func<bool> ruleDelegate, string errorMessage)
{
var name = GetPropertyName(expression);
ruleMap.Add(name, new Binder(ruleDelegate, errorMessage));
}
and
protected static string GetPropertyName<T>(Expression<Func<T>> expression)
{
if (expression == null)
throw new ArgumentNullException("expression");
Expression body = expression.Body;
MemberExpression memberExpression = body as MemberExpression;
if (memberExpression == null)
{
memberExpression = (MemberExpression)((UnaryExpression)body).Operand;
}
return memberExpression.Member.Name;
}
}
What does the () => Aid mean and why the addrule receives it and cast it as UnaryExpression and MemberExpression ?
A MemberExpression is an expression on an instance or a static expression for a MemberInfo.
An UnaryExpression is an expression that revolves around a target and a single operator (see definition for the term unary). One good example of such an operator is a cast. Another is an operation to box or unbox a value type.
So why does the example to get a MemberInfo need to handle an UnaryExpression?
Consider
public class Foo
{
public String RefType { get; set; }
public int ValueType { get; set; }
}
and some code
Expression<Func<Foo, object>> getter;
getter = f => f.RefType // compiler emits MemberExpression for RefType on a Foo instance
getter = f => f.ValueType // compiler emits an UnaryExpression to Box a MemberExpression for ValueType on a Foo instance.
() => Aid is shorthand for () => { return Aid; }, which returns a property (or field - you didn't show its declaration). Normally this results in an anonymous function.
But because the AddRule method takes an Expression<Func<T>> instead of a Func<T>, the compiler creates instructions that create an AST (abstract syntax tree) instead of an anonymous function. An AST can be compiled into a method, but here it's only used to extract the name of the property/field.
The alternative is to pass the property name as a string. The advantage of using a member expression is that it's refactor-friendly: renaming the property will also update this code, ensuring that the rule is still associated with the right property.
This is what is actually being passed into AddRule (you can do this manually, but you'd lose the refactor-friendly benefit - besides, () => Aid is a lot more concise):
Expression.Lambda<Func<T>>(
Expression.Property(
Expression.Constant(this),
"Aid"));
Aid is a property of the viewmodel used in that example:
public string Aid
{
get { return Get(() => Aid); }
set { Set(() => Aid, value); }
}
You can check this if you download the entire project from that article (link is on the top)
When you pass a property to a lambda expression, it will be represented by MemberExpression. This class is dedicated for this as described in MemberExpression Class
I want to be able to build up an expression dynamically, which is essentially a property selector.
I am trying to use this so I can provide a flexible search UI and then translate the selected search parameters to an Entity Framework query.
I have most of what I need thanks to another library I am using, but am missing the final part which translates my query string parameters to the appropriate expression selector the other library requires.
The library takes an argument of :
Expression<Func<TObject, TPropertyType>>
An example of how this would be coded if baked into an application would be :
Expression<Func<MyObject, int>> expression = x=> x.IntegerProperty;
However, I need to be able to generate this expression dynamically, as the important point is that all I will know is the type of object (MyObject) and the property name as a string value ("IntegerProperty"). The property value will obviously map to an property on the object which could be of any non complex type.
So essentially I think I am wanting to find a way to build up the expression dynamically which specifies the correct object property to return and where the return value is determined by that property type.
psuedo code :
string ObjectPropertyName
Type ObjectType
Type ObjectPropertyType = typeof(ObjectType).GetProperty(ObjectPropertyName).Property
Expression<Func<[ObjectType], [ObjectPropertyType]>> expression = x=> x.[ObjectPropertyName];
Update :
I have got as far as this
ParameterExpression objectParameter = Expression.Parameter(type, "x");
MemberExpression objectProperty = Expression.Property(objectParameter, "PropertyNameString");
Expression<Func<ObjectType, int>> expression = Expression.Lambda<Func<ObjectType, int>>(objectProperty, objectParameter);
But the problem I have with this is that the return type is not always an int but may be some other type.
Doing what you asked is bit tricky but not impossible. Since the property type is not known until run time so you can not declare the Expression<Func<,>> so it would be done by reflection.
public static class QueryableExtension
{
public static object Build<Tobject>(this Tobject source, string propertyName)
{
var propInfo = typeof(Tobject).GetProperty(propertyName);
var parameter = Expression.Parameter(typeof(Tobject), "x");
var property = Expression.Property(parameter, propInfo);
var delegateType = typeof(Func<,>)
.MakeGenericType(typeof(Tobject), propInfo.PropertyType);
var lambda = GetExpressionLambdaMethod()
.MakeGenericMethod(delegateType)
.Invoke(null, new object[] { property, new[] { parameter } });
return lambda;
}
public static MethodInfo GetExpressionLambdaMethod()
{
return typeof(Expression)
.GetMethods()
.Where(m => m.Name == "Lambda")
.Select(m => new
{
Method = m,
Params = m.GetParameters(),
Args = m.GetGenericArguments()
})
.Where(x => x.Params.Length == 2
&& x.Args.Length == 1
)
.Select(x => x.Method)
.First();
}
}
Usage -
var expression = testObject.Build("YourPropertyName");
Now this will build the Expression you desired with return type of property. But since we don't know about your library but I suggest you to call your library method via reflection and pass the expression wrapped under object.
As I mentioned in the comments, building expression without knowing the property type is easy (even with nested property support):
static LambdaExpression MakeSelector(Type objectType, string path)
{
var item = Expression.Parameter(objectType, "item");
var body = path.Split('.').Aggregate((Expression)item, Expression.PropertyOrField);
return Expression.Lambda(body, item);
}
But then you'll need to find a way to call your generic library method - using reflection or dynamic call.
If you have both ObjectType and ObjectPropertyType as generic type parameters, you can use the Expression class to do something like this:
public static Expression<Func<TObject, TPropertyType>> Generate<TObject, TPropertyType>(
string property_name)
{
var parameter = Expression.Parameter(typeof (TObject));
return Expression.Lambda<Func<TObject, TPropertyType>>(
Expression.Property(parameter, property_name), parameter);
}
There is old intresting library DynamicLinq. May be it will be useful for you. It extends System.Linq.Dynamic to support execution of Lambda expressions defined in a string. With use of DynamicLinq you can do somethink like:
public class IndexViewModel
{
public bool HasPassword { get; set; }
public string PhoneNumber { get; set; }
public bool TwoFactor { get; set; }
public bool BrowserRemembered { get; set; }
}
//...........
Expression<Func<IndexViewModel, bool>> ex =
System.Linq.Dynamic.DynamicExpression.ParseLambda<IndexViewModel, bool>("TwoFactor");
var model = new ReactJs.NET.Models.IndexViewModel() { TwoFactor = true };
var res = ex.Compile()(model);
// res == true
System.Diagnostics.Debug.Assert(res);
Suppose I have:
class Person
{
[ColumnAttribute("ID"]
public int Id;
[ColumnAttribute("Name"]
public string Name;
[ColumnAttribute("DateOfBirth"]
public date BirthDate;
}
i want to create a method that will be called as following
GetPropertyColumn<Person>(e=>e.Name)
this method will return a string that is defined by the ColumnAttribute attribute.
the problem is that i am not able to define this method.
i tried
public string GetPropertyColumn<T,U>(Expression<Func<T, U>> Lamda)
but the problem is that i can only specify the T and not the U so its not working.
any help?
thanks
Edit:
thanks for the answers but i got a lot of answers in which you need to instantinate Person but i dont want to.
because i only want to know the column given a property defined inside a Class.
If you have a generic method with 2 generic types (T and U) then both most be specified, or both most be inferred. If that isn't possible, consider an expression taking Func<T,object> (remove U), and remove the cast/convert node from the expression tree when inspecting it at runtime. You can also do things to make both types inferred, but that may need more of a refactor.
[EDIT 3]
public static string GetPropertyColumn<T>(Expression<Func<T,object>> f)
{
Type t = typeof(T);
MemberExpression memberExpression = null;
if (f.Body.NodeType == ExpressionType.Convert)
{
memberExpression = ((UnaryExpression)f.Body).Operand as MemberExpression;
}
else if (f.Body.NodeType == ExpressionType.MemberAccess)
{
memberExpression = f.Body as MemberExpression;
}
string name = memberExpression.Member.Name;
System.Reflection.FieldInfo fi = t.GetField(name);
object[] attrs = fi.GetCustomAttributes(true);
foreach (var attr in attrs)
{
ColumnAttribute columnAttr = attr as ColumnAttribute;
if (columnAttr != null)
{
return columnAttr.Name;
}
}
return string.Empty;
}
using:
Console.WriteLine(GetPropertyColumn<Person>(e => e.Name));
Make it an extension method:
static class ExtensionMethods
{
public static string GetPropertyColumn<T,U>(this T obj, Expression<Func<T, U>> selector)
{
... // whatever
}
}
And use it as follows:
Person person = ...
string propertyColumn = person.GetPropertyColumn(p => p.Name);
You don't need to specify T, because it's inferred from the first parameter, and you don't need to specify U either because it is inferred from the return type of the lambda
Note that you don't need to define it as an extension method, it could also be a normal method. You would then use it like that:
Person person = ...
string propertyColumn = GetPropertyColumn(person, p => p.Name);