Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / C#

Using Roslyn ScriptEngine for a ValueConverter to process user input

Rate me:
Please Sign up or sign in to vote.
4.97/5 (17 votes)
29 Feb 2012CPOL9 min read 49.2K   715   20   10
This article is about using Rosyln to evaluate user input in a value converter.

Introduction

I have created a number of value converters that use an equation evaluator. One converter allows the user to enter an equation which is then converted by the value converter to a value for the binding (Value Converter to Evaluate User Equation Input), and another that applies an equation defined by the ConverterParameter to the bound value (WPF Evaluated Value Binding Using Converter Parameter). For both these projects, I used the eval() method in the JavaScript engine to evaluate the equations. The article included a number of options for engines that could be used for evaluating values. Some of the comments on the article included other options that I missed including using the IronPython engine (which was not as flexible as the JavaScript version), and the Microsoft Rosyln engine.

Roslyn is Microsoft’s project to open up the VB and C# compilers to support compiler as a service.

This article is about using Rosyln to evaluate user input in a value converter (this is the simplest way to use Roslyn). There are many features that make Rosyln very good for this application. First, unlike JavaScript, it is based on the framework, so everything is compatible with the WPF/Silverlight environment. Second, it is possible to support custom functions in a user friendly way. Using the JavaScript eval() method, it is possible to input transcendental functions, but they need to be prefixed by “Math.” to access functions and function names have the right case. With Rosyln, it is possible to create functions that can be accessed with just the name. Unfortunately the names are also case sensitive for C#.

Using Roslyn

Information about Roslyn and downloads can be found at http://msdn.com/roslyn. It includes links to papers and presentations, including a nice presentation by Anders Hejlsberg about the future of C# and Visual Basic. About half the presentation is on Roslyn. According to the talk, Roslyn will not be in the 2012 release of Visual Studio due to time constraints.

There are also a couple of good articles about Roslyn on CodeProject: C# as a Scripting Language in Your .NET Applications Using Roslyn and Roslyn CTP: Three Introductory Projects.

For the sample project, Roslyn will have to be downloaded and installed. Then two references will have to be added: Roslyn.Compliers.CSharp and Roslyn.Compliers. These references are added in the “Add References” dialog (right click the references folder and click on the Add Reference… menu item). Both can be found in the Extensions list.

Image 1

Once these references have been added, the references folder for the project will look as follows:

Image 2

Implementation

The implementation is almost identical to the value converter in the article Value Converter to Evaluate User Equation Input except that now the Roslyn engine replaces the JavaScript engine:

C#
class RoslynEvaluationValueConverter : IValueConverter
{
    private ScriptEngine rosylnEngine;
    private Session session;
    public object Convert(object value, Type targetType, 
      object parameter, System.Globalization.CultureInfo culture)
    {
      return value;
    }
 
    public object ConvertBack(object value, Type targetType, 
      object parameter, System.Globalization.CultureInfo culture)
    {
      if (rosylnEngine == null) 
        Initialize();
      if (value == null)
        return null;
      try
      {
        var x = rosylnEngine.Execute(value.ToString(), session);
          return x;
      }
      catch (Exception e)
      {
        return null;
      }
    }
 
    private void Initialize()
    {
      session = Session.Create();
      rosylnEngine = new ScriptEngine();
      rosylnEngine.Execute("using System;", session);
      rosylnEngine.Execute("double sin(double x) {return Math.Sin(x);} " + 
        "double tan(double x) {return Math.Tan(x);}", session);
      //rosylnEngine.Execute("double cos(double x) {return Math.Cos(x);}", session);
      rosylnEngine.Execute("double pi = 3.1415926535;", session);
    }
}

As with the JavaScript implementation, the Convert method basically just returns the value since it is only the user input value that needs to be processed. In the ConvertTo method, the engine and session are both initialized if they have not been, and then, if the value is not null, the user input is executed by the Roslyn engine. I have two cases for when an error is thrown, which means that the user input is not valid for the engine to execute:

  1. If the return value is expected to be a string, I return the value since there is a possibility that the value is exactly what the user expects.
  2. Otherwise, a null is returned to the bound variable. Returning a null is actually good for many uses because it is an invalid value for number types. WPF is smart enough to know that when a bound value is invalid for the bound Text property, there is an error condition, and a red border is shown on a TextBox.

The Initialize method does a lot more than initialize the engine and session. First the code executes a using statement to make the System namespace available. This namespace initialization could also have been done when the script engine was instantiated:

C#
rosylnEngine = new ScriptEngine(new Assembly[] {}, new string[] { "System" });

This code also creates two functions that can be used by the user, and a constant. Defining a “sin” function gives the user access to this transcendental function without needing any namespace or class declarations:

Image 3

Note: It is the definition of the two transcendental functions that actually requires the System namespace, basic math works great without adding this namespace.

After a tab:

Image 4

This can also be done by specifying the class and the case sensitive name of the function:

Image 5

After a tab key is pressed:

Image 6

Since “pi” is defined, we can actually use degrees:

Image 7

Note: we have to include the decimal point to force a floating number, unfortunately. Of course, if pi was first in the argument for the “sin” function, there would be no problem. (I think that the compiler should recognize that the sin function takes a double value, and should calculate accordingly.)

After a tab key is pressed:

Image 8

An interesting effect of the implementation is that functions can be added to the engine:

Image 9

This causes an error because there is no result returned, but now there is another transcendental function available:

Image 10

Note: the TextBox still has a red border from when the cosine function was defined in the previous step.

After a tab key is pressed:

Image 11

Now we also have access to all of the libraries;

Image 12

After a tab key is pressed:

Image 13

Customization

Right now there are many reasons that this implementation may not be what you want to implement:

  1. It is possible to add methods and variables to the session, which may not be acceptable. However, this may also be considered a great feature.
  2. It is possible for the user to access many of the framework’s methods and properties. Again, this could be considered a great feature.
  3. It is possible to add references (e.g., “using System.Linq;”). Ditto.
  4. There may be functions that can be included that will help users do their job (such as all the transcendental functions). This is something I consider a great attribute of using Roslyn.
  5. May want to force the user input to all uppercase or lowercase letters to prevent the user from doing a lot of things (can’t do many things because most methods and classes are mixed case), and makes it so that the defined functions are case insensitive. For instance, I have defined the function ‘sin’ but this is only available if the user inputs the function in all lower case. If the input is not forced to upper or lower case, then the ‘sin’ function would have to be defined for all case combinations.
  6. ConverterParameter can be used to provide additional flexibility.

Roslyn Weaknesses

Compared to other options, Roslyn is probably the best for this feature. However, the ScriptEngine and Session objects do not currently appear to expose much information that would be helpful for something like this in the way of methods and properties; I was very frustrated with the difficulty of getting information from these two objects.

For something that is being executed, like what is happening in this feature, having visibility could be used to limit user input so methods and references could not be input, and visibility into the methods being called could mean that the case could be easily fixed.

  1. It would be really nice to be able to some way preprocess the user input to understand what the user input appears to do. For instance, if it is defining a method: argument types, name, and return type information. Then it would be nice for this input object to be used as an argument for the ScriptEngine Execute method instead of passing a string, reducing the overhead of parsing the functions twice.
  2. I am somewhat perplexed with how to define a more complex method that can execute against the session object just like a simple equation (e.g., “4+5”). I would like the ability to define a lambda expression, and then execute the lambda. This would include complex equations defined with a return statement within brackets (e.g., “(x,y) => {if (x > 0) return x; else return y;}”). What I am thinking is being able to create a lambda object from a string, and then being able to execute this lambda object and its arguments with the ScriptEngine instance with the Session instance as another argument.
  3. The Session object, which is where I suspect the current class information is stored, should have visibility into all the items of the classes (methods, properties, fields, etc.), including the defined classes (if the user executes a string that defines a class).
  4. As another improvement, it should be possible to extend a class that has already been defined, and I am not sure that is possible.

Roslyn obviously is far from a finished product since it is not even being considered for the next release of Visual Studio, so I am hoping that the ScriptEngine and Session objects will be improved.

Conclusion

The project shows how Roslyn can be used to easily provide the user of a WPF application with a way to enter values as equations using an IValueConverter Interface that is bound to numeric and string values.

  • It can be customized with added functions that may provide the required flexibility for the application.
  • The input is based on C# so that developers are very familiar with the capabilities (unfortunately this does not necessarily help the users).

The sample also provides a way to play with Roslyn where the interaction with the engine can be investigated with the debugging tools provided by Visual Studio (so enjoy using this to learn a bit about one aspect of Roslyn). This is something that cannot be done with the “C# Interactive Window” available within Visual Studio after Roslyn is installed.

The obvious disadvantage is that it is a beta product, and will be one for some time to come, but I am sure that Microsoft will eventually include the product in a release, in the meantime it will require a special installation.

The other disadvantage is a concern about injection since the ScriptEngine Execute method can execute any string.

I welcome input from other members that will help me better understand and use Roslyn for this application. Right now there are only a few articles on Roslyn on CodeProject, and it will be good to see more on this powerful feature.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Clifford Nelson Consulting
United States United States
Has been working as a C# developer on contract for the last several years, including 3 years at Microsoft. Previously worked with Visual Basic and Microsoft Access VBA, and have developed code for Word, Excel and Outlook. Started working with WPF in 2007 when part of the Microsoft WPF team. For the last eight years has been working primarily as a senior WPF/C# and Silverlight/C# developer. Currently working as WPF developer with BioNano Genomics in San Diego, CA redesigning their UI for their camera system. he can be reached at qck1@hotmail.com.

Comments and Discussions

 
GeneralMy vote of 5 Pin
D V L16-Jun-16 6:15
professionalD V L16-Jun-16 6:15 
AnswerRe: My vote of 5 Pin
Clifford Nelson16-Jun-16 6:42
Clifford Nelson16-Jun-16 6:42 
GeneralBeauty! Pin
Super Lloyd28-Nov-13 17:54
Super Lloyd28-Nov-13 17:54 
AnswerRe: Beauty! Pin
Clifford Nelson29-Nov-13 7:33
Clifford Nelson29-Nov-13 7:33 
GeneralMy vote of 2 Pin
Abolfazl Khusniddinov4-Mar-12 21:27
Abolfazl Khusniddinov4-Mar-12 21:27 
AnswerRe: My vote of 2 Pin
Clifford Nelson5-Mar-12 5:44
Clifford Nelson5-Mar-12 5:44 
GeneralRe: My vote of 2 Pin
bobfox16-Sep-15 11:42
professionalbobfox16-Sep-15 11:42 
QuestionAh so you found the time to look at Roslyn, looks cool Pin
Sacha Barber29-Feb-12 6:19
Sacha Barber29-Feb-12 6:19 
AnswerRe: Ah so you found the time to look at Roslyn, looks cool Pin
Clifford Nelson29-Feb-12 8:13
Clifford Nelson29-Feb-12 8:13 
GeneralRe: Ah so you found the time to look at Roslyn, looks cool Pin
Clifford Nelson1-Mar-12 13:34
Clifford Nelson1-Mar-12 13:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.