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.
Related
I am using Hangfire and have a method to schedule jobs by assembly,type and method name. Using the default constructor works properly but all the methods have overloaded constructors that will be activated using Autofac.
//Works for a default constructor
Type type = Type.GetType("typestring, assemblystring");
var method = type.GetMethod("methodstring");
Expression[] args = new Expression[] { Expression.Constant(options,typeof(Options)) }; //All methods use the same parameters
var action = Expression.Lambda<Action>(Expression.Call(Expression.New(type), method, args));
RecurringJob.AddOrUpdate(action, "cronstring");
Trying to modify to support overloaded constructors (No default) I have this code.
Type type = Type.GetType("typestring, assemblystring");
var method = type.GetMethod("methodstring");
Expression[] args = new Expression[] { Expression.Constant(options,typeof(Options)) }; //All methods use the same parameters
var ctor = type.GetConstructors().ToList().FirstOrDefault();
var ctorParams = ctor.GetParameters();
var ctorArgs = new Expression[ctorParams.Length];
for (int i = 0; i != ctorParams.Length; ++i)
{
ParameterExpression param = Expression.Parameter(typeof(object), ctorParams[i].Name);
ctorArgs[i] = Expression.Convert(param, ctorParams[i].ParameterType);
}
var ctorExpress = Expression.New(ctor, ctorArgs);
var action = Expression.Lambda<Action>(Expression.Call(ctorExpress, method, args));
RecurringJob.AddOrUpdate(action, "cronstring");
I receive this error: InvalidOperationException: variable '{first constructor param}' of type 'System.Object' referenced from scope '', but it is not defined
I am not sure if I am missing something or am going the wrong way about this. I have limited experience using expressions.
If I'm not mistaken and there is another code that would compile your expression and calls it, providing as a parameters object[], and you have to put those parameters into constructor sequentially, then you can use sample code below:
static void Main(string[] args)
{
Type type = typeof(Foo);
var ctor = type.GetConstructor(new[] { typeof(int), typeof(string) });
var ctorParams = ctor.GetParameters();
var dynPar = Expression.Parameter(typeof(object[]), "d");
var ctorArgs = new Expression[ctorParams.Length];
for (int i = 0; i != ctorParams.Length; ++i)
{
var indVal = Expression.ArrayIndex(dynPar, Expression.Constant(i));
ctorArgs[i] = Expression.Convert(indVal, ctorParams[i].ParameterType);
}
//{ new Foo(Convert(d[0], Int32), Convert(d[1], String))}
var ctorExpress = Expression.New(ctor, ctorArgs);
//{ new Foo(Convert(d[0], Int32), Convert(d[1], String)).Run()}
var callRun = Expression.Call(ctorExpress, type.GetMethod("Run"));
//{ d => new Foo(Convert(d[0], Int32), Convert(d[1], String)).Run()}
var action = Expression.Lambda<Action<object[]>>(callRun, dynPar);
action.Compile()(new object[] { 1, "a" });
}
public class Foo
{
public Foo(int a, string b)
{
A = a;
B = b;
}
public int A { get; }
public string B { get; }
public void Run()
{
Console.WriteLine($"It is {A} and {B}");
}
}
Thank you #ASpirin, your input helped resolve this issue. I changed the constructor's arguments to Constant instead of Parameter and passed in null.
Type type = Type.GetType("typestring, assemblystring");
var method = type.GetMethod("methodstring");
Expression[] args = new Expression[] { Expression.Constant(options,typeof(Options)) }; //All methods use the same parameters
var ctor = type.GetConstructors().ToList().FirstOrDefault();
var ctorParams = ctor.GetParameters();
var ctorArgs = new Expression[ctorParams.Length];
for (int i = 0; i != ctorParams.Length; ++i)
{
var param = Expression.Constant(null,typeof(object)); //Updated this line
ctorArgs[i] = Expression.Convert(param, ctorParams[i].ParameterType);
}
var ctorExpress = Expression.New(ctor, ctorArgs);
var action = Expression.Lambda<Action>(Expression.Call(ctorExpress, method, args));
RecurringJob.AddOrUpdate(action, "cronstring");
This is able to work since Hangfire doesn't actually execute the constructor and saved the expression like this:
//Class name is Test, method name is Run
var test = Activate<Test>();
test.Run(FromJson<Options>("REMOVED"));
Activate<Test> is activated using Autofac in the another program.
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;
}
I'd like to do:
var myClassInstance = myFactory.CreateMyClass() { MyPropertyA = "blah", MyPropertyB = "Bleh"};
but the compiler doesn't like that.
Is there away around this? I'm trying to avoid:
var myClassInstance = myFactory.CreateMyClass();
myClassInstance.MyPropertyA = "blah";
myClassInstance.MyPropertyB = "Bleh";
I know I said myFactory, but it isn't mine. I can't change CreateMyClass
While it isn't possible to do exactly what you are asking, you can use the Builder design pattern to increase readability.
Something along these lines:
var myClassInstance = myFactory.CreateMyClass()
.WithMyPropertyA("blah")
.WithMyPropertyB("Bleh");
This can be done with either extension methods or wrapping myFactory in a builder class of your own.
What i would do (depending on how often this is called and how important the performance is):
public class Factory
{
MyObject CreateMyObject(object source)
{
var target = new MyObject();
CopyPropertiesFromSource(target, source):
return target;
}
static void CopyPropertiesFromSource(MyObject target, object source)
{
var propertiesToInitialize = typeof(MyObject).GetProperties();
var sourceProperties = source.GetType().GetProperties();
foreach(var property in propertiesToInitialize)
{
var correspondingProperty = sourceProperties.FirstOrDefault(x => x.Name == property.Name && x.PropertyType == property.PropertyType);
if(correspondingProperty == null)
continue;
property.SetValue(target, correspondingProperty.GetValue(source));
}
}
}
var myClassInstance = new Factory().CreateMyObject({ MyPropertyA = "blah" });
Based on David Culp's answer, I made this extension function:
public static object With(this object obj, object additionalProperties)
{
var type = additionalProperties.GetType();
foreach (var sourceField in type.GetFields())
{
var name = sourceField.Name;
var value = sourceField.GetValue(additionalProperties);
if (type.GetMember(name)[0] is FieldInfo)
type.GetField(name).SetValue(obj, value);
else
type.GetProperty(name).SetValue(obj, value);
}
return obj;
}
and it's used like this:
var myClassInstance = myFactory.CreateMyClass().With(new { MyPropertyA = "blah", MyPropertyB = "bleh"});
It really shines when there's a lot of properties.
// insert a new version
T newVersion = (T)MemberwiseClone();
newVersion.IsSuspended = true;
newVersion.RealEffectiveDate = RealExpirationDate;
newVersion.RealExpirationDate = NullDate;
newVersion.Version++;
newVersion.BusinessEffectiveDate = BusinessExpirationDate;
newVersion.BusinessExpirationDate = NullDate;
becomes:
// insert a new version
T newVersion = (T)MemberwiseClone().With(new
{
IsSuspended = true,
RealEffectiveDate = RealExpirationDate,
RealExpirationDate = NullDate,
Version = Version + 1,
BusinessEffectiveDate = BusinessExpirationDate,
BusinessExpirationDate = NullDate
});
I have written a ToList(); extension Method to convert a DataTable to List. This just works under some circumstances but we have much old code which uses DataTables and sometimes it's needed. My Problem is that this method works with reflection what is ok but not that performant. I need about 1,2sek for 100.000 DataRows.
So i decided to build this with Expression Trees. At first i want to replace the Setter Call of Properties. Up to this time i could easily get the value:
var exactType = Nullable.GetUnderlyingType(propType) ?? propType;
var wert = Convert.ChangeType(zeile[spaltenname], exactType);
and set it:
propertyInfo.SetValue(tempObjekt, wert, null);
Now i searched StackOverflow and found this:
var zielExp = Expression.Parameter(typeof(T));
var wertExp = Expression.Parameter(propType);
var propertyExp = Expression.Property(zielExp, matchProp);
var zuweisungExp = Expression.Assign(propertyExp, wertExp);
var setter = Expression.Lambda<Action<T, int>>(zuweisungExp, zielExp, wertExp).Compile();
setter(tempObjekt, wert);
My big Problem is that the Lambda Action expects an integer. But i need this expecting the type of my Property. I have the Type of my Property via PropertyInfo. But can't get this to work. Thought i can easily make:
Action<T, object>
but this results in following excepion:
ArgumentException The ParameterExpression from Type "System.Int32"
cannot be used as Delegateparameter from Type "System.Object".
Someone out there knows a possible solution?
Instead of the generic Expression.Lambda method you can use this overload which takes a type:
public static LambdaExpression Lambda(
Type delegateType,
Expression body,
params ParameterExpression[] parameters
)
Then you can use the Type.MakeGenericType method to create the type for your action:
var actionType = typeof(Action<,>).MakeGenericType(typeof(T), proptype);
var setter = Expression.Lambda(actionType, zuweisungExp, zielExp, wertExp).Compile();
Edit following the comments regarding performance:
You can also just build the expression runtime to map the DataTable to your class of type T with a select, so there's only need to use reflection once, which should greatly improve performance. I wrote the following extension method to convert a DataTable to List<T> (note that this method will throw a runtime exception if you don't plan to map all datacolumns to a property in the class, so be sure to take care of that if that might happen):
public static class LocalExtensions
{
public static List<T> DataTableToList<T>(this DataTable table) where T : class
{
//Map the properties in a dictionary by name for easy access
var propertiesByName = typeof(T)
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.ToDictionary(p => p.Name);
var columnNames = table.Columns.Cast<DataColumn>().Select(dc => dc.ColumnName);
//The indexer property to access DataRow["columnName"] is called "Item"
var property = typeof(DataRow).GetProperties().First(p => p.Name == "Item"
&& p.GetIndexParameters().Length == 1
&& p.GetIndexParameters()[0].ParameterType == typeof(string));
var paramExpr = Expression.Parameter(typeof(DataRow), "r");
var newExpr = Expression.New(typeof(T));
//Create the expressions to map properties from your class to the corresponding
//value in the datarow. This will throw a runtime exception if your class
//doesn't contain properties for all columnnames!
var memberBindings = columnNames.Select(columnName =>
{
var pi = propertiesByName[columnName];
var indexExpr = Expression.MakeIndex(paramExpr, property,
new[] { Expression.Constant(columnName) });
//Datarow["columnName"] is of type object, cast to the right type
var convert = Expression.Convert(indexExpr, pi.PropertyType);
return Expression.Bind(pi, convert);
});
var initExpr = Expression.MemberInit(newExpr, memberBindings);
var func = Expression.Lambda<Func<DataRow, T>>(initExpr,paramExpr).Compile();
return table.Rows.Cast<DataRow>().Select(func).ToList();
}
}
Then I wrote a small testclass and some code which creates a datatable of 1,000,000 rows that get mapped to a list. Building the expression + converting to a list now only takes 486ms on my pc (granted it is a very small class of course):
class Test
{
public string TestString { get; set; }
public int TestInt { get; set; }
}
class Program
{
static void Main()
{
DataTable table = new DataTable();
table.Columns.Add(new DataColumn("TestString", typeof(string)));
table.Columns.Add(new DataColumn("TestInt", typeof(int)));
for(int i = 0; i < 1000000; i++)
{
var row = table.NewRow();
row["TestString"] = $"String number: {i}";
row["TestInt"] = i;
table.Rows.Add(row);
}
var stopwatch = Stopwatch.StartNew();
var myList = table.DataTableToList<Test>();
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed.ToString());
}
}
I think I've understood you correctly. I cannot translate your variables so I'm taking my best guess here based on what I'm seeing in your question:
For an Action<object,object> where the first parameter is the Entity itself and the second is the type of the property you can use
var instance = Expression.Parameter(typeof(object), "i");
var argument = Expression.Parameter(typeof(object), "a");
var convertObj = Expression.TypeAs(instance, propertyInfo.DeclaringType);
var convert = Expression.Convert(argument, propertyInfo.PropertyType);
var setterCall = Expression.Call(convertObj, propertyInfo.GetSetMethod(), convert);
var compiled = ((Expression<Action<object, object>>) Expression.Lambda(setterCall, instance, argument)).Compile();
If you know T (ie, the type of the Entity), you can do this instead:
var instance = Expression.Parameter(typeof(T), "i");
var argument = Expression.Parameter(typeof(object), "a");
var convert = Expression.Convert(argument, propertyInfo.PropertyType);
var setterCall = Expression.Call(instance , propertyInfo.GetSetMethod(), convert);
var compiled = ((Expression<Action<T, object>>) Expression.Lambda(setterCall, instance, argument)).Compile();
I comment here because I do not have the necessary reputation to comment on the response of #Alexander Derek
var memberBindings = columnNames.Select(columnName =>
{
var pi = propertiesByName[columnName];
var indexExpr = Expression.MakeIndex(paramExpr, property,
new[] { Expression.Constant(columnName) });
//Datarow["columnName"] is of type object, cast to the right type
var convert = Expression.Convert(indexExpr, pi.PropertyType);
return Expression.Bind(pi, convert);
});
in order to avoid runtime exception i added a try-catch and .where()
var memberBindings = columnNames.Select(columnName =>
{
try
{
var pi = propertiesByName[columnName];
var indexExpr = Expression.MakeIndex(paramExpr, property,
new[] { Expression.Constant(columnName) });
var convert = Expression.Convert(indexExpr, pi.PropertyType);
return Expression.Bind(pi, convert);
}
catch(Exception e)
{
return null;
}
});
var initExpr = Expression.MemberInit(newExpr, memberBindings.Where(obj => obj != null));
I am creating a delegate for a Select statement in LINQ. Some of the property bindings are to child properties on the object I'm selecting from.
This is the LINQ statement I want to put in my delegate:
var list = dataSet.Select(x => new ViewModel()
{
Name = x.Name,
ClassType = x.ClassType.Description
};
I can get the Name no worries with my code, but I do not know how to get the ClassType.Description.
Here is my current code:
protected Func<Student, ManagerStudentListViewModel> GetSelectStatement()
{
var studentType = typeof(Student);
var viewModelType = typeof(ManagerStudentListViewModel);
var parameterExpression = Expression.Parameter(studentType, "x");
var newInstantiationExpression = Expression.New(viewModelType);
// Name Binding
var viewModelProperty = viewModelType.GetProperty("Name");
var studentProperty = studentType.GetProperty("Name");
var nameMemberExpression = Expression.Property(parameterExpression, studentProperty);
var nameBinding = Expression.Bind(viewModelProperty, nameMemberExpression);
// ClassType.Description Binding
// ???
var bindings = new List<MemberAssignment>() { nameBinding, classTypeBinding };
var memberInitExpression = Expression.MemberInit(newInstantiationExpression, bindings);
var lambda = Expression.Lambda<Func<Student, ManagerStudentListViewModel>>(memberInitExpression, parameterExpression);
return lambda.Compile();
}
Accessing deeply nested members is no different than accessing any other properties, provided you know the name of the members. Just create an expression to get the first property, then add the expression to get the second.
Expression<Func<Student, ManagerStudentListViewModel>> GetSelectStatement()
{
var studentType = typeof(Student);
var viewModelType = typeof(ManagerStudentListViewModel);
var param = Expression.Parameter(studentType, "x");
var nameValue = Expression.Property(param, "Name");
var classTypeValue = Expression.Property(
Expression.Property(param, "ClassType"), // get the class type
"Description"); // get the description of the class type
var nameMemberBinding = Expression.Bind(
viewModelType.GetProperty("Name"),
nameValue);
var classTypeMemberBinding = Expression.Bind(
viewModelType.GetProperty("ClassType"),
classTypeValue);
var initializer = Expression.MemberInit(
Expression.New(viewModelType),
nameMemberBinding,
classTypeMemberBinding);
return Expression.Lambda<Func<Student, ManagerStudentListViewModel>>(initializer, param);
}