Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parse string to C# lambda Func

Tags:

c#

linq

Is there a way to convert string representation of lambda to a lambda Func?

Func<Product, bool> func = Parse<Product, bool>("product => product.Name.Length > 0");

I tried Dynamic LINQ but it doesn't work as expected - for example it doesn't expect lambda syntax =>.

Summary of answers:

  • writing my own C# compiler - very funny
  • firing up external compiler (like csc.exe) - very slow
  • using DLINQ - as I said I don't see how it can parse lambda expressions

Why do I need this: because there's no way to pass lambdas to custom attributes like

[Secure(role => role.CanDoThis && role.AllowedCount > 5)]

So as a workaround I'd like to pass lambda as string: "role => role.CanDoThis && role.AllowedCount > 5". But seems like I'll have to use DLINQ like this: "CanDoThis && AllowedCount > 5" - since that's the syntax it understands. But my question was about true lambdas, I've already used DLINQ at the time of asking.

like image 450
queen3 Avatar asked Nov 10 '09 13:11

queen3


People also ask

Can I split a string in C?

Splitting a string using strtok() in C In C, the strtok() function is used to split a string into a series of tokens based on a particular delimiter. A token is a substring extracted from the original string.

What is parsing a string in C?

The C function strtok() is a string tokenization function that takes two arguments: an initial string to be parsed and a const -qualified character delimiter. It returns a pointer to the first character of a token or to a null pointer if there is no token.

What is the use of strtok in C?

C library function - strtok() The C library function char *strtok(char *str, const char *delim) breaks string str into a series of tokens using the delimiter delim.

Can I index a string in C?

The index() function locates the first occurrence of c (converted to an unsigned char) in the string pointed to by string. The character c can be the NULL character (\0); the ending NULL is included in the search. The string argument to the function must contain a NULL character (\0) marking the end of the string.


4 Answers

They are many lambda expression parsers available. Some of them are Lambda-Parser, Sprache

Sample Code:

Example1 : string concat and number calculate:

string code = "2.ToString()+(4*2)"; // C# code Func<string>  func = ExpressionParser.Compile<Func<string>>(code); // compile code  string result = func(); // result = "28" 
like image 82
Prasad Kanaparthi Avatar answered Oct 20 '22 01:10

Prasad Kanaparthi


You could parse the string and build up a lambda expression using the Expression class, essentially duplicating the function of the compiler.

like image 41
tvanfosson Avatar answered Oct 20 '22 00:10

tvanfosson


I guess you have to resort to the CSharpCodeProvider. However, dealing with all possible local variable references might not be trivial. And how would you tell the CSharpCodeProvider about the type of the lambda parameter? I would probably create a template class looking like this:

class ExpressionContainer {
    public Expression<Func<Product, bool>> TheExpression;
    public string Length;

    public ExpressionContainer() {
        TheExpression = <user expression text>;
    }
}

Then do something like this:

string source = <Code from above>;
Assembly a;
using (CSharpCodeProvider provider = new CSharpCodeProvider(...) {
    List<string> assemblies = new List<string>();
    foreach (Assembly x in AppDomain.CurrentDomain.GetAssemblies()) {
        try {
            assemblies.Add(x.Location);
        }
        catch (NotSupportedException) {
            // Dynamic assemblies will throw, and in .net 3.5 there seems to be no way of finding out whether the assembly is dynamic before trying.
        }
    }

    CompilerResults r = provider.CompileAssemblyFromSource(new CompilerParameters(assemblies.ToArray()) { GenerateExecutable = false, GenerateInMemory = true }, source);
    if (r.Errors.HasErrors)
        throw new Exception("Errors compiling expression: " + string.Join(Environment.NewLine, r.Errors.OfType<CompilerError>().Select(e => e.ErrorText).ToArray()));
    a = r.CompiledAssembly;
}
object o = a.CreateInstance("ExpressionContainer");
var result = ( Expression<Func<Product, bool>>)o.GetType().GetProperty("TheExpression").GetValue(o);

Note, however, that for long-running applications, you should create all these in-memory assemblies in a separate appdomain since they can't be freed until the appdomain they reside in is unloaded.

like image 33
erikkallen Avatar answered Oct 20 '22 00:10

erikkallen


You might be able to do something with CSharpCodeProvider (wrap the expression with some more code to create a valid class and compile it into an assembly, then load the assembly).

I believe that is how LINQPad does it.

like image 31
Rasmus Faber Avatar answered Oct 20 '22 00:10

Rasmus Faber