Let us have the following:
Func<string, int> counterOfChar = (myString) => {
Console.WriteLine("Here is my parameter "+myString);
return myString.Count();
};
I want to bring all expressions involved here , by defining them so:
Expression<Action<string>> first = (param) => Console.WriteLine("Here is my parameter "+param);
Expression<Func<string, int>> second = (param) => param.Count();
And then call Expression.Block(first, second); as an example .
I am struggling for a week now and I don't want to tell you how diverse are the errors received until this moment.
Can someone write the corresponding Block and lambda expression for the delegate, but not going deep into ex: Method.Call ? Just stick to expressions !?
Thank you!
Expression<Action<string>> first = (param) => Console.WriteLine("Here is my parameter " + param);
Expression<Func<string, int>> second = (param) => param.Length; // .Count() is useless here!
Expression<Func<string, int>> third =
Expression.Lambda<Func<string, int>>(
Expression.Block(first.Body,
Expression.Invoke(second, first.Parameters[0])),
first.Parameters[0]);
var f = third.Compile();
var r1 = f("Hello");
"merging" two Expressions is always a little complex, because the two param of the two expressions are "different". They aren't the same param (it's like one is param1 and the other is param2). Here we resolve it by reusing the first parameter of the first expression as the parameter of the "new" expression and Expression.Invoke the other expression.
Without cheating, we could have
var par = Expression.Parameter(typeof(string));
Expression<Func<string, int>> third =
Expression.Lambda<Func<string, int>>(
Expression.Block(
Expression.Invoke(first, par),
Expression.Invoke(second, par)),
par);
var f = third.Compile();
var r1 = f("Hello");
where we introduce a new parameter par and we Expression.Invoke the other two expressions.
Note that Entity Framework doesn't support Expression.Invoke. In this case you can use a parameter rewriter (something like this.)
Related
I have a fairly simple javascript method
(props,propName,componentName) => {
var value = props[propName];
const getOrSpread = name =>
props[name] || props.spread && props.spread[name];
// remainder of function code omitted
}
that is working in javascript land. I'm trying to convert it to fable but either I can get it to have a definitely exists property access to .spread or dynamic access to props[propName] but not both
module JsxHelpers =
type IReactProps =
abstract member spread : obj
let isfuncOrNullPropType (props:IReactProps) (propName:string) componentName =
let propsO :obj = box props
let value:obj = propsO?propName
let valueAttempt2:obj = (box props)?(propName)
// rest of translation not attempted yet
value
where if props is defined as IReactProps, then .spread works, but neither of the two possible let value lines compile.
or props is defined as obj and it says `This expression was expected to have type 'obj' but here has type ''a -> obj'
even the simplest object from the documentation doesn't appear to compile:
let isfuncOrNullPropType (props:obj) (propName:string) =
let value2:obj = props?propName
value2
using "fable-core": "^1.0.0-narumi-905"
You definitely need to put the prop name in parentheses according to the documentation. The compiler error you're getting is because props?(propName) returns type 'a -> obj. Apparently, the dynamic (?) operator returns an Applicable, and from the fable source:
/// DO NOT USE: Internal type for Fable dynamic operations
type Applicable = obj->obj
Perhaps try:
let value : obj = unbox<obj> (props?(propName))
I'm trying to use Expression.Call on a generic, static Method.
Unfortunately, there is no signature of the Call Method allowing to give generic type arguments AND a method info argument.
Is this possibly in any way?
What I'm concretely trying to do is to write a helper class that can sort an IEnumerable(DataRow) via Linq dynamically.
Unfortunately, I have to use the DataRowExtensions to get the Field I want to sort in the Lambda expression.
The original code comes from http://aonnull.blogspot.de/2010/08/dynamic-sql-like-linq-orderby-extension.html.
The (experimental) piece of code looks at the moment as follows:
//T is DataRow
Type type = typeof(T);
IEnumerable<MethodInfo> extensions = GetExtensionMethods(Assembly.GetAssembly(typeof(DataRowExtensions)), typeof(DataRow));
ParameterExpression arg = Expression.Parameter(typeof(DataRow), "x");
//at Position 0 there is T Field<T>(string)
MethodInfo mi = extensions.ToList()[0];
var methArg = Expression.Parameter(typeof(String), "\"" + orderByInfo + "\"");
MethodCallExpression expr = Expression.Call(null, mi, arg, methArg);
Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), typeof(Object));
LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
When Runtime comes to the Expression.Call Statement an Exception is thrown saying that the Field-Method is generic.
So, yes. When you get MethodInfo for Field method, you really get generic method, without specify what type for return value.
T Field<T>(string)
For solving, just use MakeGenericMethod from this method with needed type, like
MethodInfo mi = extensions.ToList()[0].MakeGenericMethod(typeof(object));
here mi already specify generic parameter and present function
object Field(string);
Also you a bit complicate your code, so you can a bit simplified it and get something like this
//T is DataRow
Type type = typeof(T);
IEnumerable<MethodInfo> extensions = GetExtensionMethods(Assembly.GetAssembly(typeof(DataRowExtensions)), type);//if T is DataRow not needed get typeof again
ParameterExpression arg = Expression.Parameter(typeof(DataRow), "x");
//at Position 0 there is T Field<T>(string)
MethodInfo mi = extensions.ToList()[0].MakeGenericMethod(typeof(object));//you can change object type to needed type
var methArg = Expression.Parameter(typeof(String), orderByInfo);//if orderByInfo already string, then not needed wrap it in quotes
LambdaExpression lambda = Expression.Lambda<Func<T,string,object>>(
Expression.Call(mi, arg, methArg), //call mi with args: arg, methArg
arg,methArg);//pass parameters
Sidenote: you can not specify name for parameter, in this case name would be generate automatically, like: Param_0,Param_1 and etc.
You anyway not use parameter name directly.
I want to check if a variable of type guid exist. I used
new Db().JobCreate.Any(a => a.GuidVariable1.Equals(GuidVariable2, StringComparison.OrdinalIgnoreCase));
but I get the error Member 'object.Equals(object, object)' cannot be accessed with an instance reference; qualify it with a type name instead
How can I fix this?
I'm assuming you have another instance variable named GuidVariable2.
Unless there's something else involved I'd just do the following:
new Db().JobCreate.Any(a => a.GuidVariable1 == GuidVariable2);
If the variables are actually strings I'd do the following:
new Db().JobCreate.Any(a => a.GuidVariable1.ToLower() == GuidVariable2.ToLower());
Update based on comment:
Guid represents a hexidecimal number, so the case of the alpha digits is irrelevant. When represented as a string, case could be Upper, Lower or a combination but the actual number is the same:
var guid1 = Guid.Parse("a0449976-604e-4bdf-826d-234c4564c3e0");
var guid2 = Guid.Parse("A0449976-604E-4BDF-826D-234C4564C3E0");
var guid3 = Guid.Parse("A0449976-604E-4bdf-826d-234c4564c3e0");
guid1 == guid2; //true
guid2 == guid3; //true
Here you go:
new Db().JobCreate.Any(a => string.Equals(
a.GuidVariable1, GuidVariable2, StringComparison.OrdinalIgnoreCase));
(this is of course assuming your so-called GUIDs are actually strings, if you want to compare GUIDs just do this:)
new Db().JobCreate.Any(a => a.GuidVariable1.Equals(GuidVariable2));
I've got a form with multiple fields on it (company name, postcode, etc.) which allows a user to search for companies in a database. If the user enters values in more than one field then I need to search on all of those fields. I am using LINQ to query the database.
So far I have managed to write a function which will look at their input and turn it into a List of expressions. I now want to turn that List into a single expression which I can then execute via the LINQ provider.
My initial attempt was as follows
private Expression<Func<Company, bool>> Combine(IList<Expression<Func<Company, bool>>> expressions)
{
if (expressions.Count == 0)
{
return null;
}
if (expressions.Count == 1)
{
return expressions[0];
}
Expression<Func<Company, bool>> combined = expressions[0];
expressions.Skip(1).ToList().ForEach(expr => combined = Expression.And(combined, expr));
return combined;
}
However this fails with an exception message along the lines of "The binary operator And is not defined for...". Does anyone have any ideas what I need to do to combine these expressions?
EDIT: Corrected the line where I had forgotten to assign the result of and'ing the expressions together to a variable. Thanks for pointing that out folks.
You can use Enumerable.Aggregate combined with Expression.AndAlso. Here's a generic version:
Expression<Func<T, bool>> AndAll<T>(
IEnumerable<Expression<Func<T, bool>>> expressions) {
if(expressions == null) {
throw new ArgumentNullException("expressions");
}
if(expressions.Count() == 0) {
return t => true;
}
Type delegateType = typeof(Func<,>)
.GetGenericTypeDefinition()
.MakeGenericType(new[] {
typeof(T),
typeof(bool)
}
);
var combined = expressions
.Cast<Expression>()
.Aggregate((e1, e2) => Expression.AndAlso(e1, e2));
return (Expression<Func<T,bool>>)Expression.Lambda(delegateType, combined);
}
Your current code is never assigning to combined:
expr => Expression.And(combined, expr);
returns a new Expression that is the result of bitwise anding combined and expr but it does not mutate combined.
EDIT: Jason's answer is now fuller than mine was in terms of the expression tree stuff, so I've removed that bit. However, I wanted to leave this:
I assume you're using these for a Where clause... why not just call Where with each expression in turn? That should have the same effect:
var query = ...;
foreach (var condition in conditions)
{
query = query.Where(condition);
}
Here we have a general question about combining Linq expressions. I have a general solution for this problem. I will provide an answer regarding the specific problem posted, although it's definitely not the way to go in such cases. But when simple solutions fail in your case, you may try to use this approach.
First you need a library consisting of 2 simple functions. They use System.Linq.Expressions.ExpressionVisitor to dynamically modify expressions. The key feature is unifying parameters inside the expression, so that 2 parameters with the same name were made identical (UnifyParametersByName). The remaining part is replacing a named parameter with given expression (ReplacePar). The library is available with MIT license on github: LinqExprHelper, but you may quickly write something on your own.
The library allows for quite simple syntax for combining complex expressions. You can mix inline lambda expressions, which are nice to read, together with dynamic expression creation and composition, which is very capable.
private static Expression<Func<Company, bool>> Combine(IList<Expression<Func<Company, bool>>> expressions)
{
if (expressions.Count == 0)
{
return null;
}
// Prepare a master expression, used to combine other
// expressions. It needs more input parameters, they will
// be reduced later.
// There is a small inconvenience here: you have to use
// the same name "c" for the parameter in your input
// expressions. But it may be all done in a smarter way.
Expression <Func<Company, bool, bool, bool>> combiningExpr =
(c, expr1, expr2) => expr1 && expr2;
LambdaExpression combined = expressions[0];
foreach (var expr in expressions.Skip(1))
{
// ReplacePar comes from the library, it's an extension
// requiring `using LinqExprHelper`.
combined = combiningExpr
.ReplacePar("expr1", combined.Body)
.ReplacePar("expr2", expr.Body);
}
return (Expression<Func<Company, bool>>)combined;
}
Assume you have two expression e1 and e2, you can try this:
var combineBody = Expression.AndAlso(e1.Body, Expression.Invoke(e2, e1.Parameters[0]));
var finalExpression = Expression.Lambda<Func<TestClass, bool>>(combineBody, e1.Parameters).Compile();
I've written a number of methods (.WhereOr, .WhereAnd) which basically allow me to "stack up" a bunch of lambda queries, and then apply them to a collection. For example, the usage with datasets would be a little like this (although it works with any class by using generics):
WITH LINQ TO DATASETS (Using the .NET DataSetExtensions)
DataTable Result;
List<Expression<Func<DataRow, bool>> Queries = new List<Expression<Func<DataRow, bool>>();
Queries.Add(dr=> dr.Field<string>("field1") == "somestring");
Queries.Add(dr=> dr.Field<string>("field2") == "somestring");
Queries.Add(dr=> dr.Field<string>("field3") == "somestring");
Result = GetSomeTable().AsEnumarable().WhereOr(Queries).CopyToDataTable();
Now say that in the example above only one row in the collection matches "somestring", and it's on field "field2".
That means that the count for Result should be 1.
Now, say I re-write the code above slightly to this:
DataTable Result;
List<Expression<Func<DataRow, bool>> Queries = new List<Expression<Func<DataRow, bool>>();
List<string> columns = new string[]{"field1","field2","field3"}.ToList();
string col;
foreach(string c in columns){
col = c;
Queries.Add(dr=> dr.Field<string>(col) == "somestring");
}
Result = GetSomeTable().AsEnumarable().WhereOr(Queries).CopyToDataTable();
Now, I don't really understand expressions, but to me both examples above are doing exactly the same thing.
Except that "Result" in the first example has a count of 1, and "Result" in the second example has a count of 0.
Also, in the List columns in the second example, if you put "field2" last, instead of second, then "Result" does correctly have a count of 1.
So, from all this I've come to a kind of conclusion, but I don't really understand what's happening, nor how to fix it..? Can I "evaluate" those expressions earlier...or part of them?
CONCLUSION:
Basically, it seems like, if I send literal values into there, like "field1", it works. But if I send in variables, like "col", it doesn't work, because those "expressions" are only getting evaluated much later in the code.
that would also explain why it works when I move "field2" to the last position. it works because the variable "col" was assigned to "field2" lastly, thus by the time the expressions evaluate "col" equals "field2".
Ok, so, is there any way around this??
Here's the code for my WhereOr method (it's an extension method to IENumerable):
public static IQueryable<T> WhereOr<T>(this IEnumerable<T> Source, List<Expression<Func<T, bool>>> Predicates) {
Expression<Func<T, bool>> FinalQuery;
FinalQuery = e => false;
foreach (Expression<Func<T, bool>> Predicate in Predicates) {
FinalQuery = FinalQuery.Or(Predicate);
}
return Source.AsQueryable<T>().Where(FinalQuery);
}
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> Source, Expression<Func<T, bool>> Predicate) {
InvocationExpression invokedExpression = Expression.Invoke(Predicate, Source.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>(Expression.Or(Source.Body, invokedExpression), Source.Parameters);
}
The "how to fix" answer. Change this:
string col;
foreach(string c in columns) {
col = c;
Queries.Add(dr=> dr.Field<string>(col) == "somestring");
}
to this:
foreach(string c in columns) {
string col = c;
Queries.Add(dr=> dr.Field<string>(col) == "somestring");
}
Enjoy. The "what & why" answer was given by Brian.
I don't even bother reading the question bodies any more, I just read the title and then say
See
http://lorgonblog.spaces.live.com/blog/cns!701679AD17B6D310!689.entry
and
http://blogs.msdn.com/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx
Oh geez, after I commented I saw the issue. You are using the same variable, "col", in every iteration of the loop. When you build the lambda expression, it doesn't bind to the value of the variable, it references the variable itself. By the time you execute the query, "Col" is set to whatever it's last value is. Try creating a temporary string inside the loop, setting its value, and using that.
I found great article to fulfill your requirements.
The link provides And<T> and Or<T> extension method to make it.
http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx