SharePoint Newbie
SharePoint Newbie

Reputation: 6082

Is there any off the shelf component which can be used to evaluate expressions on an object?

We would like to parse expressions of the type:

Func<T1, bool>, Func<T1, T2, bool>, Func<T1, T2, T3, bool>, etc.

I understand that it is relatively easy to build an expression tree and evaluate it, but I would like to get around the overhead of doing a Compile on the expression tree.

Is there any off the shelf component which can do this?

Is there any component which can parse C# expressions from a string and evaluate them? (Expression services for C# , I think there is something like this available for VB which is used by WF4)

Edit: We have specific models which on which we need to evaluate expressions which are entered by IT Administrators.

public class SiteModel
{
    public int NumberOfUsers {get;set;}
    public int AvailableLicenses {get;set;}
}

We would like for them to enter an expression like:

Site.NumberOfUsers > 100 && Site.AvailableLicenses < Site.NumberOfUsers

We would then like to generate a Func which can be evaluated by passing a SiteModel object.

Func<SiteModel, bool> (Site) => Site.NumberOfUsers > 100 && Site.AvailableLicenses < Site.NumberOfUsers

Also, the performance should not be miserable (but around 80-100 calls per second on a normal PC should be fine).

Upvotes: 3

Views: 529

Answers (8)

SharePoint Newbie
SharePoint Newbie

Reputation: 6082

Thanks for your answers.

  • Introducing a dependency on Mono in a product like ours (which has more than 100K installations and has a long release cycle of 1-1.5 years) may not be a good option for us. This might also be an overkill since we only need to support simple expressions (with little or no nested expressions) and not an entire language.
  • After using the code dom compiler, we noticed that it causes the application to leak memory. Although we could load it in a separate app domain to work around this, this again might be an overkill.
  • The dynamic LINQ expression tree sample provided as part of the VS Samples has a lot of bugs and no support for type conversions when ding comparisons (changing a string to an int, a double to an int, a long to an int, etc). The parsing for indexers also seems to be broken. Although not usable off the shelf, it shows promise for our use cases.

We have decided to go with expression trees as of now.

Upvotes: 0

user1222021
user1222021

Reputation:

Perhaps this technique is useful to you - especially regarding the dependency reviews as you are depending solely on framework components.

EDIT: as pinpointed by @Asti, this technique creates dynamic assemblies that unfortunately, due to limitations of .net Framework design, cannot be unloaded, so careful consideration should be done before using it. This means that if a script is updated, the old assembly containing the previous version of the script can't be unloaded from memory and will be lingering until the application or service hosting it is restarted.

In a scenario where the frequency of change in scripts is reduced, and where compiled scripts are cached and reused and not recompiled on every use, this memory leak can be IMO safely tolerated (this has been the case for all our uses of this technique). Fortunately, in my experience, the memory footprint of the generated assemblies for typical scripts tends to be quite small.

If this is not acceptable, then the scripts can be compiled on a separate AppDomain that can be removed from memory, although, this would require call marshaling between domains (e.g. a named pipe WCF service), or perhaps an IIS hosted service, where unloading occurs automatically after an inactivity period, or a memory footprint threshold is exceeded).

End EDIT

First, you need to add to your project a reference to Microsoft.CSharp, and add the following using statements

using System.CodeDom.Compiler;   // this is included in System.Dll assembly
using Microsoft.CSharp;

Then, I'm adding the following method:

 private void TestDynCompile() {
         // the code you want to dynamically compile, as a string

         string code = @"
            using System;

            namespace DynCode {
               public class TestClass {
                  public string MyMsg(string name) {
                     //---- this would be code your users provide
                     return string.Format(""Hello {0}!"", name);
                     //-----
                  }
               }
            }";

         // obtain a reference to a CSharp compiler
         var provider = CodeDomProvider.CreateProvider("CSharp");

         // Crate instance for compilation parameters
         var cp = new CompilerParameters();

         // Add assembly dependencies
         cp.ReferencedAssemblies.Add("System.dll");

         // hold compiled assembly in memory, don't produce an output file
         cp.GenerateInMemory = true;
         cp.GenerateExecutable = false;

         // don't produce debugging information    
         cp.IncludeDebugInformation = false;

         // Compile source code
         var rslts = provider.CompileAssemblyFromSource(cp, code);

         if( rslts.Errors.Count == 0 ) {
            // No errors in compilation, obtain type for DynCode.TestClass
            var type = rslts.CompiledAssembly.GetType("DynCode.TestClass");
            // Create an instance for the dynamically compiled class
            dynamic instance = Activator.CreateInstance(type);
            // Invoke dynamic code
            MessageBox.Show(instance.MyMsg("Gerardo"));  // Hello Gerardo! is diplayed  =)
         }
       }

As you can see, you need to add boilerplate code like a wrapper class definition, inject assembly dependencies, etc.), but this is a really powerful technique that adds scripting capabilities with full C# syntax and executes almost as fast as static code. (Invocation will be a little bit slower).

Assembly dependencies can refer to your own project dependencies, so classes and types defined in your project can be refered and used inside the dynamic code.

Hope this helps!

Upvotes: 1

Daniel
Daniel

Reputation: 16464

The "component" you are talking about:

  • Needs to understand C# syntax (for parsing your input string)
  • Needs to understand C# semantics (where to perform implicit int->double conversions, etc.)
  • Needs to generate IL code

Such a "component" is called a C# compiler.

  1. The current Microsoft C# compiler is poor option as it runs in a separate process (thus increasing compilation time as all the metadata needs to be loaded into that process) and can only compile full assemblies (and .NET assemblies cannot be unloaded without unloading the whole AppDomain, thus leaking memory). However, if you can live with those restrictions, it's an easy solution - see sgorozco's answer.

  2. The future Microsoft C# compiler (Roslyn project) will be able to do what you want, but that is still some time in the future - my guess is that it will be released with the next VS after VS11, i.e. with C# 6.0.

  3. Mono C# compiler (see Mark H's answer) can do what you want, but I don't know if that supports code unloading or will also leak a bit of memory.

  4. Roll your own. You know which subset of C# you need to support, and there are separate components available for the various "needs" above. For example, NRefactory 5 can parse C# code and analyze semantics. Expression Trees greatly simplify IL code generation. You could write a converter from NRefactory ResolveResults to Expression Trees, that would likely solve your problem in less than 300 lines of code. However, NRefactory reuses large parts of the Mono C# compiler in its parser - and if you're taking that big dependency, you might as well go with option 3.

Upvotes: 1

Tarion
Tarion

Reputation: 17154

Maybe you can use LUA Scripts as input. The user enters a LUA expression and you can parse and execute it with the LUA engine. If needed you can wrap the input with some other LUA code before you interpret it and I'm not sure about the performance. But 100 calls/s are not that much.

Evaluating expressions is always a security issue. So take care of that, too. You can use LUA in c#


Another way would be to compile some C# code that contains the input expression in a class. But here you will end up with one assembly per request. I think .net 4.0 can unload assemblies but older versions of .net can't. so this solution might not scale well. A workaround can be an own process that is restarted every X requests.

Upvotes: 0

Rohit Sharma
Rohit Sharma

Reputation: 6500

Generate xsd out of SiteModel class, then through web/whatever-UI let the administrator input the expression, transform the input via xsl where you modify the expression as a functor literal, then generate and execute it via CodeDom on the fly.

Upvotes: 0

Yahia
Yahia

Reputation: 70379

Not sure about the performance part but this seems like a good match for dynamic linq...

Upvotes: 0

Philipp Schmid
Philipp Schmid

Reputation: 5828

Maybe ILCalc (on codeplex) does what you are looking for. It comes as a .NET and a Silverlight version and is open sourced.

We have been using it successfully for quite a while. It even allows you to reference variables in your expression.

Upvotes: 1

Mark H
Mark H

Reputation: 13907

Mono.CSharp can evaluate expressions from strings, and is very simple to use. The required references come with the mono compiler and runtime. (In the tools directory iirc).

You need to reference Mono.CSharp.dll and the Mono C# compiler executable (mcs.exe).

Next set up the evaluator to know about your code if necessary.

using Mono.CSharp;
...
Evaluator.ReferenceAssembly (Assembly.GetExecutingAssembly ());
Evaluator.Run ("using Foo.Bar;");

Then evaluating expressions is as simple as calling Evaluate.

var x = (bool) Evaluator.Evaluate ("0 == 1");

Upvotes: 1

Related Questions