I'm trying to understand how dynamic expressions work. So for learning purposes I'd like to do the following:
I have an object which I can currently access with a Linq statement that uses a lambda expression:
someObj.IncludeStory(x => x.News);
What I'd like to do is replace the labmda x => x.Newswith a string, for example:
string myLambda = "x => x.News";
someObj.IncludeStory(myLambda);
Obviously you can't do it like that, but as far as I understand you can achieve somewhat the same with Dynamic Expressions(?).
I've been looking at the System.Linq.Dynamic source code to get an idea of how this should work. But that only confuses me more. I think that library is far to complex for what I want. I don't need sorting, grouping and all that fancy stuff.
Basically my questions are:
Can I use Dynamic Expressions to generate a lambda like this dynamicaly: x => x.News?
If so, then how would I do this with a Dynamic Expression?
I find it hard to get started with this.
What I've tried is something like:
var expression = #"IncludeStory(x => x.News)";
var p = Expression.Parameter(someObj.GetType(), "News");
var e = myAlias.DynamicExpression.ParseLambda(new[] { p }, null, expression);
var result1 = e.Compile().DynamicInvoke(someObj);
You can use DynamicExpression.ParseLambda to convert a string into an Expression Tree. For more detail go and see the project that comes with VS2010 C:\Program Files (x86)\Microsoft Visual Studio 10.0\Samples\1033 -> CSharpSamples -> LinqSamples -> DynamicQuery (Also I think is part of the installation of higher versions)
Related
I have to perform find/replace functionality in my app and we decided to add ignore space option to our regular expression Mongo query. so using this code
BsonRegularExpression breg = new BsonRegularExpression (pattern,request.matchCase ? "x" : "xi");
It produces this string that is later be queried in mongo {/patter/xi}
But it turned out in order to use "x" and "s" option you have to use $options: format in mongoDB below is the exact quote from MongoDB Documentation
To use either the x option or s options, you must use the $regex operator expression with the $options operator. For example, to specify the i and the s options, you must use $options for both
So now I do not know how I can make MongoDB C# driver to produce the query that I want or if there is any other work around.
these are two possibilities i know of.
var spec = new Document("someName", new MongoRegex("your regex", "x"));
yourCollection.Find(spec)
second approach
var collection = GetCollection();
var filter = Builders<Entity>.Filter.Regex("x", new BsonRegularExpression("your regex, "i"));
return await collection.Find(filter).FirstOrDefaultAsync();
for more info checkout on the BsonRegularExpression method check out http://api.mongodb.com/java/current/org/bson/BsonRegularExpression.html
How can I get value of x from string equation using c#.
string eq = sin(x) = 5x-2""
eq = "x=(1/5)*(sin(x)+2)"
Is it even possible?
Poor quality question aside, what you want is either to create your expression parser which will then either compile or interpret the expression, OR you could use an open source library like NCalc:
http://ncalc.codeplex.com/
This is built to do this sort of thing.
I am developing a parser for a language similar to SQL and I have the problem of creating some of the rules of language, such as: expression IS NULL and expression IN (expression1, expression2, ...) with priority between logical and mathematical operators.
I uploaded a GitHub test project https://github.com/anpv/SpracheTest/ but this variant is not good.
I tried to use the following rules:
private static readonly Parser<AstNode> InOperator =
from expr in Parse.Ref(() => Expression)
from inKeyword in Parse.IgnoreCase("in").Token()
from values in Parse
.Ref(() => Expression)
.DelimitedBy(Comma)
.Contained(OpenParenthesis, CloseParenthesis)
select new InOperator(expr, values);
private static readonly Parser<AstNode> IsNullOperator =
from expr in Parse.Ref(() => Expression)
from isNullKeyword in Parse
.IgnoreCase("is")
.Then(_ => Parse.WhiteSpace.AtLeastOnce())
.Then(_ => Parse.IgnoreCase("null"))
select new IsNullOperator(expr);
private static readonly Parser<AstNode> Equality =
Parse
.ChainOperator(Eq, IsNullOperator.Or(InOperator).Or(Additive), MakeBinary);
which throws ParseException in code like ScriptParser.ParseExpression("1 is null") or ScriptParser.ParseExpression("1 in (1, 2, 3)"): "Parsing failure: Left recursion in the grammar.".
How can I look-ahead for Expression, or do other variants exist to solve this problem?
The answer is, unfortunately, the Sprache cannot parse a left-recursive grammar. I stumbled on comments in the source code talking about how buggy support for left-recursive grammars had been removed when researching this question (which was also how I found your question) - see the source code.
In order to deal with this problem you need to reorganize how you do your parsing. If you are writing a simple expression parser, for example, this is a common problem you have to deal with. Searching the web there is lots of discussion of how to remove left-recursion from a grammar, in particular, for expressions.
In your case, I expect you'll need to do something like:
term := everything simple in an expression (like "1", "2", "3", etc.)
expression := term [ IN ( expression*) | IS NULL | "+" expression | "-" expression | etc.]
or similar - basically - you have to unwind the recursion yourself. By doing that I was able to fix my issues with expressions. I suspect any basic compiler book probably has a section on how to "normalize" a grammar.
It makes building whatever object you are returning from the parser a bit more of a pain, but in the select statement instead of doing "select new Expression(arg1, arg2)" I changed it to be a function call, and the function decides on the specific object being returned depending on what the arguments were.
The standard analyzer does not work. From what I can understand, it changes this to a search for c and net
The WhitespaceAnalyzer would work but it's case sensitive.
The general rule is search should work like Google so hoping it's a configuration thing considering .net, c# have been out there for a while or there's a workaround for this.
Per the suggestions below, I tried the custom WhitespaceAnalyzer but then if the keywords are separated by a comma and no-space are not handled correctly e.g.
java,.net,c#,oracle
will not be returned while searching which would be incorrect.
I came across PatternAnalyzer which is used to split the tokens but can't figure out how to use it in this scenario.
I'm using Lucene.Net 3.0.3 and .NET 4.0
Write your own custom analyzer class similar to SynonymAnalyzer in Lucene.Net – Custom Synonym Analyzer. Your override of TokenStream could solve this by pipelining the stream using WhitespaceTokenizer and LowerCaseFilter.
Remember that your indexer and searcher need to use the same analyzer.
Update: Handling multiple comma-delimited keywords
If you only need to handle unspaced comma-delimited keywords for searching, not indexing then you could convert the search expression expr as below.
expr = expr.Replace(',', ' ');
Then pass expr to the QueryParser. If you want to support other delimiters like ';' you could do it like this:
var terms = expr.Split(new char[] { ',', ';'} );
expr = String.Join(" ", terms);
But you also need to check for a phrase expression like "sybase,c#,.net,oracle" (expression includes the quote " chars) which should not be converted (the user is looking for an exact match):
expr = expr.Trim();
if (!(expr.StartsWith("\"") && expr.EndsWith("\"")))
{
expr = expr.Replace(',', ' ');
}
The expression might include both a phrase and some keywords, like this:
"sybase,c#,.net,oracle" server,c#,.net,sybase
Then you need to parse and translate the search expression to this:
"sybase,c#,.net,oracle" server c# .net sybase
If you also need to handle unspaced comma-delimited keywords for indexing then you need to parse the text for unspaced comma-delimited keywords and store them in a distinct field eg. Keywords (which must be associated with your custom analyzer). Then your search handler needs to convert a search expression like this:
server,c#,.net,sybase
to this:
Keywords:server Keywords:c# Keywords:.net, Keywords:sybase
or more simply:
Keywords:(server, c#, .net, sybase)
Use the WhitespacerAnalyzer and chain it with a LowerCaseFilter.
Use the same chain at search and index time. by converting everything to lower case, you actually make it case insensitive.
According to your problem description, that should work and be simple to implement.
for others who might be looking for an answer as well
the final answer turned out be to create a custom TokenFilter and a custom Analyzer using
that token filter along with Whitespacetokenizer, lowercasefilter etc., all in all about 30 lines of code, i will create a blog post and post the link here when i do, have to create a blog first !
What method would be considered best practice for parsing a LINQ string into a query?
Or in other words, what approach makes the most sense to convert:
string query = #"from element in source
where element.Property = ""param""
select element";
into
IEnumerable<Element> = from element in source
where element.Property = "param"
select element;
assuming that source refers to an IEnumerable<Element> or IQueryable<Element> in the local scope.
Starting with .NET 4.6 you can use CSharpScript to parse Linq. Assuming the expression you want to parse is in string variable "query", this will do it:
string query = "from element in source where element.Property = ""param"" select element";
IEnumerable result = null;
try
{
var scriptOptions = ScriptOptions.Default.WithReferences(typeof(System.Linq.Enumerable).Assembly).WithImports("System.Linq");
result = await CSharpScript.EvaluateAsync<IEnumerable>(
query,
scriptOptions,
globals: global);
} catch (CompilationErrorException ex) {
//
}
Don't forget to pass your (Data)source you want to work on, with the global-variable(s) to have access to them in script parsing.
It requires some text parsing and heavy use of System.Linq.Expressions. I've done some toying with this here and here. The code in the second article is somewhat updated from the first but still rough in spots. I've continued to mess round with this on occasion and have a somewhat cleaner version that I've been meaning to post if you have any interest. I've got it pretty close to supporting a good subset of ANSI SQL 89.
You're going to need a C# language parser (at least v3.5, possibly v4.0, depending on what C# language features you wish to support in LINQ). You'll take those parser results and feed it directly into an Expression tree using a visitor pattern. I'm not sure yet but I'm willing to bet you'll also need some form of type analysis to fully generate the Expression nodes.
I'm looking for the same thing as you, but I don't really need it that badly so I haven't searched hard nor written any code along these lines.
I have written something that takes user string input and compiles it to a dynamic assembly using the Microsoft.CSharp.CSharpCodeProvider compiler provider class. If you just want to take strings of code and execute the result, this should suit you fine.
Here's the description of the console tool I wrote, LinqFilter:
http://bittwiddlers.org/?p=141
Here's the source repository. LinqFilter/Program.cs demonstrates how to use the compiler to compile the LINQ expression:
http://bittwiddlers.org/viewsvn/trunk/public/LinqFilter/?root=WellDunne
This might work for you: C# eval equivalent?
This may or may not help you, but check out LINQ Dynamic Query Library.
While this doesn't specifically give an example to answer your question I would have thought the best practice would generally be to build an expression tree from the string.
In this question I asked how to filter a linq query with a string which shows you building a portion of an expression tree. This concept however can be extended to build an entire expression tree representing your string.
See this article from Microsoft.
There are probably other better posts out there as well. Additionally I think something like RavenDB does this already in its code base for defining indexes.