Is there an easy way to parse a (lambda expression) string into an Action delegate?

21,073

Solution 1

The Dynamic LINQ library is a fine choice, as it'll generate expressions you can compile to code in a lightweight fashion.

The example you provided actually produces a boolean -- so you should be able to ask for a Func and it might sort it out.

Edit: This of course is wrong, as Expressions don't have assignment in them at all.

So, another potential way is to take two lambdas. One to find the property you want, one to provide a value:

(a => a.AccountId), (a => true)

Then use reflection to set the property referenced in the first lambda with the result of the second one. Hackish, but it's still probably lightweight compared to invoking the C# compiler.

This way you don't have to do much codegen yourself - the expressions you get will contain most everything you need.

Solution 2

You may try this: Dynamic Lambda Expressions Using An Isolated AppDomain

It compiles a lambda expression using CodeDOM compiler. In order to dispose the in-memory assembly that gets created, the compiler runs on an isolated AppDomain. For the passing the expression through the domain boundary, it has to be serialized. Alas, Expression<> is not Serializable. So, a trick has to be used. All the details are explained in the post.

I'm the author of that component, by the way. I would like very much to hear your feedback from it.

Solution 3

There is no general way to parse a string into a lambda expression without a full compilation, because lambda expressions can reference things that are defined outside the lambda expression. I know of no library that handles the specific case you want. There's a long discussion of this on a thread on a C# discussion group.

The easiest way to get what you want is to compile a method at runtime. You can write a function that takes in the string "a.Enabled = true; return a;" and sticks that in the middle of a function that takes an Account as a parameter. I would use this library as a starting point, but you can also use the function mentioned on another thread.

Share:
21,073
Arthur Ulfeldt
Author by

Arthur Ulfeldt

I'm a .Net developer working for a small company in London, UK.

Updated on October 14, 2020

Comments

  • Arthur Ulfeldt
    Arthur Ulfeldt over 3 years

    I have a method that alters an "Account" object based on the action delegate passed into it:

    public static void AlterAccount(string AccountID, Action<Account> AccountAction) {
      Account someAccount = accountRepository.GetAccount(AccountID);
      AccountAction.Invoke(someAccount);
      someAccount.Save();
    }
    

    This works as intended...

    AlterAccount("Account1234", a => a.Enabled = false);
    

    ...but now what I'd like to try and do is have a method like this:

    public static void AlterAccount(string AccountID, string AccountActionText) {
      Account someAccount = accountRepository.GetAccount(AccountID);
      Action<Account> AccountAction = MagicLibrary.ConvertMagically<Action<Account>>(AccountActionText);
      AccountAction.Invoke(someAccount);
      someAccount.Save();
    }
    

    It can then be used like:

    AlterAccount("Account1234", "a => a.Enabled = false");
    

    to disable account "Account1234".

    I've had a look at the linq dynamic query library, which seems to do more or less what I want but for Func type delegates, and my knowledge of Expression trees etc isn't quite good enough to work out how to achieve what I want.

    Is there an easy way to do what I want, or do I need to learn expressions properly and write a load of code?

    (The reason I want to do this is to allow an easy way of bulk updating account objects from a powershell script where the user can specify a lambda expression to perform the changes.)

  • Arthur Ulfeldt
    Arthur Ulfeldt about 15 years
    Thanks - From a quick try that doesn't seem to work when evaluating a string like "a => a.Enabled = true" to an Action<Account> delegate - am I doing something wrong or will it not work as that's not a complete c# statement on its own? I've updated the question to clarify.
  • Arthur Ulfeldt
    Arthur Ulfeldt about 15 years
    I think...If I was doing "a => a.Enabled == false" then it would work, but I'm doing "a => a.Enabled = false" - I want to do that action on the Account, so I don't think it'll work without some modification.
  • MichaelGG
    MichaelGG about 15 years
    rossfabricant; the dynamic linq library he mentions does exactly that. Of course, you have to provide typing information when you go to compile, but it compiles just fine.
  • MichaelGG
    MichaelGG about 15 years
    Bah you're right. It works for funcs: Func<bool> exp = () => test = false; but not for Expressions, as they have no way to represent assignment.
  • RossFabricant
    RossFabricant about 15 years
    It can build queries but I didn't see that it could build anything that modifies state.
  • Arthur Ulfeldt
    Arthur Ulfeldt about 15 years
    This is interesting - particularly the thread on microsoft.public.dotnet.languages.csharp - it does seem that using Expression trees is the way forwards with this tho, I don't quite see how the dynamic compilation route will allow a simple lambda to be entered up front and get the desired results.
  • Arthur Ulfeldt
    Arthur Ulfeldt about 15 years
    Yep - I think I may go down the route of trying to modify the Dynamic LINQ stuff to work with Action<T> as well as Func<T, TResult>...
  • RossFabricant
    RossFabricant about 15 years
    A lambda expression won't work, but using this approach if you know the types and number of all the arguments in advance you can factor out all the boiler plate, and the client code can just pass in a string like "a.Enabled = !a.Enabled".
  • MichaelGG
    MichaelGG about 15 years
    Whisk - we modified dynamic LINQ a lot. (Added let and do bindings, various other features.) It compiles to Expressions, and Expressions don't have assignment.
  • MichaelGG
    MichaelGG about 15 years
    So, basically you will need to significantly expand it. For instance, split on =, and compile each as an expression, then wireup the actual assignment/set property with codegen.
  • Arthur Ulfeldt
    Arthur Ulfeldt about 15 years
    Yep, i started down that road but I think it's going to be harder than is worth it for this - it looks like .NET 4.0 is going to have an Expression.AssignField method which may help a bit...I'll give your 2 lambda idea a try and see where that goes...
  • MichaelGG
    MichaelGG about 15 years
    Yea it could only modify state indirectly. So it could do a => a.SetEnabled(true), right?
  • Marc Gravell
    Marc Gravell almost 15 years
    (replied to your comment on generic operators, btw)