Change Parameter Value of Expression<Func<<string>> - c#

Let's say I've got the class Item which looks like this
public class Item
{
// ..
Expression<Func<string>> Callback {get; set;}
}
Item defines a property called Callback which can be used like this
public string TestFunction(string ident, DateTime value1, DateTime value2)
{
return string.Join(";", ident, value1, value2);
}
// ..
Item x = new Item();
x.Callback = () => TestFunction("Hello there", DateTime.Now.Date, DateTime.Now);
Console.WriteLine(x.Callback.Compile().Invoke()); // prints the expected output
That works just well. Now, what I'm trying to do is change the value of the DateTime parameters.
I've already figured out how to get the arguments:
MethodCallExpression body = (MethodCallExpression)x.Callback.Body;
foreach(ConstantExpression arg in body.Arguments) {
if(arg.Type == typeof(DateTime)) {
//arg.Value = => READONLY!
}
}
However, I can't assign a new value to arg.Value because doesn't have a setter.
There seems to be something called ExpressionVisitor but I'm unsure if that's something I need.
Is there any way to achieve what I'm trying to do?
Thank you in advance
__
Update
I almost got it working with #Guru Stron help but there's still a small problem.
This piece of code works perfectly fine:
var newParams = new[] { Expression.Constant("testIdent"), Expression.Constant(DateTime.Now), Expression.Constant(DateTime.Now) };
However, the following code throws an
Expression of type 'System.Linq.Expressions.ConstantExpression' cannot be used for parameter of type 'System.String' of method 'System.String TestFunction(System.String, System.DateTime, System.DateTime)'
Exception.
List<ConstantExpression> para = new List<ConstantExpression>();
foreach (var arg in body.Arguments) {
if (arg.Type == typeof(DateTime)) {
para.Add(Expression.Constant(DateTime.Now));
continue;
}
para.Add(Expression.Constant(arg));
}
var exprBody = Expression.Call(body.Object, body.Method, para); // Exception is thrown here
The error is pretty obvious but I can't seem to find a way to convert the parameter to the correct type.
The reason why I changed the code is because I don't know the amount of parameters, so I tried to loop through them any only change the ones I need since the order remains correct.
Any ideas?

You will need to build a new expression and pass new desired parameters to it:
MethodCallExpression body = (MethodCallExpression)x.Callback.Body;
var newParams = new[] { Expression.Constant("NEW "), Expression.Constant(DateTime.Now), Expression.Constant(DateTime.Now)};
var exprBody = Expression.Call(body.Object, body.Method, newParams );
var newExpr = Expression.Lambda<Func<string>>(exprBody);
var newFunc = newExpr.Compile();
Console.WriteLine(newFunc()); // "NEW ;03-Jun-20 5:07:16 PM;03-Jun-20 5:07:16 PM"

Related

I need to call an EntityFunctions method in a dynamic EF Filter generation. How?

We have implemented a method of dynamically creating Lambda filters using some reflection and other methods. Most of what I want is working beautifully. The problem is when it comes to DateTime values and equality/greater than/less than filters.
Our SQL server is storing the items as DateTime objects and sometimes the Time is specified for specific items. However, when displaying a list on the web, we are only showing the date. So when the user (using Kendo Grid) tries to filter the data to dates greater than or something similar, it is using midnight the day of as it parameter. This means all items that happened after midnight on that day are included when they shouldn't be. It also means that when we use a equals statement, nothing is returned as there is very little that happens exactly at midnight.
My research (using Stack Overflow) led me to using EntityFunctions.TruncateTime as a method. I'm not sure if I am to do it to both the field and the filtering value or not, but I can't even get past the value for now.
My first try was to set the right side of my comparison (the value portion) to a call to EntityFunctions.TruncateTime(filter.Value). This gave me a This function can only be invoked by linq to entities error. Further research lead me to using the Expression.Call method, but currently I am getting an exception of No method 'TruncateTime' on type 'System.Data.Objects.EntityFunctions' is compatible with the supplied arguments.
Below is my code where I try to make the call. I provided the entire function for context. I created a GetPropertyType function to let me know if I am filtering on a DateTime field. I also added a property to my Filter object to tell me if I should ignore time or not. If both are true, only then do I try and apply the TruncateTime function.
I've also tried specifying the type of DateTime as a type argument to the Expression.Constant call in the method, just in case it needed to be typed, but that didn't help either.
public static Expression GetLambdaFilters(ICollection<Filter> filters)
{
if (filters.Count > 0)
{
Type entityType = filters.ElementAt(0).EntityType;
var item = Expression.Parameter(entityType, entityType.Name);
Expression leftSide = null;
Expression rightSide = null;
Expression filterExpression = null;
foreach (var filter in filters)
{
Expression left = GetPropertyExpression(filter, item);
Expression comparison = null;
if (left is MethodCallExpression)
{
comparison = left;
}
else
{
Expression right = null;
if (!filter.IsCollectionQuery)
{
if (GetPropertyType(filter) == typeof (DateTime) && filter.IgnoreTime)
{
right = Expression.Call(typeof (EntityFunctions), "TruncateTime", null,
Expression.Constant(filter.Value));
}
else
{
right = Expression.Constant(filter.Value);
}
}
else
{
Filter innerFilter = new Filter();
innerFilter.IsCollectionQuery = false;
innerFilter.Operator = filter.Operator;
innerFilter.PropertyName = GetCollectionPropertyName(filter, item);
innerFilter.Type = filter.CollectionType;
innerFilter.Value = filter.Value;
List<Filter> innerfilters = new List<Filter>(){
innerFilter
};
right = GetLambdaFilters(innerfilters);
filter.Operator = FilterOperator.Any;
}
comparison = GetExpression(left, right, filter);
}
if (leftSide == null)
{
leftSide = comparison;
filterExpression = leftSide;
continue;
}
if (rightSide == null)
{
rightSide = comparison;
filterExpression = Expression.AndAlso(leftSide, rightSide);
continue;
}
filterExpression = Expression.AndAlso(filterExpression, comparison);
}
var func = typeof(Func<,>);
func.MakeGenericType(entityType, typeof(bool));
return Expression.Lambda(func.MakeGenericType(entityType, typeof(bool)), filterExpression, item);
}
else
{
return GetLambdaFilter(filters.First());
}
}
If you change the line:
right = Expression.Call(typeof (EntityFunctions), "TruncateTime", null,
Expression.Constant(filter.Value));
to
right = Expression.Call(typeof (EntityFunctions), "TruncateTime", null,
Expression.Convert(Expression.Constant(DateTime.Parse(filter.Value)), typeof(DateTime?)));
this should work. Its the boxing conversion part that wont happen without the convert call that is causing the error.

Expression Tree with string assignment and getting value

I have built my own SQL Query builder that breaks apart an Expression, however, I'm having an issue trying to get the value of string defined in the same function as the lambda expression.
Here is what I am trying to do in console app:
private static void MyBuilderTest()
{
var sqlBuilder = new SqlBuilder();
// Doesn't work -- NEED GUIDANCE HERE
var testValue = "Test"; // Defined in the same function as the lambda below
sqlBuilder.Select<FooObject>(o => o.FooValue == testValue);
// Works
var someObject = new SomeObject { SomeValue = "classTest };
sqlBuilder.Select<FooObject>(o => o.FooValue == someObject.SomeValue);
}
In my builder it subclasses from ExpressionVisitor, and I override the VisitMember. I found that a string defined in at the base Console level will come back as:
Node.Expression.NodeType == ExpressionType.Constant
The Node.Expression passes back properties of:
CanReduce = false
DebugView = ".Constant<ConsoleApplication1.Program+<>c__DisplayClass1>(ConsoleApplication1.Program+<>c__DisplayClass1)"
NodeType = Constant
Type = System.Type {System.RunetimeType}
Value = {ConsoleApplication1.Program}
The Node.Expression.Value contains:
testValue = "Test" (Type: string)
How do I get this value? I've tried several things, like:
var memberType = node.Expression.Type.DeclaringType;
This passes back a ConsoleApplication1.Program type.
However, when I do:
memberType.GetProperty("testValue"); // Declaring Type from Expression
It passes back null.
The above methods work fine if I place the lambda "strings" in a class, but doesn't work if they string is defined in the console function.
Can anyone tell me how to get the string value if it's defined at the function level of the lambda?
EDITED: Added VisitMember
protected override Expression VisitMember(MemberExpression node)
{
if (node.NodeType == ExpressionType.Constant)
{
// Node.Expression is a ConstantExpression type.
// node.Expression contains properties above
// And Has Value of: {ConsoleApplication1.Program}
// Expanding Value in Watch window shows: testValue = "Test"
// How do I get this value, if the ConsoleApplication1.Program type doesn't
// even know about it? Looks like maybe a dynamic property?
}
}
EDITED
Added code to the console app example to show what works and what doesn't.
The lambda in your example has "closed over" the testValue variable, meaning the compiler has captured it as a field of the same name in an automatically generated class called ConsoleApplication1.Program+<>c__DisplayClass1>. You can use normal reflection to get the current value of that field by casting the right hand-side of the binary expression into a MemberExpression.
var testValue = "hello";
var expr = (Expression<Func<string, bool>>) (x => x == testValue);
var rhs = (MemberExpression) ((BinaryExpression) expr.Body).Right;
var obj = ((ConstantExpression) rhs.Expression).Value;
var field = (FieldInfo) rhs.Member;
var value = field.GetValue(obj);
Debug.Assert(Equals(value, "hello"));
testValue = "changed";
value = field.GetValue(obj);
Debug.Assert(Equals(value, "changed"));
Alternatively you can change your variable into a constant.
const string testValue = "hello";
var expr = (Expression<Func<string, bool>>) (x => x == testValue);
var value = ((ConstantExpression) ((BinaryExpression) expr.Body).Right).Value;
Debug.Assert(Equals(value, "hello"));
Instead of doing this by yourself, have a look at PartialEvaluator from Matt Warren. It replaces all references to constants with the constants themselves.

Assigning property of anonymous type via anonymous method

I am new in the functional side of C#, sorry if the question is lame.
Given the following WRONG code:
var jobSummaries = from job in jobs
where ...
select new
{
ID = job.ID,
Description = job.Description,
FileName = (job) => {
// primitive logic not
// worth to become a named method
try { return job.Files[0].LocalName); }
catch { return null as string; }
}
};
This code produces the following justified compiler error:
cannot assign lambda expression to
anonymous type property
The code above would set the delegate to the FileName property. But that is not my aim. I want the code work like this but without naming the method:
var jobSummaries = from job in jobs
where ...
select new
{
ID = job.ID,
Description = job.Description,
FileName = this.ExtractFileName(job)
};
...
private string ExtractFileName(Job job)
{
try { return Path.GetFileName(job.Files[0].LocalName); }
catch { return null as string; }
}
Any suggestions?
To call an anonymous function directly, this works:
int result = new Func<int, int>( (int i) =>{ return i + 5; } ).Invoke(3);
// result = 8
But I agree, int result = (i => i + 5)(3); would be way cooler =)
As far as I know, you can't inline lambda expressions like that because a lamda expression is an instance itself (of the type Expression<Func<T>> or similar).
However, you can do this (updated with calculation of fileName, since this is now provided by the OP):
var jobSummaries = from job in jobs
where ...
let fileName = job.Files.Select(f => f.LocalName).FirstOrDefault()
select new
{
ID = job.ID,
Description = job.Description,
FileName = fileName
};
Notice the use of the let keyword, that lets you extract the filename from the job variable directly inside the LINQ expression.
The compiler is complaining because you are not calling your lambda function, you are defining it. If the compiler would let you, you'd have a FileName property that is a function rather than a value.
If you can write your "primitive logic" as an expression, you can write that directly in the assignment statement.
How about using extension for select. So you can do your little logic inside
var jobSummaries = jobs.Select(j =>
{
var someVar = j + "bla";
return new
{
somelogic = someVar
};
});

How do I create and access a new instance of an Anonymous Class passed as a parameter in C#?

I have created a function that takes a SQL command and produces output that can then be used to fill a List of class instances. The code works great. I've included a slightly simplified version without exception handling here just for reference - skip this code if you want to jump right the problem. If you have suggestions here, though, I'm all ears.
public List<T> ReturnList<T>() where T : new()
{
List<T> fdList = new List<T>();
myCommand.CommandText = QueryString;
SqlDataReader nwReader = myCommand.ExecuteReader();
Type objectType = typeof (T);
FieldInfo[] typeFields = objectType.GetFields();
while (nwReader.Read())
{
T obj = new T();
foreach (FieldInfo info in typeFields)
{
for (int i = 0; i < nwReader.FieldCount; i++)
{
if (info.Name == nwReader.GetName(i))
{
info.SetValue(obj, nwReader[i]);
break;
}
}
}
fdList.Add(obj);
}
nwReader.Close();
return fdList;
}
As I say, this works just fine. However, I'd like to be able to call a similar function with an anonymous class for obvious reasons.
Question #1: it appears that I must construct an anonymous class instance in my call to my anonymous version of this function - is this right? An example call is:
.ReturnList(new { ClientID = 1, FirstName = "", LastName = "", Birthdate = DateTime.Today });
Question #2: the anonymous version of my ReturnList function is below. Can anyone tell me why the call to info.SetValue simply does nothing? It doesn't return an error or anything but neither does it change the value of the target field.
public List<T> ReturnList<T>(T sample)
{
List<T> fdList = new List<T>();
myCommand.CommandText = QueryString;
SqlDataReader nwReader = myCommand.ExecuteReader();
// Cannot use FieldInfo[] on the type - it finds no fields.
var properties = TypeDescriptor.GetProperties(sample);
while (nwReader.Read())
{
// No way to create a constructor so this call creates the object without calling a ctor. Could this be a source of the problem?
T obj = (T)FormatterServices.GetUninitializedObject(typeof(T));
foreach (PropertyDescriptor info in properties)
{
for (int i = 0; i < nwReader.FieldCount; i++)
{
if (info.Name == nwReader.GetName(i))
{
// This loop runs fine but there is no change to obj!!
info.SetValue(obj, nwReader[i]);
break;
}
}
}
fdList.Add(obj);
}
nwReader.Close();
return fdList;
}
Any ideas?
Note: when I tried to use the FieldInfo array as I did in the function above, the typeFields array had zero elements (even though the objectType shows the field names - strange). Thus, I use TypeDescriptor.GetProperties instead.
Any other tips and guidance on the use of reflection or anonymous classes are appropriate here - I'm relatively new to this specific nook of the C# language.
UPDATE: I have to thank Jason for the key to solving this. Below is the revised code that will create a list of anonymous class instances, filling the fields of each instance from a query.
public List<T> ReturnList<T>(T sample)
{
List<T> fdList = new List<T>();
myCommand.CommandText = QueryString;
SqlDataReader nwReader = myCommand.ExecuteReader();
var properties = TypeDescriptor.GetProperties(sample);
while (nwReader.Read())
{
int objIdx = 0;
object[] objArray = new object[properties.Count];
foreach (PropertyDescriptor info in properties)
objArray[objIdx++] = nwReader[info.Name];
fdList.Add((T)Activator.CreateInstance(sample.GetType(), objArray));
}
nwReader.Close();
return fdList;
}
Note that the query has been constructed and the parameters initialized in previous calls to this object's methods. The original code had an inner/outer loop combination so that the user could have fields in their anonymous class that didn't match a field. However, in order to simplify the design, I've decided not to permit this and have instead adopted the db field access recommended by Jason. Also, thanks to Dave Markle as well for helping me understand more about the tradeoffs in using Activator.CreateObject() versus GenUninitializedObject.
Anonymous types encapsulate a set of read-only properties. This explains
Why Type.GetFields returns an empty array when called on your anonymous type: anonymous types do not have public fields.
The public properties on an anonymous type are read-only and can not have their value set by a call to PropertyInfo.SetValue. If you call PropertyInfo.GetSetMethod on a property in an anonymous type, you will receive back null.
In fact, if you change
var properties = TypeDescriptor.GetProperties(sample);
while (nwReader.Read()) {
// No way to create a constructor so this call creates the object without calling a ctor. Could this be a source of the problem?
T obj = (T)FormatterServices.GetUninitializedObject(typeof(T));
foreach (PropertyDescriptor info in properties) {
for (int i = 0; i < nwReader.FieldCount; i++) {
if (info.Name == nwReader.GetName(i)) {
// This loop runs fine but there is no change to obj!!
info.SetValue(obj, nwReader[i]);
break;
}
}
}
fdList.Add(obj);
}
to
PropertyInfo[] properties = sample.GetType().GetProperties();
while (nwReader.Read()) {
// No way to create a constructor so this call creates the object without calling a ctor. Could this be a source of the problem?
T obj = (T)FormatterServices.GetUninitializedObject(typeof(T));
foreach (PropertyInfo info in properties) {
for (int i = 0; i < nwReader.FieldCount; i++) {
if (info.Name == nwReader.GetName(i)) {
// This loop will throw an exception as PropertyInfo.GetSetMethod fails
info.SetValue(obj, nwReader[i], null);
break;
}
}
}
fdList.Add(obj);
}
you will receive an exception informing you that the property set method can not be found.
Now, to solve your problem, what you can do is use Activator.CreateInstance. I'm sorry that I'm too lazy to type out the code for you, but the following will demonstrate how to use it.
var car = new { Make = "Honda", Model = "Civic", Year = 2008 };
var anothercar = Activator.CreateInstance(car.GetType(), new object[] { "Ford", "Focus", 2005 });
So just run through a loop, as you've done, to fill up the object array that you need to pass to Activator.CreateInstance and then call Activator.CreateInstance when the loop is done. Property order is important here as two anonymous types are the same if and only if they have the same number of properties with the same type and same name in the same order.
For more, see the MSDN page on anonymous types.
Lastly, and this is really an aside and not germane to your question, but the following code
foreach (PropertyDescriptor info in properties) {
for (int i = 0; i < nwReader.FieldCount; i++) {
if (info.Name == nwReader.GetName(i)) {
// This loop runs fine but there is no change to obj!!
info.SetValue(obj, nwReader[i]);
break;
}
}
}
could be simplified by
foreach (PropertyDescriptor info in properties) {
info.SetValue(obj, nwReader[info.Name]);
}
I had the same problem, I resolved it by creating a new Linq.Expression that's going to do the real job and compiling it into a lambda: here's my code for example:
I want to transform that call:
var customers = query.ToList(r => new
{
Id = r.Get<int>("Id"),
Name = r.Get<string>("Name"),
Age = r.Get<int>("Age"),
BirthDate = r.Get<DateTime?>("BirthDate"),
Bio = r.Get<string>("Bio"),
AccountBalance = r.Get<decimal?>("AccountBalance"),
});
to that call:
var customers = query.ToList(() => new
{
Id = default(int),
Name = default(string),
Age = default(int),
BirthDate = default(DateTime?),
Bio = default(string),
AccountBalance = default(decimal?)
});
and do the DataReader.Get things from the new method, the first method is:
public List<T> ToList<T>(FluentSelectQuery query, Func<IDataReader, T> mapper)
{
return ToList<T>(mapper, query.ToString(), query.Parameters);
}
I had to build an expression in the new method:
public List<T> ToList<T>(Expression<Func<T>> type, string sql, params object[] parameters)
{
var expression = (NewExpression)type.Body;
var constructor = expression.Constructor;
var members = expression.Members.ToList();
var dataReaderParam = Expression.Parameter(typeof(IDataReader));
var arguments = members.Select(member =>
{
var memberName = Expression.Constant(member.Name);
return Expression.Call(typeof(Utilities),
"Get",
new Type[] { ((PropertyInfo)member).PropertyType },
dataReaderParam, memberName);
}
).ToArray();
var body = Expression.New(constructor, arguments);
var mapper = Expression.Lambda<Func<IDataReader, T>>(body, dataReaderParam);
return ToList<T>(mapper.Compile(), sql, parameters);
}
Doing this that way, i can completely avoid the Activator.CreateInstance or the FormatterServices.GetUninitializedObject stuff, I bet it's a lot faster ;)
Question #2:
I don't really know, but I would tend to use Activator.CreateObject() instead of FormatterServices.GetUninitializedObject(), because your object might not be created properly. GetUninitializedObject() won't run a default constructor like CreateObject() will, and you don't necessarily know what's in the black box of T...
This method stores one line of a sql query in a variable of anonymous type. You have to pass a prototype to the method. If any property of the anonymous type can not be found within the sql query, it is filled with the prototype-value. C# creates constructors for its anonymous classes, the parameters have the same names as the (read-only) properties.
public static T GetValuesAs<T>(this SqlDataReader Reader, T prototype)
{
System.Reflection.ConstructorInfo constructor = prototype.GetType().GetConstructors()[0];
object[] paramValues = constructor.GetParameters().Select(
p => { try { return Reader[p.Name]; }
catch (Exception) { return prototype.GetType().GetProperty(p.Name).GetValue(prototype); } }
).ToArray();
return (T)prototype.GetType().GetConstructors()[0].Invoke(paramValues);
}

Retrieving an Expression from a property and adding it to an expression tree

I've tried to simplify this example, as the actual code I'm playing with is more complex. So while this example may seem silly, bear with me. Let's say I'm working with the AdventureWorks database and I decide I want to add a property called Blarg to the Product table that returns an expression that contains code I would like to use in several places:
public partial class Product
{
public Expression<Func<Product, string>> Blarg
{
get { return product => product.ProductModelID.HasValue ? "Blarg?" : "Blarg!"; }
}
}
What I want to do is create an expression expression tree, have it get the Expression from Product.Blarg, and group by the result. Something like this:
var productParameter = Expression.Parameter(typeof(Product), "product");
// The Problem
var groupExpression = Expression.Lambda<Func<Product, string>>(
Expression.Invoke(
Expression.Property(productParameter, "Blarg"),
productParameter),
productParameter);
using (AdventureWorksDataContext db = new AdventureWorksDataContext())
{
var result = db.Products.GroupBy(groupExpression).ToList();
// Throws ArgumentException: "The argument 'value' was the wrong type.
// Expected 'System.Delegate'.
// Actual 'System.Linq.Expressions.Expression`1[System.Func`2[LINQ_Test.Product,System.String]]'."
}
Obviously groupExpression is incorrect (see the code comment for the exception), but I'm not sure how I should be doing it. What I thought I was saying is "get the Expression from the product.Blarg, execute it, and return the string result." I guess that's not what I'm actually saying there, though. I'm still trying to figure out expression trees. Any idea how I could pull this off?
When you call Expression.Invoke, the first argument must be an existing LambdaExpression - it can't be an Expression to a LambdaExpression. Or in other words: it isn't going to evaluate Product.Blarg per row and use a different sub-expression each time.
Instead, you would retrieve this lambda first, perhaps making it static and accessing it via reflection if you only know it by name:
var lambda = (LambdaExpression) typeof(Product)
.GetProperty("Blarg").GetValue(null,null);
And pass lambda in as the argument to Expression.Invoke; here's a fully working LINQ-to-Objects example showing this (via AsQueryable()):
using System;
using System.Linq;
using System.Linq.Expressions;
public partial class Product
{
public static Expression<Func<Product, string>> Blarg
{
get { return product => product.ProductModelID.HasValue ? "Blarg?" : "Blarg!"; }
}
public int? ProductModelID { get; set; }
static void Main()
{
var lambda = (LambdaExpression)typeof(Product)
.GetProperty("Blarg").GetValue(null, null);
var productParameter = Expression.Parameter(typeof(Product), "product");
// The Problem
var groupExpression = Expression.Lambda<Func<Product, string>>(
Expression.Invoke(
lambda,
productParameter),
productParameter);
var data = new[] {
new Product { ProductModelID = 123},
new Product { ProductModelID = null},
new Product { ProductModelID = 456},
};
var qry = data.AsQueryable().GroupBy(groupExpression).ToList();
}
}
var qry = data.AsQueryable().GroupBy(Blarg).ToList();
That works, same as Marc's code.
Note: Blarg is already correct, there is no reason to 're-invoke' it.

Categories