tags:

views:

404

answers:

4

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.

A: 

Have a look at Expression Trees.

vbocan
Can't see how that site could actually help.
Arnis L.
That just raises another question: Is there a way to convert a string representation of an expression to an expression tree?
LukeH
Arnis L., do you downvote just to practice clicking the mouse or you happen to read the reply? Get a good book on LINQ please...
vbocan
How do I use it to parse strings?
queen3
@vbocan I downvoted because I'm completely sure that queen3 (i've noticed his answers before too) knows basics and provided link `DOES NOT` provide any information about how to parse string to lambda expression. Nothing personal. Just my 2 cents that might be completely wrong.
Arnis L.
+4  A: 

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

tvanfosson
I guess it could be fun to spend time on it but not for my customers - they don't pay me to write C# compiler.
queen3
What a strange customers. :)
Arnis L.
+1  A: 

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.

Rasmus Faber
I know how to invoke csc.exe, and have experience with doing on-the-fly compilation using CodeDom, that's too much overhead - in my experience it actually runs csc.exe (when I used it on .NET 1.1).
queen3
+1  A: 

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.

erikkallen
Yes and then I'll have csc.exe running and AppDomains only for couple of lambdas... This reminds me of my Turbo Pascal program that allowed user to enter expressions... and had to be deployed along with Turbo Pascal compiler ;-)
queen3
On the other hand - there was something called 'dynamic methods'. A more lightweight way to handle situations like these. Unluckily - i've never used them. :/
Arnis L.
@queen: Does it matter? Your customers will already have csc deployed, as it is part of the framework. Also, the whole purpose of running in separate appdomains is that you can retrieve the value and then tear down the appdomain. Your customer can buy quite a few new servers to handle the computation for the money you save by not implementing a compiler yourself.
erikkallen