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);
}
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'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 want to add where clauses to a linq query only if there is the column on the generic type table.
code example :
this function is generic for all tables in model. I want to add where condition for all the tabels that have "AccountId" column.
public IQueryable RetrieveAll(params Expression>[] eagerProperties) {
var entitySet = ResolveEntitySet(typeof(T));
var query = context.CreateQuery<T>(entitySet);
foreach (var e in eagerProperties)
{
query = query.Expand(e);
}
var type = typeof(T);
var account = type.GetProperty("AccountId");
if(account!=null)
{
query = query.where(x=>x...)
}
return query
I need something like
Guid g = new Guid("3252353h....")
query.where(x=>x.AccountId == g)
Thanks
You must create the Where predicate dynamically using Expression Tree, look code below:
public static IQueryable<T> RetrieveAll<T>(params Expression[] eagerProperties)
{
var type = typeof(T);
var entitySet = ResolveEntitySet(type);
var query = context.CreateQuery<T>(entitySet);
foreach (var e in eagerProperties)
{
query = query.Expand(e);
}
var account = type.GetProperty("AccountId");
if (account != null)
{
Guid g = new Guid("3252353h....");
var parameter = Expression.Parameter(type);
var property = Expression.Property(parameter, account);
var guidValue = Expression.Constant(g);
var lambdaPredicate = Expression.Lambda<Func<T, bool>>(Expression.Equal(property, guidValue), parameter);
return query.Where(lambdaPredicate);
}
return query;
}
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.