So I'm working on a project where I need to parse an expression tree. I got most of the things working, but I've run into a bit of a problem.
I've been looking at the other questions on StackOverflow on Expression Trees, but can't seem to find an answer to my question, so here goes.
My problem is the difference (or lack of) between constants and variables. Let me start off with an example:
user => user.Email == email
This is clearly not a constant but a variable, but this ends up being a ConstantExpression somewhere in the expression tree. If you take a look at the expression itself, it looks a bit odd:
Expression = {value(stORM.Web.Security.User+<>c__DisplayClassa)}
If we take another example:
task => task.Status != TaskStatus.Done && t.Status != TaskStatus.Failed
Here I'm using an ENUM (TaskStatus).
So my problem is that in the tree parsing I seem to end up with a ConstantExpression in both cases, and I really need to be able to tell them apart. These are just examples, so what I'm asking is a generic way of telling these two types of expression from each other, so I can handle then in 2 different ways in my parsing.
EDIT: okay, my examples might not be clear, so I'll try again. First example:
User user = db.Search < User > (u => u.Email == email);
I'm trying to find a user with the given e-mail address. I'm parsing this into a stored procedure, but that's besides the point I guess.
Second example:
IList < Task > tasks = db.Search(t => t.Status != TaskStatus.Done && t.Status != TaskStatus.Failed);
And here I'm trying to locate all tasks with a status different from Done and Failed.
Again this is being parsing into a stored procedure. In the first example my code needs to determine that the stored procedure needs a input parameter, the value of the email variable. In the second example I don't need any input parameters, I just need to create the SQL for selecting task with a status different from Done and Failed.
Thanks again
So from the point of view of expression the value is a constant. It can not be changed by the expression.
What you have is a potentially open closure - i.e. the value can change between executions of the expression, but not during it. So it is a "constant". This is a paradigm difference between the world of functional programming and un-functional :) programming.
Consider
int a =2;
Expression<Func<int, int>> h = x=> x+ a;
Expression<Func<int, int>> j = x => x +2;
a = 1;
the term a is a member access into an anonymous class that wraps up and access the a variable on the stack. The first node is a MemberAccess node then underneath that - the expression is a constant.
For the code above:
((SimpleBinaryExpression)(h.Body)).Right
{value(WindowsFormsApplication6.Form1+<>c__DisplayClass0).a}
CanReduce: false
DebugView: ".Constant<WindowsFormsApplication6.Form1+<>c__DisplayClass0>(WindowsFormsApplication6.Form1+<>c__DisplayClass0).a"
Expression: {value(WindowsFormsApplication6.Form1+<>c__DisplayClass0)}
Member: {Int32 a}
NodeType: MemberAccess
Type: {Name = "Int32" FullName = "System.Int32"}
And the constant underneath that:
((MemberExpression)((SimpleBinaryExpression)(h.Body)).Right).Expression
{value(WindowsFormsApplication6.Form1+<>c__DisplayClass0)}
CanReduce: false
DebugView: ".Constant<WindowsFormsApplication6.Form1+<>c__DisplayClass0>(WindowsFormsApplication6.Form1+<>c__DisplayClass0)"
NodeType: Constant
Type: {Name = "<>c__DisplayClass0" FullName = "WindowsFormsApplication6.Form1+<>c__DisplayClass0"}
Value: {WindowsFormsApplication6.Form1.}
}
}
The plain old 2 comes out to a:
((SimpleBinaryExpression)(j.Body)).Right
{2}
CanReduce: false
DebugView: "2"
NodeType: Constant
Type: {Name = "Int32" FullName = "System.Int32"}
Value: 2
So I don't know if that helps you or not. You can kind of tell by looking at the parent node - or the type of object being accessed by the parent node.
Adding as a result of your clarification -
so when you say
user => user.Email == email
You mean look for all users with an email equal to a passed in parameter - however that link expression means something quite different.
what you want to say is
Expression<Func<User, string, bool>> (user, email) => user.Email == email
This way the email will now be a parameter. If you don't like that there is one other thing you can do.
The second example will work just fine - no extra params are needed consts will be consts.
t => t.Status != TaskStatus.Done && t.Status != TaskStatus.Failed
Edit: adding another way:
So one of the things that you had to do to get your code working was declare a string email outside the lambda - that is kind of clunky.
You could better identify parameters by conventionally putting them in a specific place - like a static class. Then when going through the Lambda you don't have to look at some horrible cloture object - but a nice static class of your making.
public static class Parameter
{
public static T Input<T>(string name)
{
return default(T);
}
}
Then your code looks like this:
Expression<Func<User, bool>> exp = x => x.Email == Parameter.Input<String>("email");
You can then traverse the tree - when you come to a call to to the Parameter static class you can look at the type and the name (in the arguments collection) and off you go....
The name is a bit unfortunate, it is not actually a constant.
It simply refers to a value outside the Expression.
A captured variable (the first case with email) is typically a ConstantExpression representing the capture class instance, with a MemberExpression to a FieldInfo for the "variable" - as if you had:
private class CaptureClass {
public string email;
}
...
var obj = new CaptureClass();
obj.email = "foo#bar.com";
Here, obj is the constant inside the expression.
So: if you see a MemberExpression (of a field) to a ConstantExpression, it is probably a captured variable. You could also check for CompilerGeneratedAttribute on the capture-class...
A literal constant will typically just be a ConstantExpression; in fact, it would be hard to think of a scenario where you use a constant's member, unless you could something like:
() => "abc".Length
but here .Length is a property (not a field), and the string probably doesn't have [CompilerGenerated].
Just check the Type of the ConstantExpression. Any 'constant' ConstantExpression has a primitive type.
Related
The title may be ambiguous, but let me explain.
I am working with MongoDb and with the c# driver for MongoDb, we can create a FilterDefinition<T> to create a filter to fetch data as follows :
var Filter = Builders<TestClass>.Filter.Eq(x => x.AnyProperty, Value);
I am trying to put this code inside a reusable generic method, so that i don't end up writing the same code again and again. I won't be including the entire function here, but inside the function, i am trying to do something as follows :
var Filter = Builders<T>.FIlter.Eq(x => x.AnyProperty, value);
Now the drawbacks are :
T is a generic type, meaning it doesn't have the property i am looking for. So, i try to get the type of T and get the property that i am looking for by name, as follows :
...FIlter.Eq(x => x.GetType().GetProperty(PropertyName), value)
This results in an exception :
Unable to determine the serialization information for x => x.GetType().GetProperty("UserName"). // UserName is the property name
So, my question is, what can i do here for the generic type, which would be equivalent of x => x.PropertyName inside the lambda expression ?
Update
Forgot to mention, i did try this :
var Filter = Builders<T>.FIlter.Eq("PropertName", value);
But it doesn't return the results from the database, where as this does :
var Filter = Builders<MyClass>.FIlter.Eq("PropertName", value);
I really wonder why!
Update 2
Definition of Filter.Eq is as follows :
public FilterDefinition<TDocument> Eq<TField>(FieldDefinition<TDocument, TField> field, TField value);
In the code
FIlter.Eq(x => x.GetType().GetProperty(PropertyName), value), my undersatnding is that Mongo driver is expecting an Expression, which is automatically created when you use the Metadata like in original case x => x.AnyProperty
In this case you need to explicitly supply MemberExpressionas follows
var parameterExpression = Expression.Parameter(typeof(T),"x");
var memberAccessExpression = Expression.MakeMemberAccess(parameterExpression, typeof(T).GetProperty("AnyProperty"));
Now you can supply to the FIlter.Eq value memberAccessExpression, in this case it will fail at run-time, if the AnyProperty is not part of type T, since its verified at the run-time.
In ExpressionTrees, this is the replacement of the x => x.AnyProperty
Edit 1:
Reviewing the Mongo DB Driver documents, following are the important details on the Definitions and Builders, there's an example as follows:
var builder = Builders<Widget>.Filter;
var filter = builder.Eq(widget => widget.X, 10) & builder.Lt(widget => widget.Y, 20);
Following is the definition of the FilterDefinitionBuilder.Eq, which expose the Eq and various other filters:
public FilterDefinition<TDocument> Eq<TField>(Expression<Func<TDocument, TField>> field,TField value)
In this case, we need a generic type TDocument, which is the main class and TField, which is the type of the field on which filter is applied, therefore code in your case would be:
var builder = Builders<T>.Filter;
// Use makeMemberAccessExpression created above
var filter = builder.Eq(Expression.Lambda<Func<T,string>>(makeMemberAccessExpression), "<stringValue>");
Important points:
As it can be seen in the Mongo documentation, we have 2 generic types, TDocument and TField, here you are working with just one, so with the above code it will be restricted to string as type for TField, until and unless you make that also generic, which is your choice, otherwise all your fields shall be of specific type that you supply, which is string in this case
More important is the value shall be of type TField that you supply else it will not work and will fail at compile time
There's another overload, Eq<TField>(FieldDefinition<TDocument, TField>, TField), which would work in a similar way, but since it expects FieldDefinition<TDocument, TField>, we need to supply the Expression<Func<TDocument,TField>> as part of the class, both overloads translates into similar code
I'm currently trying to find invocations of .ExecuteSqlCommand and examine the first value being passed to the sql param.
Here is an example of the differences I've found in our code base.
ExecuteSqlCommand("[sql statement here]");
vs.
var sql = "sql statement";
ExecuteSqlCommand(sql);
So far, I have this:
var invocations = root.DescendantNodes()
.OfType<InvocationExpressionSyntax>()
.Select(ie => ModelExtensions.GetSymbolInfo(model, ie).Symbol)
.Where(symbol => symbol != null && symbol.Name == "ExecuteSqlCommand");
foreach (var invocation in invocations)
{
var method = (IMethodSymbol)invocation;
foreach (var param in method.Parameters)
{
//I can't quite seem to get information from IParameterSymbol about whether the param is a string literal, or a reference to a string via a variable.
}
}
If the param is not a string, and instead, a var, then I'll need to get the value of the var (as much as it's defined at runtime).
I'm not too sure if this is a job for the SemanticModel or the SyntaxTree, but my GUESS is that the SemanticModel should have the richer information I need to let me discover what I'm looking for.
My overall goal is to interrogate the sql being passed to the ExecuteSqlCommand method.
Thanks!
SemanticModel.GetConstantValue is the API we have for handling this situation.
It can accept both a syntax node and an expression. You will still need to track the state of variables back to their declaration sites and determine if they were given a constant expression.
I would use SemanticModel.GetSymbolInfo.Symbol?.DeclaringSyntaxReferences.First() to find the declaration site of a variable and then check to see if its a constant expression.
The Syntax API could be used to extract the sql statement value but depends on whether the variable declaration (i.e. var sql = "sql statement";) is included as part of code submitted to the syntax tree.
For example, if it's part of the same method implementation as where ExcuteSqlCommand() is called then you can first get the name of variable (i.e. sql) passed to it and use that to find the matching variable declaration statement within that same method. Finally, the sql statement value (i.e. "sql statement") can be extracted from that.
The following code first checks if the sql value is passed as a string literal otherwise looks for the variable declaration. The assumption it's all within the same method:
// whatever c# *method* code contains the sql.
// otherwise the root of the tree would need to be changed to filter to a specific single `MethodDeclarationSyntax`.
string submittedCode = "public void SomeMethodContainingSql(){ ...//rest of code...";
var tree = CSharpSyntaxTree.ParseText(submittedCode);
var root = (CompilationUnitSyntax) tree.GetRoot();
var arguments = root
.DescendantNodes()
.OfType<InvocationExpressionSyntax>()
.First(node => node.DescendantNodes().OfType<IdentifierNameSyntax>()
.First()
.Identifier.Text == "ExecuteSqlCommand")
.ArgumentList.DescendantNodes().ToList();
string sqlStatementValue = "";
var literalExpression = arguments.OfType<LiteralExpressionSyntax>().FirstOrDefault();
if (literalExpression != null)
{
sqlStatementValue = literalExpression.GetText().ToString();
}
else
{
var variableName = arguments
.First()
.ToFullString();
var variableDeclaration = root
.DescendantNodes()
.OfType<VariableDeclarationSyntax>()
.Single(node => node.DescendantNodes().OfType<VariableDeclaratorSyntax>()
.First()
.Identifier.Text == variableName);
sqlStatementValue = variableDeclaration.DescendantNodes()
.OfType<LiteralExpressionSyntax>()
.First()
.DescendantTokens()
.First()
.Text;
}
Otherwise, may need to look for the variable declaration in other parts of the submitted code (ex. class fields, properties, other methods, etc.) which is a bit more cumbersome.
Unfortunately Roslyn cannot provide a variable's value in a common cases when values is defined at runtime, because Roslyn actually doesn't know all possible values which may pass from outside the program, it doesn't calculate them and so on so forth. But if you can limit the needed cases to a inlining strings or variables which was declared and initialized at strings, Roslyn may help you with this:
You need to keep InvocationExpressionSyntax (or directly their first arguments)
var invocations = root.DescendantNodes()
.OfType<InvocationExpressionSyntax>()
.Select(ie => (ModelExtensions.GetSymbolInfo(model, ie).Symbol, ie))
// Would be better to compare not method's name but it FQN
.Where((symbol, node)) => symbol != null && symbol.Name == "ExecuteSqlCommand" &&
symbol.Parameters.Length == 1 &&
symbol.Parameters[0].Type.SpecialType == SpecialType.System_String &&
node.ArgumentList.Arguments.Count == 1)
.Select((symbol, node) => node);
You need to check not method parameter, but method argument which was passed to it
foreach (var invocation in invocations)
{
var argument = invocation .ArgumentList.Arguments[0];
if (argument is LiteralExpressionSyntax literal && literal.IsKind(SyntaxKind.StringLiteralExpression))
{
// You find invocation of kind `ExecuteSqlCommand("sql")`
}
else
{
var argSymbol = ModelExtensions.GetSymbolInfo(model, argument).Symbol;
if (!(argSymbol is null) && (argSymbol.Kind == SymbolKind.Field || argSymbol.Kind == SymbolKind.Property || argSymbol.Kind == SymbolKind.Local))
{
if (argSymbol.DeclaringSyntaxReferences.Length == 1)
{
var declarationNode = argSymbol.DeclaringSyntaxReferences[0].GetSyntax();
// I'm actually don't remember what exactlly `GetSyntax` returns for fields or locals:
// VariableDeclaratorSyntax or one of it parent LocalDeclarationStatementSyntax and FieldDeclarationSyntax, but if it returns declarations you also can
// get from them VariableDeclaratorSyntax that you need, it's just be a more deep pattern matching
if (declarationNode is VariableDeclaratorSyntax declaratorSyntax &&
declaratorSyntax.EqualsValueClauseSyntax?.Value is LiteralExpressionSyntax literal2 && literal2.IsKind(SyntaxKind.StringLiteralExpression) )
{
// You find invocation of kind `ExecuteSqlCommand(variable)` where variable is local variable or field
}
else if (declarationNode is PropertyDeclarationSyntax property)
{
// You can do the same things for properties initializer or expression body that you was do for fields and locals to check your case,
// but it doesn't work for property with get/set accessors in a common cases
}
}
}
}
}
EDIT: Updated example to what I'm actually doing.
EDIT 2: The why:
My class "takes hold" of a property setter (or method, anonymous or not) and "controls" it for a period of time. The user could try and attach another instance of my class and attach that to a property that is already "controlled." I need a way to detect these conflicts reliably. I could forego the () => { property } method but that requires the user to wrap the target property in a method and, if he/she wants conflict detection, to pass in the object containing the property. Doing it that way introduces higher risk of bugs as they may type in the wrong object and the class wouldn't have any means to check for them.
I am unable to utilize the Lambda Compile() because I am targeting AOT in addition to JIT.
I have a method that takes an
Expression<Func<T>>
as an argument. The Expression always evaluates to a property with a setter of Type T.
private static TargetInfo GetTargetInfo<T>(Expression<Func<T>> _propertyExpression, out Action<T> _setter)
{
//Get Property info
var propInfo = ((MemberExpression)_propertyExpression.Body).Member as PropertyInfo;
if (propInfo == null)
throw new ArgumentException("_propertyExpression must be a property.");
var declType = propInfo.DeclaringType;
var methodInfo = propInfo.GetSetMethod(true) ?? declType.GetProperty(propInfo.Name).GetSetMethod(true);
if (methodInfo == null)
throw new Exception("Could not create setter from property '" + _propertyExpression + "'");
var isStaticProp = methodInfo.IsStatic;
//Get Target Object
object targetObject = null;
var memberExp = _propertyExpression.Body as MemberExpression;
var bodyExp = memberExp.Expression;
if (bodyExp != null)
// PROBLEM LINE BELOW - Can't use Compile() *********************************
targetObject = Expression.Lambda<Func<object>>(bodyExp).Compile()();
// PROBLEM LINE ABOVE *******************************************************
else if (isStaticProp)
targetObject = memberExp.Member.DeclaringType;
else
throw new Exception("Could not determine target object. Use Action<T> overload. (no conflict detection)");
//Cache setter of property
if (isStaticProp)
_setter = (Action<T>) Delegate.CreateDelegate(typeof (Action<T>), methodInfo);
else
_setter = (Action<T>)Delegate.CreateDelegate(typeof(Action<T>), targetObject, methodInfo);
return new TargetInfo(methodInfo, targetObject);
}
The expression (_propertyExpression) is always of a property. And the property always has a setter.
I need to extract the property's setter as Action (which I can do
no problem)
I also need the instance of the object containing this
property without using Compile() as it is not available on some of
the target platforms I am targeting.
The reason I need the instance is to determine whether or not another object instance of my class is currently utilizing that property SETTER on THAT object. (No Threading, just usage over a span of time) I store the object Instance and property setter info together as a key, if two keys that are equal that exist then there is a conflict and it is handled based on the user's configuration settings of my class.
I am able to get the object instance for simple expressions like:
() => MyProperty
or
() => MyObjectInst.PropertyFoo
or
() => MyStaticClass.PropertyFooBar
No Sweat!
But what if the user does this:
//Need instance of 'someObject' here.
() => SomeArray[idx].someObject.propertyA
or blow my top:
//Need instance of 'someField[4]' here
() => SomeStaticClass.somePropertyList[5].SomeDictionary[objKey].SomeProperty.someFieldArray[4].propertyB
AAAK!
I've been looking at this for a while and I BELIEVE that I need to traverse the tree BACK to the base object (ConstantExpression) then start evaluating back UP the tree using the info collected walking to the front. Given the examples above I decided that I need "bigger guns".
Is there an easier way to get the property's caller's instance other than completely walking the tree WITHOUT using Compile()? Please explain if so!
If not 1. If anyone could point me in the right direction on how to traverse the tree and handle stuff like the above. That would be great. I'm not finding a whole lot of info on traversing Expression Trees, expression types, etc. Even a reference to a recommended book specific to the subject would be helpful.
sigh Should I just give up and force the user to use simple expressions.
ie. Do not allow the user to type in lengthy property access lines.
Clarification:
The setter is being cached for use by a mechanism class "FOO" that will be setting this value an UNKNOWN range of values of type T over some defined period of time. The object instance is cached and used as a key to detect the case that another instance of FOO tries to attach and set values to the same property on that same object. The object instance is used to detect these conflicts and also to keep track of all of the "FOO"s attached to any particular object. The user can attach multiple "FOO"s to the same object, to different properties. In the case that the user attaches an instance of FOO to a property on an object that already has a FOO attached for that property, a conflict event occurs. FOO can be configured on how to handle when that happens.
Here's the problems with your suggested approaches:
Is there an easier way to get the property's caller's instance other
than completely walking the tree WITHOUT using Compile()? Please
explain if so!
Short answer, no. Expressions are meant to be compiled. Either into .NET Funcs/Actions or some other platform (LINQ-to-Entities essentially compiles them into SQL). If you don't want to compile it, you're probably using the wrong tool.
If not 1. If anyone could point me in the right direction on how to
traverse the tree and handle stuff like the above. That would be
great. I'm not finding a whole lot of info on traversing Expression
Trees, expression types, etc. Even a reference to a recommended book
specific to the subject would be helpful.
The best way to traverse an Expression tree is by inheriting from ExpressionVisitor. A good blog to start with would be Matt Warren's blog http://blogs.msdn.com/b/mattwar/, read the series on implementing IQueryable. But no, it's not easy.
One could (theoretically) inherit from ExpressionVisitor to walk down/up the chain to the original instance. But you would then have to walk back up/down the chain somehow, and the easiest way to do that would be by compiling the child Expressions. You could, theoretically, use Reflection to walk back, as your answer tries, but I hope you're distinctly getting the impression you have chosen the wrong tool if your No-Compiling stance is iron-clad.
sigh Should I just give up and force the user to use simple expressions.
You haven't outlined the benefits/drawbacks of this.
I have confirmed that the below works on for MONO -> iOS... At least with the basics and arrays. Haven't been able to get the Dictionary/List rolling. It seems AOT doesn't like MethodInfo.Invoke.
EDIT:
I found this answer on MSDN:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/af305a45-36e4-4607-9190-f1b33a0bea57/get-class-instance-from-lambda-expression
It traverses the Expression tree, evaluating each part of the tree as necessary and finally returns the containing object of the property specified.
Put in this (along with adding the method code below in):
targetObject = GetContainer(_propertyExpression);
instead of this:
targetObject = Expression.Lambda<Func<object>>(bodyExp).Compile()();
in the code above.
I have yet to do full testing on it on other platforms and I'm sure I'll run into other cases where I need to add more cases but this might be enough to help someone else and enough to keep me going and I'll try and keep this updated as I run into more:
public static object GetContainer<T>(Expression<Func<T>> propertyLambdaExpression)
{
return Evaluate((propertyLambdaExpression.Body as MemberExpression).Expression);
}
public static object Evaluate(Expression e)
{
switch (e.NodeType)
{
case ExpressionType.Constant:
return (e as ConstantExpression).Value;
case ExpressionType.MemberAccess:
{
var propertyExpression = e as MemberExpression;
var field = propertyExpression.Member as FieldInfo;
var property = propertyExpression.Member as PropertyInfo;
var container = propertyExpression.Expression == null ? null : Evaluate(propertyExpression.Expression);
if (field != null)
return field.GetValue(container);
else if (property != null)
return property.GetValue(container, null);
else
return null;
}
case ExpressionType.ArrayIndex: //Arrays
{
var arrayIndex = e as BinaryExpression;
var idx = (int)Evaluate(arrayIndex.Right);
var array = (object[])Evaluate(arrayIndex.Left);
return array[idx];
}
case ExpressionType.Call: //Generic Lists and Dictionaries
{
var call = e as MethodCallExpression;
var callingObj = Evaluate(call.Object);
object[] args = new object[call.Arguments.Count];
for (var idx = 0; idx < call.Arguments.Count; ++idx)
args[idx] = Evaluate(call.Arguments[idx]);
return call.Method.Invoke(callingObj, args);
}
default:
return null;
}
}
I am trying to create an query extension which would compare a nullable int sql column value with a value. But i am struggling already over 8 hours to find any working solution.
I have already found a lot of help on this side. But all the remarks did not helped me.
I have altered the code so many times, but nothing seems to work. I want to create something similar as WHERE ManagerID IN (10,20,30)
The main code
IQueryable<Users> query = _context.CreateObjectSet<Users>();
query = query.IsMember(a => a.ManagerID, new Int32?[] { 10,20,30 });
return query.ToList();
Currently while executing the query.ToList(); it returns me a
Unable to create a constant value of type 'System.Object'. Only primitive types or enumeration types are supported in this context.
public static IQueryable<T> IsMember<T>(this IQueryable<T> source, Expression<Func<T, Int32?>> stringProperty, params Int32?[] searchTerms)
{
if (searchTerms == null || !searchTerms.Any())
{
return source;
}
Expression orExpression = null;
foreach (var searchTerm in searchTerms)
{
var searchTermExpression = Expression.Constant(searchTerm, typeof(object)); // <<--- This cast would make it no longer a primitive type
var containsExpression = Expression.Call(stringProperty.Body, typeof(Int32?).GetMethod("Equals"), searchTermExpression);
orExpression = BuildOrExpression(orExpression, containsExpression);
}
var completeExpression = Expression.Lambda<Func<T, bool>>(orExpression, stringProperty.Parameters);
return source.Where(completeExpression);
}
private static Expression BuildOrExpression(Expression existingExpression, Expression expressionToAdd)
{
return existingExpression == null ? expressionToAdd : Expression.OrElse(existingExpression, expressionToAdd);
}
The line marked to give the constant another datatype, is indeed causing the issue, but if I dont make it of type object, the Expression will not work, as it could not match the Int32? datatype.
Can anybody help me?
thanks
=============================================
Additional info
It is indeed to had a a larger picture.
I just want to create something more dynamic which could be used on other projects also.
I would like to use some functions which will look appealing than all those multilines
query = query.Like(a => a.UserName, filter.UserName, true);
query = query.Equals(a => a.UserTown, filter.UserTown, true);
query = query.IsMember(a => a.Division, filter.Division); // is an array of possible divisions
it worked fine for Like and Equals which are string based. But want to have a similar product for (nullable) integers
I was inspired by the following post. Which created a search function (which i renamed for my project to Like)
link
I wanted to create others similar. the last boolean is to verify if nullable in the column is allowed or not.
The reason also to use an extension, is that i also have alot of filters in my filter page.
With this extension, i would easily check in the beginning of my Like and Equal function if a filter was given without checking if my filter has a value 20x.
public static IQueryable<T> Like<T>(this IQueryable<T> source, Expression<Func<T, string>> stringProperty, string searchTerm, bool isnullValueAllowed)
if (String.IsNullOrEmpty(searchTerm))
{
return query;
}
It's not clear why you want to create this extension as you could simply write something like:
query.Where(user => (new[]{10,20,30}).Contains(user.ManagerId)).ToList();
However, assuming the real use case is somewhat more complicated than the example you've given, could you not construct your expression as either a comparison against a constant Int32 or a comparison against null, depending on whether searchTerm.HasValue() was true?
You need to use the equality operator == rather than the Equals method here. It actually makes your expression code a tad simpler:
foreach (var searchTerm in searchTerms)
{
var comparison = Expression.Equals(stringProperty.Body,
Expression.Constant(searchTerm));
orExpression = BuildOrExpression(orExpression, comparison);
}
Of course, as is mentioned by others, you don't need to build up an expression representing an OR of each of these comparisons, you can simply use Conatains on the set in a lambda and the query provider will do all of this for you.
I have C# a method that is declared as the following:
public IEnumerable<ClientEntity> Search(Func<Client, bool> searchPredicate)
{
// Uses the search searchPredicate to perform a search.
}
This method gets called with something like:
string searchCriteria = "My Search Criteria";
bool searchOnlyActive = false;
myClientService.Search(c => c.Name.Contains(searchCriteria) && (c.Active || !searchOnlyActive));
Now, if I throw a breakpoint at the beginning of that method and I look at the searchPredicate properties in the Immediate Window, when I type searchPredicate.Target, I get something like this:
{MyNamespace.ClientsService.}
searchCriteria: "My Search Criteria"
searchOnlyActive: false
What I would like is to actually get the "My Search Criteria" value and the false value displayed there, like the debugger does, but I didn't manage to as the type of the Target property is something like "<>c__DisplayClass2" which I have no idea where that came from. I know it can be done because the debugger does it, I just don't know how.
Any ideas? Thanks!
<>c__DisplayClass2 is the class that the compiler invented to get the capture context. You can just use reflection:
object target = searchPredicate.Target;
if(target != null) {
foreach(var field in target.GetType().GetFields()) {
Console.WriteLine("{0}={1}", field.Name, field.GetValue(target));
}
}
which outputs:
searchCriteria=My Search Criteria
searchOnlyActive=False
However! Unless you understand anonymous methods and captured variables (and how that is implemented in terms of compiler-generated context classes), I don't think this will do what you want it to; for example, there could be no context (a Target that is null), or multiple nested contexts...
Also: expression trees via Expression<Func<Client,bool>> are far more inspectable, if that is your intent.