VB.NET logical expression evaluator - vb.net

I need to test a logical expression held in a string to see if it evaluate to TRUE or FALSE.(the strig is built dynamically)
For example the resulting string may contain "'dog'<'cat' OR (1>4 AND 4<6)". There are no variables in the string, it will logically evaluate. It will only contain simple operators = > < >< >= <= and AND , OR and Open and Close Brackets, string constants and numbers. (converted to correct syntax && || etc.)
I currently acheive this by creating a jscipt function and compiling it into a .dll. I then reference the .dll in my VB.NET project.
class ExpressionEvaluator
{
function Evaluate(Expression : String)
{
return eval(Expression);
}
}
Is there a simpler method using built in .NET functions or Lamdba expressions.

I tried the demo out for this project and you might like it over you current method of evaluating. Note, it doesn't use lamdba expressions or any build it .NET methods.
http://web1.codeproject.com/KB/vb/expression_evaluator.aspx?msg=1151870

try out: http://www.codeproject.com/KB/cs/ExpressionEval.aspx
More guidance :http://www.thefreakparade.com/2008/07/evaluating-expressions-at-runtime-in-net-c/
Good one: http://flee.codeplex.com/
Boolean Example which you are looking for : http://flee.codeplex.com/wikipage?title=BooleanExpression&referringTitle=Examples (ignore the variable adding part as you are not looking for variable)

Related

Can you build an expression inside of a custom code function in Report Builder?

I need to provide my RDL files to teammates so that they can make minor customizations for each client. One of the ways I thought I might improve the efficiency is if I could build more complex expressions inside of custom code functions so that they can input some simple arguments and have the function handle the "heavy lifting" of adjusting the expression accordingly.
This is a very simple example, and not one I would take this step for, but I thought it the easiest place to start figuring out if I can make this work. For instance, in a tablix we want a count returned based on a value where the value is customized per client (and isn't a parameter).
=Count(iif(trim(Fields!Category.Value)="OPTIONA",1,nothing))
Is there a way I could build a function so that my teammates would just need to enter the following?
=Code.CustomFunction("OPTIONA")
My understanding is that the custom code in Report Builder can't query datasets, or at least not in the way that an expression within a tablix would be able to. I've built custom functions that work with the results of an expression added as the argument, but I can't seem to wrap my head around if there's a way to construct an expression within a custom function and pass it back to the expression.
For instance:
Public Function CustomFunction(field As String) As String
Dim customExpression As String = "Count(iif(trim(Fields!Category.Value)=" & field & ",1,nothing))"
Return customExpression
End Function
As expected, this just returns a string with the text of the expression, but not an executed expression. Is what I'm trying to achieve possible with Report Builder?
Or, as an alternative approach, can I somehow place variables at the beginning of an expression that are used later so that anyone else working on the expression just needs to worry about the beginning? Essentially create multiple custom functions and call them later on?
=Code.CustomFunction("OPTIONA",Count(iif(trim(Fields!Category.Value)=Code.CustFunc01(),1,nothing)))
Honestly not sure how I would go about building the functions themselves from here.
You can use a function instead of the related field. The function takes the field string as an argument and the filter string for which will increase the counter. Finally it returns the original field value
Private Dim Counter As Integer
Public Function SetCounter( Expr As String, Filter As String) As String
If Expr = Filter Then Counter = Counter + 1
Return Expr
End Function
Public Function GetCounter( ) As Integer
Return Counter
End Function
For the field value you can use the following expression (yellow color)
=Code.SetCounter( Fields!MyString.Value,"OPTION A")
To get the counter value you can either use the following expression calling a function (orange color)
= Code.GetCounter()
Or make the variable public and use Code.Counter as the expression

VB.Net Alternative to C# Underscore (discard)

In c# i can do:
_ = Bla();
Can I do that in VB.Net ?
I think the answer is no but I just wanted to make sure.
The underscore (_), as used in your example, is C#'s discard token. Unfortunately, there is (currently) nothing similar in VB. There is a discussion about adding a similar feature on the VB language design github page.
In your example, however, you can just omit assigning the result (both in C# and VB), i.e.
Bla(); // C#
Bla() ' VB
The "discard variable" is particularly useful for out parameters. In VB, you can just pass an arbitrary value instead of a variable to discard unused ByRef parameters. Let me give you an example:
The following two lines are invalid in C#:
var b = Int32.TryParse("3", 0); // won't compile
var b = Int32.TryParse("3", out 0); // won't compile
Starting with C# 7, you can use _ for that purpose:
var b = Int32.TryParse("3", out _); // compiles and discards the out parameter
This, however, is perfectly valid in VB, even with Option Strict On:
Dim b = Int32.TryParse("3", 0)
So, yes, it would be nice to make the fact that "I want to ignore the ByRef value" more explicit, but there is a simple workaround in VB.NET. Obviously, once VB.NET gets pattern matching or deconstructors, this workaround won't be enough.

Write String.Join(Of T) in VB.Net

I have a simple code in C#:
Console.WriteLine(string.Join<char>("", ""));
And I can't convert it to VB.Net. Even reflector show me code in VB like:
Console.WriteLine(String.Join(Of Char)("", ""))
But it can't be compiled becouse I have an starge error:
Error 1 Expression expected.
It looks like VB.Net don't have this generic method at all.
Both project use Net Framework 4.
Why this error happened?
UPD:
I've create a custom class and copy Join(Of T) declaration to it:
Class string2
Public Shared Function Join(Of T)(ByVal separator As String, ByVal values As System.Collections.Generic.IEnumerable(Of T)) As String
Return "1"
End Function
End Class
Console.WriteLine(string2.Join(Of Char)("", ""))
It works
UPD2:
My compilation string, where you can see that I'm using Net4:
http://pastebin.com/TYgS3Ys3
Do you have a code element named String somewhere in your project?
Based on the answer you have added to this question (where you indicate that changing String to [String] appears to have solved the problem), I guessed that this may be the result of a naming collision.
I was able to duplicate the error you are seeing -- "Expression expected" -- by adding a module to my project called String and defining a (non-generic) Join method from within that module.
This may not be the specific scenario you find yourself in. But the fact that the code works for you with [String] is, to me, very compelling evidence of a simple namespace collision.
Based on the documentation for the "Expression expected" error, I'm guessing you haven't included the entire section of code where this error is appearing for you.
Do you have a lingering operator such as + or = somewhere?
(The VB.NET code you posted is indeed equivalent to the C# code above it and should compile no problem. This is why I suspect the real issue lies elsewhere.)
String.Join<T>(string, IEnumerable<T>) is useful with LINQ, for standard joins is better to use the String.Join(string, string()) overload.
In C#, "" as Char produces an empty Char (\0). Writing the same thing ("") in VB produces an empty string which is not the same as an empty char. In order to produce an empty character, you'll have to write New Char().
Your VB code therefore becomes:
Console.WriteLine(String.Join(Of Char)(New Char(), New Char()))
Edit
I just checked and it appears String.Join does not support the format you're specifying.
Instead, it goes as follows:
Join(separator As String, value As String()) As String
Your code should be as follows:
Console.WriteLine(String.Join("", New String() {""}))
String.Join(Of Char)(str1, str2) wasn't added til .net 4, it seems. That's why your custom class worked -- it had the declaration, but the String class in the framework you're actually using doesn't.
Check your settings and references to make sure you're targeting .net 4 all around -- cause that's the only thing that seems able at this point to stop the call from working.
Here the solution:
Console.WriteLine([String].Join(Of Char)("", ""))
Why this problem occurs only with generic method? I wish I know...

Lambdas with captured variables

Consider the following line of code:
private void DoThis() {
int i = 5;
var repo = new ReportsRepository<RptCriteriaHint>();
// This does NOT work
var query1 = repo.Find(x => x.CriteriaTypeID == i).ToList<RptCriteriaHint>();
// This DOES work
var query1 = repo.Find(x => x.CriteriaTypeID == 5).ToList<RptCriteriaHint>();
}
So when I hardwire an actual number into the lambda function, it works fine. When I use a captured variable into the expression it comes back with the following error:
No mapping exists from object type
ReportBuilder.Reporter+<>c__DisplayClass0
to a known managed provider native
type.
Why? How can I fix it?
Technically, the correct way to fix this is for the framework that is accepting the expression tree from your lambda to evaluate the i reference; in other words, it's a LINQ framework limitation for some specific framework. What it is currently trying to do is interpret the i as a member access on some type known to it (the provider) from the database. Because of the way lambda variable capture works, the i local variable is actually a field on a hidden class, the one with the funny name, that the provider doesn't recognize.
So, it's a framework problem.
If you really must get by, you could construct the expression manually, like this:
ParameterExpression x = Expression.Parameter(typeof(RptCriteriaHint), "x");
var query = repo.Find(
Expression.Lambda<Func<RptCriteriaHint,bool>>(
Expression.Equal(
Expression.MakeMemberAccess(
x,
typeof(RptCriteriaHint).GetProperty("CriteriaTypeID")),
Expression.Constant(i)),
x)).ToList();
... but that's just masochism.
Your comment on this entry prompts me to explain further.
Lambdas are convertible into one of two types: a delegate with the correct signature, or an Expression<TDelegate> of the correct signature. LINQ to external databases (as opposed to any kind of in-memory query) works using the second kind of conversion.
The compiler converts lambda expressions into expression trees, roughly speaking, by:
The syntax tree is parsed by the compiler - this happens for all code.
The syntax tree is rewritten after taking into account variable capture. Capturing variables is just like in a normal delegate or lambda - so display classes get created, and captured locals get moved into them (this is the same behaviour as variable capture in C# 2.0 anonymous delegates).
The new syntax tree is converted into a series of calls to the Expression class so that, at runtime, an object tree is created that faithfully represents the parsed text.
LINQ to external data sources is supposed to take this expression tree and interpret it for its semantic content, and interpret symbolic expressions inside the tree as either referring to things specific to its context (e.g. columns in the DB), or immediate values to convert. Usually, System.Reflection is used to look for framework-specific attributes to guide this conversion.
However, it looks like SubSonic is not properly treating symbolic references that it cannot find domain-specific correspondences for; rather than evaluating the symbolic references, it's just punting. Thus, it's a SubSonic problem.

How to dynamically invoke a chain of delegates in VB.NET

Does someone knows if it's possible to dynamically create a call chain and invoke it?
Lets say I have two classes A & B:
public class A
public function Func() as B
return new B()
end function
end class
public class B
public function Name() as string
return "a string";
end function
end class
I want to be able to get MethodInfo for both Func() & Name() and invoke them dynamically so that I can get a call similar to A.Func().Name().
I know I can use Delegate.CreateDelegate to create a delegate I can invoke from the two MethodInfo objects but this way I can only call the two functions separately and not as part of a call chain.
I would like two solutions one for .NET 3.5 using expression tree and if possible a solution that is .NET 2.0 compatible as well
Are you using .NET 3.5? If so, it should be relatively straightforward to build an expression tree to represent this. I don't have enough expression-tree-fu to easily write the relevant tree without VS open, but if you confirm that it's an option, I'll get to work in notepad (from my Eee... hence the lack of VS).
EDIT: Okay, as an expression tree, you want something like (C# code, but I'm sure you can translate):
// I assume you've already got fMethodInfo and nameMethodInfo.
Expression fCall = Expression.Call(null, fMethodInfo);
Expression nameCall = Expression.Call(fCall, nameMethodInfo);
Expression<Func<string>> lambda = Expression.Lambda<Func<string>>(nameCall, null);
Func<string> compiled = lambda.Compile();
This is untested, but I think it should work...
You need t add this line before the 1st expression:
Expression ctorCall = Expression.Constructor(A)
And pass that expression as the 1st parameter when creating fCall
Otherwise we're missing a starting point for the chain and we'll get an exception when running the code