Create Expression<Action> from Method name with overloaded constructor - c#

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.

Related

C# using ExpressionTree to map DataTable to List<T>

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));

Generic DbDataReader to List<T> mapping

I am having a slight issue (more like an annoyance) with my property binding data access classes. The problem is that the mapping fails when there exists no column in the reader for corresponding property in class.
Code
Here is the mapper class:
// Map our datareader object to a strongly typed list
private static IList<T> Map<T>(DbDataReader dr) where T : new()
{
try
{
// initialize our returnable list
List<T> list = new List<T>();
// fire up the lamda mapping
var converter = new Converter<T>();
while (dr.Read())
{
// read in each row, and properly map it to our T object
var obj = converter.CreateItemFromRow(dr);
// add it to our list
list.Add(obj);
}
// reutrn it
return list;
}
catch (Exception ex)
{
return default(List<T>);
}
}
Converter class:
/// <summary>
/// Converter class to convert returned Sql Records to strongly typed classes
/// </summary>
/// <typeparam name="T">Type of the object we'll convert too</typeparam>
internal class Converter<T> where T : new()
{
// Concurrent Dictionay objects
private static ConcurrentDictionary<Type, object> _convertActionMap = new ConcurrentDictionary<Type, object>();
// Delegate action declaration
private Action<IDataReader, T> _convertAction;
// Build our mapping based on the properties in the class/type we've passed in to the class
private static Action<IDataReader, T> GetMapFunc()
{
var exps = new List<Expression>();
var paramExp = Expression.Parameter(typeof(IDataReader), "o7thDR");
var targetExp = Expression.Parameter(typeof(T), "o7thTarget");
var getPropInfo = typeof(IDataRecord).GetProperty("Item", new[] { typeof(string) });
var _props = typeof(T).GetProperties();
foreach (var property in _props)
{
var getPropExp = Expression.MakeIndex(paramExp, getPropInfo, new[] { Expression.Constant(property.Name, typeof(string)) });
var castExp = Expression.TypeAs(getPropExp, property.PropertyType);
var bindExp = Expression.Assign(Expression.Property(targetExp, property), castExp);
exps.Add(bindExp);
}
// return our compiled mapping, this will ensure it is cached to use through our record looping
return Expression.Lambda<Action<IDataReader, T>>(Expression.Block(exps), new[] { paramExp, targetExp }).Compile();
}
internal Converter()
{
// Fire off our mapping functionality
_convertAction = (Action<IDataReader, T>)_convertActionMap.GetOrAdd(typeof(T), (t) => GetMapFunc());
}
internal T CreateItemFromRow(IDataReader dataReader)
{
T result = new T();
_convertAction(dataReader, result);
return result;
}
}
Exception
System.IndexOutOfRangeException {"Mileage"}
Stacktrace
at System.Data.ProviderBase.FieldNameLookup.GetOrdinal(String fieldName)
at System.Data.SqlClient.SqlDataReader.GetOrdinal(String name)
at System.Data.SqlClient.SqlDataReader.get_Item(String name)
at lambda_method(Closure , IDataReader , Typing )
at o7th.Class.Library.Data.Converter`1.CreateItemFromRow(IDataReader dataReader) in d:\Backup Folder\Development\o7th Web Design\o7th.Class.Library.C-Sharp\o7th.Class.Library\Data Access Object\Converter.cs:line 50
at o7th.Class.Library.Data.Wrapper.Map[T](DbDataReader dr) in d:\Backup Folder\Development\o7th Web Design\o7th.Class.Library.C-Sharp\o7th.Class.Library\Data Access Object\Wrapper.cs:line 33
Question
How can I fix it, so that it will not fail when I have an extra property that the reader may not have as column and vice versa? Of course the quick band-aid would be to simply add NULL As Mileage to this query in example, however, this is not a solution to the problem :)
Here's Map<T> using reflection:
// Map our datareader object to a strongly typed list
private static IList<T> Map<T>(DbDataReader dr) where T : new()
{
try
{
// initialize our returnable list
List<T> list = new List<T>();
T item = new T();
PropertyInfo[] properties = (item.GetType()).GetProperties();
while (dr.Read()) {
int fc = dr.FieldCount;
for (int j = 0; j < fc; ++j) {
var pn = properties[j].Name;
var gn = dr.GetName(j);
if (gn == pn) {
properties[j].SetValue(item, dr[j], null);
}
}
list.Add(item);
}
// return it
return list;
}
catch (Exception ex)
{
// Catch an exception if any, an write it out to our logging mechanism, in addition to adding it our returnable message property
_Msg += "Wrapper.Map Exception: " + ex.Message;
ErrorReporting.WriteEm.WriteItem(ex, "o7th.Class.Library.Data.Wrapper.Map", _Msg);
// make sure this method returns a default List
return default(List<T>);
}
}
Note:
This method is 63% slower than using expression trees...
As noted in comments, the problem is that there exists no column in the reader for the specified property. The idea is to loop by the column names of reader first, and check to see if matching property exists. But how do one get the list of column names beforehand?
One idea is to use expression trees itself to build the list of column names from the reader and check it against properties of the class. Something like this
var paramExp = Expression.Parameter(typeof(IDataRecord), "o7thDR");
var loopIncrementVariableExp = Expression.Parameter(typeof(int), "i");
var columnNamesExp = Expression.Parameter(typeof(List<string>), "columnNames");
var columnCountExp = Expression.Property(paramExp, "FieldCount");
var getColumnNameExp = Expression.Call(paramExp, "GetName", Type.EmptyTypes,
Expression.PostIncrementAssign(loopIncrementVariableExp));
var addToListExp = Expression.Call(columnNamesExp, "Add", Type.EmptyTypes,
getColumnNameExp);
var labelExp = Expression.Label(columnNamesExp.Type);
var getColumnNamesExp = Expression.Block(
new[] { loopIncrementVariableExp, columnNamesExp },
Expression.Assign(columnNamesExp, Expression.New(columnNamesExp.Type)),
Expression.Loop(
Expression.IfThenElse(
Expression.LessThan(loopIncrementVariableExp, columnCountExp),
addToListExp,
Expression.Break(labelExp, columnNamesExp)),
labelExp));
would be the equivalent of
List<string> columnNames = new List<string>();
for (int i = 0; i < reader.FieldCount; i++)
{
columnNames.Add(reader.GetName(i));
}
One may continue with the final expression, but there is a catch here making any further effort along this line futile. The above expression tree will be fetching the column names every time the final delegate is called which in your case is for every object creation, which is against the spirit of your requirement.
Another approach is to let the converter class have a pre-defined awareness of the column names for a given type, by means of attributes (see for an example) or by maintaining a static dictionary like (Dictionary<Type, IEnumerable<string>>). Though it gives more flexibility, the flip side is that your query need not always include all the column names of a table, and any reader[notInTheQueryButOnlyInTheTableColumn] would result in exception.
The best approach as I see is to fetch the column names from the reader object, but only once. I would re-write the thing like:
private static List<string> columnNames;
private static Action<IDataReader, T> GetMapFunc()
{
var exps = new List<Expression>();
var paramExp = Expression.Parameter(typeof(IDataRecord), "o7thDR");
var targetExp = Expression.Parameter(typeof(T), "o7thTarget");
var getPropInfo = typeof(IDataRecord).GetProperty("Item", new[] { typeof(string) });
foreach (var columnName in columnNames)
{
var property = typeof(T).GetProperty(columnName);
if (property == null)
continue;
// use 'columnName' instead of 'property.Name' to speed up reader lookups
//in case of certain readers.
var columnNameExp = Expression.Constant(columnName);
var getPropExp = Expression.MakeIndex(
paramExp, getPropInfo, new[] { columnNameExp });
var castExp = Expression.TypeAs(getPropExp, property.PropertyType);
var bindExp = Expression.Assign(
Expression.Property(targetExp, property), castExp);
exps.Add(bindExp);
}
return Expression.Lambda<Action<IDataReader, T>>(
Expression.Block(exps), paramExp, targetExp).Compile();
}
internal T CreateItemFromRow(IDataReader dataReader)
{
if (columnNames == null)
{
columnNames = Enumerable.Range(0, dataReader.FieldCount)
.Select(x => dataReader.GetName(x))
.ToList();
_convertAction = (Action<IDataReader, T>)_convertActionMap.GetOrAdd(
typeof(T), (t) => GetMapFunc());
}
T result = new T();
_convertAction(dataReader, result);
return result;
}
Now that begs the question why not pass the data reader directly to constructor? That would be better.
private IDataReader dataReader;
private Action<IDataReader, T> GetMapFunc()
{
var exps = new List<Expression>();
var paramExp = Expression.Parameter(typeof(IDataRecord), "o7thDR");
var targetExp = Expression.Parameter(typeof(T), "o7thTarget");
var getPropInfo = typeof(IDataRecord).GetProperty("Item", new[] { typeof(string) });
var columnNames = Enumerable.Range(0, dataReader.FieldCount)
.Select(x => dataReader.GetName(x));
foreach (var columnName in columnNames)
{
var property = typeof(T).GetProperty(columnName);
if (property == null)
continue;
// use 'columnName' instead of 'property.Name' to speed up reader lookups
//in case of certain readers.
var columnNameExp = Expression.Constant(columnName);
var getPropExp = Expression.MakeIndex(
paramExp, getPropInfo, new[] { columnNameExp });
var castExp = Expression.TypeAs(getPropExp, property.PropertyType);
var bindExp = Expression.Assign(
Expression.Property(targetExp, property), castExp);
exps.Add(bindExp);
}
return Expression.Lambda<Action<IDataReader, T>>(
Expression.Block(exps), paramExp, targetExp).Compile();
}
internal Converter(IDataReader dataReader)
{
this.dataReader = dataReader;
_convertAction = (Action<IDataReader, T>)_convertActionMap.GetOrAdd(
typeof(T), (t) => GetMapFunc());
}
internal T CreateItemFromRow()
{
T result = new T();
_convertAction(dataReader, result);
return result;
}
Call it like
List<T> list = new List<T>();
var converter = new Converter<T>(dr);
while (dr.Read())
{
var obj = converter.CreateItemFromRow();
list.Add(obj);
}
There are a number of improvements that I can suggest, though.
The generic new T() you're calling in CreateItemFromRow is slower, it uses reflection behind the scenes. You can delegate that part to expression trees as well which should be faster
Right now GetProperty call isn't case insensitive, meaning your column names will have to exactly match the property name. I would make it case insensitive using one of those Bindings.Flag.
I'm not sure at all why you are using a ConcurrentDictionary as a caching mechanism here. A static field in a generic class <T> will be unique for every T. The generic field itself can act as cache. Also why is the Value part of ConcurrentDictionary of type object?
As I said earlier, it's not the best to strongly tie a type and the column names (which you're doing by caching one particular Action delegate per type). Even for the same type your queries can be different selecting different set of columns. It's best to leave it to data reader to decide.
Use Expression.Convert instead of Expression.TypeAs for value type conversion from object.
Also note that reader.GetOrdinal is much faster way to perform data reader lookups.
I would re-write the whole thing like:
readonly Func<IDataReader, T> _converter;
readonly IDataReader dataReader;
private Func<IDataReader, T> GetMapFunc()
{
var exps = new List<Expression>();
var paramExp = Expression.Parameter(typeof(IDataRecord), "o7thDR");
var targetExp = Expression.Variable(typeof(T));
exps.Add(Expression.Assign(targetExp, Expression.New(targetExp.Type)));
//does int based lookup
var indexerInfo = typeof(IDataRecord).GetProperty("Item", new[] { typeof(int) });
var columnNames = Enumerable.Range(0, dataReader.FieldCount)
.Select(i => new { i, name = dataReader.GetName(i) });
foreach (var column in columnNames)
{
var property = targetExp.Type.GetProperty(
column.name,
BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
if (property == null)
continue;
var columnNameExp = Expression.Constant(column.i);
var propertyExp = Expression.MakeIndex(
paramExp, indexerInfo, new[] { columnNameExp });
var convertExp = Expression.Convert(propertyExp, property.PropertyType);
var bindExp = Expression.Assign(
Expression.Property(targetExp, property), convertExp);
exps.Add(bindExp);
}
exps.Add(targetExp);
return Expression.Lambda<Func<IDataReader, T>>(
Expression.Block(new[] { targetExp }, exps), paramExp).Compile();
}
internal Converter(IDataReader dataReader)
{
this.dataReader = dataReader;
_converter = GetMapFunc();
}
internal T CreateItemFromRow()
{
return _converter(dataReader);
}

Rewriting method declaration

I'm iterating over the methods in a type and have to rewrite those that aren't static. I'd have to insert a parameter either as the first or the last parameter but can't figure out how to create the ParameterSyntax object
This is what I have so far
var methods = from m in r.Members.OfType<MethodDeclarationSyntax>()
where !m.Modifiers.Contains(Syntax.Token(SyntaxKind.StaticKeyword))
select new {
orgMethodDecl = m,
rewrittenDecl = RewriteMethodDeclaration(m,name)};
var rewrittenType = r;
foreach(var m in methods){
rewrittenType = rewrittenType.ReplaceNode(m.orgMethodDecl, m.rewrittenDecl);
}
and the RewriteMethodDeclaration is where I have the issue
MethodDeclarationSyntax RewriteMethodDeclaration(MethodDeclarationSyntax method, string name)
{
var p = Syntax.Parameter(); //type dynamic, name: name
var parameters = method.ParameterList.AddParameters(p);
return method.WithParameterList(parameters);
}
Try this:
static MethodDeclarationSyntax
RewriteMethodDeclaration(MethodDeclarationSyntax method, string name)
{
var type = Syntax.ParseTypeName("dynamic");
var identifier = Syntax.Identifier(String.Format(" {0}", name));
var p = Syntax.Parameter(
new SyntaxList<AttributeListSyntax>(),
new SyntaxTokenList(),
type,
identifier,
null);
var parameters = method.ParameterList.AddParameters(p);
return method.WithParameterList(parameters);
}

Expression to create an instance with object initializer

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.

C# deep/nested/recursive merge of dynamic/expando objects

I need to "merge" 2 dynamic objects in C#. All that I've found on stackexchange covered only non-recursive merging. But I am looking to something that does recursive or deep merging, very much the same like jQuery's $.extend(obj1, obj2) function.
Upon collision of two members, the following rules should apply:
If the types mismatch, an exception must be thrown and merge is aborted. Exception: obj2 Value maybe null, in this case the value & type of obj1 is used.
For trivial types (value types + string) obj1 values are always prefered
For non-trivial types, the following rules are applied:
IEnumerable & IEnumberables<T> are simply merged (maybe .Concat() ? )
IDictionary & IDictionary<TKey,TValue> are merged; obj1 keys have precedence upon collision
Expando & Expando[] types must be merged recursively, whereas Expando[] will always have same-type elements only
One can assume there are no Expando objects within Collections (IEnumerabe & IDictionary)
All other types can be discarded and need not be present in the resulting dynamic object
Here is an example of a possible merge:
dynamic DefaultConfig = new {
BlacklistedDomains = new string[] { "domain1.com" },
ExternalConfigFile = "blacklist.txt",
UseSockets = new[] {
new { IP = "127.0.0.1", Port = "80"},
new { IP = "127.0.0.2", Port = "8080" }
}
};
dynamic UserSpecifiedConfig = new {
BlacklistedDomain = new string[] { "example1.com" },
ExternalConfigFile = "C:\\my_blacklist.txt"
};
var result = Merge (UserSpecifiedConfig, DefaultConfig);
// result should now be equal to:
var result_equal = new {
BlacklistedDomains = new string[] { "domain1.com", "example1.com" },
ExternalConfigFile = "C:\\my_blacklist.txt",
UseSockets = new[] {
new { IP = "127.0.0.1", Port = "80"},
new { IP = "127.0.0.2", Port = "8080" }
}
};
Any ideas how to do this?
Right, this is a bit longwinded but have a look. it's an implementation using Reflection.Emit.
Open issue for me is how to implement a ToString() override so that you can do a string comparison. Are these values coming from a config file or something? If they are in JSON Format you could do worse than use a JsonSerializer, I think. Depends on what you want.
You could use the Expando Object to get rid of the Reflection.Emit nonsense as well, at the bottom of the loop:
var result = new ExpandoObject();
var resultDict = result as IDictionary<string, object>;
foreach (string key in resVals.Keys)
{
resultDict.Add(key, resVals[key]);
}
return result;
I can't see a way around the messy code for parsing the original object tree though, not immediately. I'd like to hear some other opinions on this. The DLR is relatively new ground for me.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
dynamic DefaultConfig = new
{
BlacklistedDomains = new string[] { "domain1.com" },
ExternalConfigFile = "blacklist.txt",
UseSockets = new[] {
new { IP = "127.0.0.1", Port = "80" },
new { IP = "127.0.0.2", Port = "8080" }
}
};
dynamic UserSpecifiedConfig = new
{
BlacklistedDomains = new string[] { "example1.com" },
ExternalConfigFile = "C:\\my_blacklist.txt"
};
var result = Merge(UserSpecifiedConfig, DefaultConfig);
// result should now be equal to:
var result_equal = new
{
BlacklistedDomains = new string[] { "domain1.com", "example1.com" },
ExternalConfigFile = "C:\\my_blacklist.txt",
UseSockets = new[] {
new { IP = "127.0.0.1", Port = "80"},
new { IP = "127.0.0.2", Port = "8080" }
}
};
Debug.Assert(result.Equals(result_equal));
}
/// <summary>
/// Merge the properties of two dynamic objects, taking the LHS as primary
/// </summary>
/// <param name="lhs"></param>
/// <param name="rhs"></param>
/// <returns></returns>
static dynamic Merge(dynamic lhs, dynamic rhs)
{
// get the anonymous type definitions
Type lhsType = ((Type)((dynamic)lhs).GetType());
Type rhsType = ((Type)((dynamic)rhs).GetType());
object result = new { };
var resProps = new Dictionary<string, PropertyInfo>();
var resVals = new Dictionary<string, object>();
var lProps = lhsType.GetProperties().ToDictionary<PropertyInfo, string>(prop => prop.Name);
var rProps = rhsType.GetProperties().ToDictionary<PropertyInfo, string>(prop => prop.Name);
foreach (string leftPropKey in lProps.Keys)
{
var lPropInfo = lProps[leftPropKey];
resProps.Add(leftPropKey, lPropInfo);
var lhsVal = Convert.ChangeType(lPropInfo.GetValue(lhs, null), lPropInfo.PropertyType);
if (rProps.ContainsKey(leftPropKey))
{
PropertyInfo rPropInfo;
rPropInfo = rProps[leftPropKey];
var rhsVal = Convert.ChangeType(rPropInfo.GetValue(rhs, null), rPropInfo.PropertyType);
object setVal = null;
if (lPropInfo.PropertyType.IsAnonymousType())
{
setVal = Merge(lhsVal, rhsVal);
}
else if (lPropInfo.PropertyType.IsArray)
{
var bound = ((Array) lhsVal).Length + ((Array) rhsVal).Length;
var cons = lPropInfo.PropertyType.GetConstructor(new Type[] { typeof(int) });
dynamic newArray = cons.Invoke(new object[] { bound });
//newArray = ((Array)lhsVal).Clone();
int i=0;
while (i < ((Array)lhsVal).Length)
{
newArray[i] = lhsVal[i];
i++;
}
while (i < bound)
{
newArray[i] = rhsVal[i - ((Array)lhsVal).Length];
i++;
}
setVal = newArray;
}
else
{
setVal = lhsVal == null ? rhsVal : lhsVal;
}
resVals.Add(leftPropKey, setVal);
}
else
{
resVals.Add(leftPropKey, lhsVal);
}
}
foreach (string rightPropKey in rProps.Keys)
{
if (lProps.ContainsKey(rightPropKey) == false)
{
PropertyInfo rPropInfo;
rPropInfo = rProps[rightPropKey];
var rhsVal = rPropInfo.GetValue(rhs, null);
resProps.Add(rightPropKey, rPropInfo);
resVals.Add(rightPropKey, rhsVal);
}
}
Type resType = TypeExtensions.ToType(result.GetType(), resProps);
result = Activator.CreateInstance(resType);
foreach (string key in resVals.Keys)
{
var resInfo = resType.GetProperty(key);
resInfo.SetValue(result, resVals[key], null);
}
return result;
}
}
}
public static class TypeExtensions
{
public static Type ToType(Type type, Dictionary<string, PropertyInfo> properties)
{
AppDomain myDomain = Thread.GetDomain();
Assembly asm = type.Assembly;
AssemblyBuilder assemblyBuilder =
myDomain.DefineDynamicAssembly(
asm.GetName(),
AssemblyBuilderAccess.Run
);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(type.Module.Name);
TypeBuilder typeBuilder = moduleBuilder.DefineType(type.Name,TypeAttributes.Public);
foreach (string key in properties.Keys)
{
string propertyName = key;
Type propertyType = properties[key].PropertyType;
FieldBuilder fieldBuilder = typeBuilder.DefineField(
"_" + propertyName,
propertyType,
FieldAttributes.Private
);
PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(
propertyName,
PropertyAttributes.HasDefault,
propertyType,
new Type[] { }
);
// First, we'll define the behavior of the "get" acessor for the property as a method.
MethodBuilder getMethodBuilder = typeBuilder.DefineMethod(
"Get" + propertyName,
MethodAttributes.Public,
propertyType,
new Type[] { }
);
ILGenerator getMethodIL = getMethodBuilder.GetILGenerator();
getMethodIL.Emit(OpCodes.Ldarg_0);
getMethodIL.Emit(OpCodes.Ldfld, fieldBuilder);
getMethodIL.Emit(OpCodes.Ret);
// Now, we'll define the behavior of the "set" accessor for the property.
MethodBuilder setMethodBuilder = typeBuilder.DefineMethod(
"Set" + propertyName,
MethodAttributes.Public,
null,
new Type[] { propertyType }
);
ILGenerator custNameSetIL = setMethodBuilder.GetILGenerator();
custNameSetIL.Emit(OpCodes.Ldarg_0);
custNameSetIL.Emit(OpCodes.Ldarg_1);
custNameSetIL.Emit(OpCodes.Stfld, fieldBuilder);
custNameSetIL.Emit(OpCodes.Ret);
// Last, we must map the two methods created above to our PropertyBuilder to
// their corresponding behaviors, "get" and "set" respectively.
propertyBuilder.SetGetMethod(getMethodBuilder);
propertyBuilder.SetSetMethod(setMethodBuilder);
}
//MethodBuilder toStringMethodBuilder = typeBuilder.DefineMethod(
// "ToString",
// MethodAttributes.Public,
// typeof(string),
// new Type[] { }
//);
return typeBuilder.CreateType();
}
public static Boolean IsAnonymousType(this Type type)
{
Boolean hasCompilerGeneratedAttribute = type.GetCustomAttributes(
typeof(CompilerGeneratedAttribute), false).Count() > 0;
Boolean nameContainsAnonymousType =
type.FullName.Contains("AnonymousType");
Boolean isAnonymousType = hasCompilerGeneratedAttribute && nameContainsAnonymousType;
return isAnonymousType;
}
}
This works for me, but I'm sure it could be given some love and attention and look better. It does not include your type-check but that would be rather trivial to add. So while this is not a perfect answer, I hope it can get you closer to a solution.
Subsequent calls to DynamicIntoExpando(...) will keep appending and overwriting new and existing values to the existing Source structure. You can call it as many times as you need to. The function MergeDynamic() illustrates how two dynamics are merged into one ExpandoObject.
The code basically iterates over the dynamic value, checks the type, and merge appropriately and recursively to any depth.
I wrapped it in a helper class for my own purposes.
using System.Dynamic; // For ExpandoObject
...
public static class DynamicHelper
{
// We expect inputs to be of type IDictionary
public static ExpandoObject MergeDynamic(dynamic Source, dynamic Additional)
{
ExpandoObject Result = new ExpandoObject();
// First copy 'source' to Result
DynamicIntoExpando(Result, Source);
// Then copy additional fields, boldy overwriting the source as needed
DynamicIntoExpando(Result, Additional);
// Done
return Result;
}
public static void DynamicIntoExpando(ExpandoObject Result, dynamic Source, string Key = null)
{
// Cast it for ease of use.
var R = Result as IDictionary<string, dynamic>;
if (Source is IDictionary<string, dynamic>)
{
var S = Source as IDictionary<string, dynamic>;
ExpandoObject NewDict = new ExpandoObject();
if (Key == null)
{
NewDict = Result;
}
else if (R.ContainsKey(Key))
{
// Already exists, overwrite
NewDict = R[Key];
}
var ND = NewDict as IDictionary<string, dynamic>;
foreach (string key in S.Keys)
{
ExpandoObject NewDictEntry = new ExpandoObject();
var NDE = NewDictEntry as IDictionary<string, dynamic>;
if (ND.ContainsKey(key))
{
NDE[key] = ND[key];
}
else if (R.ContainsKey(key))
{
NDE[key] = R[key];
}
DynamicIntoExpando(NewDictEntry, S[key], key);
if(!R.ContainsKey(key)) {
ND[key] = ((IDictionary<string, dynamic>)NewDictEntry)[key];
}
}
if (Key == null)
{
R = NewDict;
}
else if (!R.ContainsKey(Key))
{
R.Add(Key, NewDict);
}
}
else if (Source is IList<dynamic>)
{
var S = Source as IList<dynamic>;
List<ExpandoObject> NewList = new List<ExpandoObject>();
if (Key != null && R.ContainsKey(Key))
{
// Already exists, overwrite
NewList = (List<ExpandoObject>)R[Key];
}
foreach (dynamic D in S)
{
ExpandoObject ListEntry = new ExpandoObject();
DynamicIntoExpando(ListEntry, D);
// in this case we have to compare the ListEntry to existing entries and on
NewList.Add(ListEntry);
}
if (Key != null && !R.ContainsKey(Key))
{
R[Key] = NewList.Distinct().ToList();
}
}
else
{
R[Key] = Source;
}
}
}

Categories

Resources