Trying to create an expression tree to do an object mapper type thing.
Type ts = typeof(Source);
Type td = typeof(Dest);
ParameterExpression val = Expression.Parameter(ts);
ParameterExpression ret = Expression.Parameter(td);
PropertyInfo[] propsS = ts.GetProperties();
PropertyInfo[] propsD = td.GetProperties();
List<Expression> lst = new List<Expression>();
foreach (PropertyInfo pi in propsS)
{
PropertyInfo piD = propsD.Where(x => x.Name == pi.Name).FirstOrDefault();
if (piD != null)
{
MethodInfo ge = pi.GetGetMethod();
MethodInfo se = piD.GetSetMethod();
var v1 = Expression.Call(val, ge);
var v2 = Expression.Call(ret, se, v1);
lst.Add(v2);
}
}
lst.Add(Expression.Return(Expression.Label(td), ret));
BlockExpression block = Expression.Block(
new[] { ret },
lst.ToArray()
);
//Func<Source, Dest> v = Expression.Lambda<Func<Source, Dest>>(block, val).Compile();
var v = Expression.Lambda(block, val);
So that's what I have now... its very close, but don't see what I'm missing...
v comes out to:
.Lambda #Lambda1<System.Action`1[ConsoleApplication2.Source]>(ConsoleApplication2.Source $var1) {
.Block(ConsoleApplication2.Dest $var2) {
.Call $var2.set_S1(.Call $var1.get_S1());
.Call $var2.set_S2(.Call $var1.get_S2());
.Call $var2.set_I1(.Call $var1.get_I1());
.Call $var2.set_I2(.Call $var1.get_I2());
.Call $var2.set_S3(.Call $var1.get_S3());
.Call $var2.set_S4(.Call $var1.get_S4());
.Call $var2.set_S5(.Call $var1.get_S5());
.Return #Label1 { $var2 }
}
}
Do I need to new up $var2 somewhere?
Is there a better way to do the assigns?
The lambda doesn't seem to see the return value...
Do I need to do the block? or is there a better way?
You can write something like this:
Type sourceType = typeof(Source);
ParameterExpression source = Expression.Parameter(sourceType);
var createModel = Expression.New(typeof(Dest));
var bindings = new List<MemberAssignment>();
foreach (var prop in sourceType.GetProperties())
{
var v1 = Expression.Call(source, prop.GetGetMethod());
var destinationProperty = typeof(Dest).GetProperty(prop.Name);
bindings.Add(Expression.Bind(destinationProperty, v1));
}
var init = Expression.MemberInit(createModel, bindings);
var lambdaExpression = Expression.Lambda<Func<Source, Dest>>(init, source);
Which will generate the following:
Param_0 => new Dest()
{
A = Param_0.get_A(),
B = Param_0.get_B()
}
And testing it:
var s = new Source { A = 5, B = "TEST" };
var res = lambdaExpression.Compile()(s);
Yields an object of Dest:
A 5
B TEST
Related
I'm trying to create some expression at runtime to change a given dictionary's values. I created this snippet which generates the expression successfully and compiles it an Action. But calling the action cannot modify dictionary's value, and also doesn't throw any error. Here is the code:
public class ChangeDicValue {
public void Change(IDictionary<string, object> dic) {
var blocks = MakeCleaningBlock(dic);
foreach (var block in blocks)
block.Invoke(dic);
}
private List<Action<IDictionary<string, Object>>> MakeCleaningBlock(IDictionary<string , object > dic) {
var allKeys = dic.Keys.ToArray();
var dicType = typeof(IDictionary<,>).MakeGenericType(typeof(string), typeof(object));
var dicContainsMethod = dicType.GetMethod("ContainsKey", new[] {typeof(string)})
?? throw new InvalidOperationException();
var actions = new List<Action<IDictionary<string, Object>>>();
ParameterExpression actionArguments =
Expression.Parameter(dicType, "actionArguments");
foreach (var k in allKeys) {
Expression key = Expression.Constant(k, typeof(string));
Expression target = Expression.Property(actionArguments, "Item", key);
var innerStatements = new List<Expression>(Changers);
var cleanStatements = new List<Expression>();
foreach (var ins in innerStatements) {
var assign = Expression.Assign(target, Expression.Block(ins, target));
cleanStatements.Add(assign);
}
Expression body1 = Expression.Block(new List<Expression>(cleanStatements) {target});
var callToContains = Expression.Call(actionArguments, dicContainsMethod, key);
var ifThenBody = Expression.IfThen(callToContains, body1);
var cleanedValueBlock = Expression.Block(target, ifThenBody, target);
var assignDic = Expression.Assign(target, cleanedValueBlock);
// see the debug view of assignDic in UPDATE
var lambda = Expression.Lambda<Action<IDictionary<string, Object>>>(assignDic, actionArguments);
var method = lambda.Compile();
actions.Add(method);
}
return actions;
}
private static readonly Expression<Func<object, string>>[] Changers
= {
s => s + " First changer added.",
s => s + " Second changer added."
};
}
As you can see, it's a pretty simple code and causen't any error. Do you have any idea what I missed?
EDIT:
The debug view of variable assignDic for one item in a sample dictionary:
$actionArguments.Item["a"] = .Block() {
$actionArguments.Item["a"];
.If (
.Call $actionArguments.ContainsKey("a")
) {
.Block() {
$actionArguments.Item["a"] = .Block() {
.Lambda #Lambda1<System.Func`2[System.Object,System.String]>;
$actionArguments.Item["a"]
};
$actionArguments.Item["a"] = .Block() {
.Lambda #Lambda2<System.Func`2[System.Object,System.String]>;
$actionArguments.Item["a"]
};
$actionArguments.Item["a"]
}
} .Else {
.Default(System.Void)
};
$actionArguments.Item["a"]
}
.Lambda #Lambda1<System.Func`2[System.Object,System.String]>(System.Object $s) {
$s + " First changer added."
}
.Lambda #Lambda2<System.Func`2[System.Object,System.String]>(System.Object $s) {
$s + " Second changer added."
}
OK. Finally I found the problem & solution. The break point of the code was on the assignment in the inner foreach loop, where I was trying to assign an Expression.Block to a IndexerExpression. It seems blocking an expression won't call it. So, I changed it to an InvokationExpression by calling Expression.Invoke and passing the IndexerExpression (named target) and now it works like a charm:
foreach (var ins in innerStatements) {
var assign = Expression.Assign(target, Expression.Invoke(ins, target));
cleanStatements.Add(assign);
}
I want to populate the itemsource of a ComboBox with items from my List, depending on which property from T is selected.
The statement should be like:
foreach property which is a string,
select the values of the property, make distinct.
public Dictionary<string, List<string>> CreateSuggestionsLists<T>(List<T> data)
{
var queryableData = data.AsQueryable();
var paramExp = Expression.Parameter(typeof(T), "left");
foreach (var pi in typeof(T).GetProperties().Where(p => p.PropertyType == typeof(string)))
{
var callExpr = Expression.MakeMemberAccess(paramExp, pi);
var lambdaExpr = Expression.Lambda(callExpr) ;
// From here on it goes wrong!!!
var comleteExpr = lambdaExpr as Expression<Func<T, bool>>;
var compiledExpr = comleteExpr.Compile();
var res = data.Select(compiledExpr).Distinct().ToList();
// add to results ...
}
return null;
}
The problem seems to be the casting from the lambda expression to prepare for compilation.
Thank you for your help.
First of all you need to provide paramExp to lambda. Secondly there is generic version of Lamda method which is just easier to use. Finally, you don't need to compile expression when you use IQueryable. You created queryableData variable and didn't use it.
Here is code:
public Dictionary<string, List<string>> CreateSuggestionsLists<T>(List<T> data)
{
var queryableData = data.AsQueryable();
var paramExp = Expression.Parameter(typeof(T), "left");
foreach (var pi in typeof(T).GetProperties().Where(p => p.PropertyType == typeof(string)))
{
var callExpr = Expression.MakeMemberAccess(paramExp, pi);
var lambdaExpr = Expression.Lambda<Func<T, bool>>(callExpr, paramExp);
var res = queryableData.Select(lambdaExpr).Distinct().ToList();
// add to results ...
}
return null;
}
I think you should check if the casting result is not null :
public Dictionary<string, List<string>> CreateSuggestionsLists<T>(List<T> data)
{
IQueryable<T> queryableData = data.AsQueryable();
ParameterExpression paramExp = Expression.Parameter(typeof(T), "left");
foreach (PropertyInfo pi in typeof(T).GetProperties().Where(p => p.PropertyType == typeof(string)))
{
MemberExpression callExpr = Expression.MakeMemberAccess(paramExp, pi);
LambdaExpression lambdaExpr = Expression.Lambda(callExpr);
// From here on it goes wrong!!!
if (!(lambdaExpr is Expression<Func<T, bool>> comleteExpr)) continue;
Func<T, bool> compiledExpr = comleteExpr.Compile();
List<bool> res = data.Select(compiledExpr).Distinct().ToList();
// add to results ...
}
return null;
}
I am creating lambda expression for Iqueryable to get value from a collection but I want to convert that value to other datatype like int or decimal. So as I cannot use c# casting with Iqueryable so I have created user defined scalar function in sql and trying to access that in expression but it throws exception that the 'methodname' cannot be converted to sql expression.
public class Context
{
[DbFunction("dbo", "ConvertToDouble")]
public int? ConvertToDouble(string value)
{
var sql = $"set #result = dbo.[ConvertToDouble]('{value}')";
var output = new SqlParameter { ParameterName = #"result", DbType = DbType.Int32, Size = 16, Direction = ParameterDirection.Output };
var result = Database.ExecuteSqlCommand(sql, output);
return output.Value as int?;
}
}
private static Expression<Func<TSource, TDataType>> CreateLamdaExpression<TSource, TDataType>(string fieldName)
{
var parameterExpression = Expression.Parameter(typeof(TSource));
var collectionParameter = Expression.Property(parameterExpression, "CustomFieldValues");
var childType = collectionParameter.Type.GetGenericArguments()[0];
var propertyParameter = Expression.Parameter(childType, childType.Name);
var left = Expression.Property(propertyParameter, "Name");
var right = Expression.Constant(fieldName);
var innerLambda = Expression.Equal(left, right);
var innerFunction = Expression.Lambda(innerLambda, propertyParameter);
var method = typeof(Enumerable).GetMethods().Where(m => m.Name == "FirstOrDefault" && m.GetParameters().Length == 2).FirstOrDefault().MakeGenericMethod(typeof(CustomFieldValue));
var outerLambda = Expression.Call(method, Expression.Property(parameterExpression, collectionParameter.Member as System.Reflection.PropertyInfo), innerFunction);
var propertyGetter = Expression.Property(outerLambda, "Value");
if (typeof(TDataType) != typeof(object))
{
/var changeTypeCall = Expression.Call(Expression.Constant(Context), Context.GetType().GetMethod("ConvertToDouble", BindingFlags.Public | BindingFlags.Instance),
propertyGetter
);
Expression convert = Expression.Convert(changeTypeCall,
typeof(TDataType));
return Expression.Lambda<Func<TSource, TDataType>>(convert, new ParameterExpression[] { parameterExpression });
}
var result = Expression.Lambda<Func<TSource, TDataType>>(propertyGetter, new ParameterExpression[] { parameterExpression });
return result;
}
Im trying to implement Dynamic LINQ Query for selected columns to show has Output columns in LINQ Query.
Here is the error:
/Property 'System.String CompanyCode' is not defined for type
'System.String'"
public static void SelectProjection()
{
DataMovementDataContext dbMovement = new DataMovementDataContext();
var entity = dbMovement.ListofAccountingDocs2_1075s.AsQueryable();
Type type = entity.ElementType;
var entityParam = Expression.Parameter(entity.ElementType, "row");
Expression expr = entityParam;
string[] props = "AccountingDocumentNbr,CompanyCode,FiscalYearNbr".Split(',');
foreach (string prop in props)
{
// use reflection (not ComponentModel) to mirror LINQ
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
// row => row.Property
// var columnLambda = Expression.Lambda( Expression.Property(entityParam, "GLCompanyCode"), entityParam);
var columnLambda = Expression.Lambda(Expression.Property(expr, "AccountingDocumentNbr,GLCompanyCode"), entityParam);
// Items.Select(row => row.Property)
var selectCall = Expression.Call(typeof(Queryable), "Select", new Type[] { entity.ElementType, columnLambda.Body.Type }, entity.Expression, columnLambda);
// Items.Select(row => row.Property).Distinct
var distinctCall = Expression.Call(typeof(Queryable), "Distinct", new Type[] { typeof(string) }, selectCall);
// colvalue => colvalue
var sortParam = Expression.Parameter(typeof(string), "AccountingDocumentNbr");
var columnResultLambda = Expression.Lambda(sortParam, sortParam);
// Items.Select(row => row.Property).Distinct.OrderBy(colvalue => colvalue)
var ordercall = Expression.Call(typeof(Queryable), "OrderBy",
new Type[] { typeof(string), columnResultLambda.Body.Type },
distinctCall, columnResultLambda);
var result = entity.Provider.CreateQuery(ordercall);
foreach (var item in result)
{
Console.Write(item);
}
}
Can any one provide me help in solving above error?
If we look at this code:
// I chnaged this part:
string[] props = new string[]
{
"AccountingDocumentNbr",
"CompanyCode",
"FiscalYearNbr"
};
Expression expr;
Type type = entity.ElementType;
foreach (string prop in props)
{
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType; // This line may cause your loop
// to do something you dont want to do?
}
It seems to me that your loop going down the properties, gets a string and then searches for a prop called CompanyCode, which the class String obviously doesnt have.
Your loop does the following at the moment:
from type get the property named AccountingDocumentNbr
from the return type of property AccountingDocumentNbr get the property called CompanyCode
form the return type of property CompanyCode get the type of the property called FiscalYearNbr
I hardly doubt this is what you really want to do.
Is there any way to create an instance of an object with object initializer with an Expression Tree? I mean create an Expression Tree to build this lambda:
// my class
public class MyObject {
public bool DisplayValue { get; set; }
}
// my lambda:
var lambda = (Func<bool, MyObject>)
(displayValue => new MyObject { DisplayValue = displayValue });
How can I create this lambda with an Expression Tree?
UPDATE:
I tryed myself and write following code:
public static Func<bool, dynamic> Creator;
static void BuildLambda() {
var expectedType = typeof(MyObject);
var displayValueParam = Expression.Parameter(typeof(bool), "displayValue");
var ctor = Expression.New(expectedType);
var local = Expression.Parameter(expectedType, "obj");
var displayValueProperty = Expression.Property(ctor, "DisplayValue");
var returnTarget = Expression.Label(expectedType);
var returnExpression = Expression.Return(returnTarget,local, expectedType);
var returnLabel = Expression.Label(returnTarget, Expression.Default(expectedType));
var block = Expression.Block(
new[] { local },
Expression.Assign(local, ctor),
Expression.Assign(displayValueProperty, displayValueParam),
Expression.Return(Expression.Label(expectedType), local, expectedType),
returnExpression,
returnLabel
);
Creator =
Expression.Lambda<Func<bool, dynamic>>(block, displayValueParam)
.Compile();
}
But it throws the following error:
Cannot jump to undefined label ''.
Can everybody help me please?
To represent object initializers in an Expression, you should use Expression.MemberInit():
Expression<Func<bool, MyObject>> BuildLambda() {
var createdType = typeof(MyObject);
var displayValueParam = Expression.Parameter(typeof(bool), "displayValue");
var ctor = Expression.New(createdType);
var displayValueProperty = createdType.GetProperty("DisplayValue");
var displayValueAssignment = Expression.Bind(
displayValueProperty, displayValueParam);
var memberInit = Expression.MemberInit(ctor, displayValueAssignment);
return
Expression.Lambda<Func<bool, MyObject>>(memberInit, displayValueParam);
}
To verify this actually does what you want, you can call ToString() on the created expression. In this case, the output is as expected:
displayValue => new MyObject() {DisplayValue = displayValue}
Finally I found my answer:
public static Func<bool, dynamic> Creator;
static void BuildLambda() {
var expectedType = typeof(MyObject);
var displayValueParam = Expression.Parameter(typeof(bool), "displayValue");
var ctor = Expression.New(expectedType);
var local = Expression.Parameter(expectedType, "obj");
var displayValueProperty = Expression.Property(local, "DisplayValue");
var returnTarget = Expression.Label(expectedType);
var returnExpression = Expression.Return(returnTarget,local, expectedType);
var returnLabel = Expression.Label(returnTarget, Expression.Default(expectedType));
var block = Expression.Block(
new[] { local },
Expression.Assign(local, ctor),
Expression.Assign(displayValueProperty, displayValueParam),
/* I forgot to remove this line:
* Expression.Return(Expression.Label(expectedType), local, expectedType),
* and now it works.
* */
returnExpression,
returnLabel
);
Creator =
Expression.Lambda<Func<bool, dynamic>>(block, displayValueParam)
.Compile();
}
UPDATE:
While it works fine, but #svick provide a better and shorter way in his answer that is actuallt wath I was looking for: MemberInit. Please see #svick's answer.