Thursday, December 15, 2011

Dynamically creating lambda expressions in .Net (C#)


Dynamically creating lambda expressions in .Net (C#)

Passing lambda expressions as strings
In a situation where you want to allow a user to create his own lambda expressions at runtime, there are not a lot of options. Whether you read the expression from a file, or an input box, you end up with a string that you wish to convert. There is no method in .Net 4.0 that lets you do just that (easily).
But as for most things, workarounds exist. Below is the one I’m using. Better solutions are very welcome.

Suppose you want to call Regex.Replace with a lambda expression for the MatchEveluator. At design time this is easy:
                Regex.Replace(“MyInputString”, @”\B[A-Z]”, m => “ “ + m.ToString().ToLower());
This results in: "My input string"

But when the lambda expression is a string, that won’t work:
String input = “MyInputString”;
String expr = @”\B[A-Z]”;
String lambda = “m => \” \” + m.ToString().ToLower();
Regex.Replace(input, expr, lambda);
This results in:  "Mym => \" \" + m.ToString().ToLower()nputm => \" \" + m.ToString().ToLower()tring"


Logical, but not quite what’s intended.

In order to convert the string to a true lambda expression would take considerable effort. However, creating a regular ‘method’ at runtime is a lot easier.

The question then becomes: how can we leverage the normal compiler routines in compiling lambda ‘strings’. Here’s an example. It requires 3 steps:

Step 1: create a method that builds of the body of a class and method

private string createRegexClassInMemory(
    string input,

    string expr,
    string lambda,
    RegexOptions regexOptions = RegexOptions.None) {
        StringBuilder classbuilder = new StringBuilder("using System;”);
        classbuilder.Append(“using System.Text;");
        classbuilder.Append(“using System.Text.RegularExpressions;");

        // Note that we need to double escape 'escapes' and double accolade 'accolades'
        classbuilder.AppendFormat("namespace {0}{{", GetType().Namespace);
        classbuilder.Append(@"
            public class RegexLambdaClass{
            public static string RegexLambda(){");

        // Note that we need to double escape 'escapes', except for the lambda expression, as it gets handled differently
        input = input.Replace("\\", "\\\\");
        expr = expr.Replace("\\", "\\\\");

        // Note the lack of double-quotes around the third parameter !!!!!!
        classbuilder.AppendFormat("return Regex.Replace(\"{0}\", \"{1}\", {2}, RegexOptions.{3});",
            input,
            expr,
            lambda,
            regexOptions);
        classbuilder.Append("}}}");
        return classbuilder.ToString();
}

Step 2: create a module to compile and run this code

The code below has been adapted from “http://www.codeproject.com/KB/cs/runtimecompiling.aspx”.

using System;
using System.Text;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;
using System.Text.RegularExpressions;

namespace Invocation {

    /// Example
    /// /x myNamespace MyClass MyFunction False param1,param2,param3,etc
    /// The False states that MyFunction is not static.
    
    /// <summary>
    /// This class can be used to execute dynamic uncompiled code at runtime
    /// This class is not exception safe, all function calls should be exception handled.
    /// </summary>
    public class CodeCompiler {

        private CompilerParameters compilerparams = new CompilerParameters();
        /// <summary>
        /// Default Constructor.
        /// It is necessary to have an instance of the class so that the reflection
        /// can get the namespace of this class dynamically.
        /// </summary>
        /// <remarks>
        /// This class could be static, but I wanted to make it easy for developers
        /// to use this class by allowing them to change the namespace without
        /// harming the code.
        /// </remarks>
        public CodeCompiler() {
            compilerparams.GenerateExecutable = false;
            compilerparams.GenerateInMemory = true;
            // Add System reference
            this.addReference(typeof(System.String).Assembly.Location);
        }

        public void clearReferences() {
            compilerparams.ReferencedAssemblies.Clear();
        }

        public void addReference(string reference) {

            compilerparams.ReferencedAssemblies.Add(reference);
        }

        /// <summary>
        /// This is the main code execution function.
        /// It allows for any compilable c# code to be executed.
        /// </summary>
        /// <param name="code">the code to be compiled then executed</param>
        /// <param name="namespacename">the name of the namespace to be executed</param>
        /// <param name="classname">the name of the class of the function in the code that you will execute</param>
        /// <param name="functionname">the name of the function that you will execute</param>
        /// <param name="isstatic">True if the function you will execute is static, otherwise false</param>
        /// <param name="args">any parameters that must be passed to the function</param>
        /// <returns>what ever the executed function returns, in object form</returns>
        public object ExecuteCode(string code, string namespacename, string classname,
            string functionname, bool isstatic, params object[] args) {
            object returnval = null;
            Assembly asm = BuildAssembly(code);
            object instance = null;
            Type type = null;
            if (isstatic) {
                type = asm.GetType(namespacename + "." + classname);
            }
            else {
                instance = asm.CreateInstance(namespacename + "." + classname);
                type = instance.GetType();
            }
            MethodInfo method = type.GetMethod(functionname);
            returnval = method.Invoke(instance, args);
            return returnval;
        }

        /// <summary>
        /// This private function builds the assembly file into memory after compiling the code
        /// </summary>
        /// <param name="code">C# code to be compiled</param>
        /// <returns>the compiled assembly object</returns>
        private Assembly BuildAssembly(string code) {
            Microsoft.CSharp.CSharpCodeProvider provider = new CSharpCodeProvider();

            CompilerResults results = provider.CompileAssemblyFromSource(compilerparams, code);

            if (results.Errors.HasErrors)
            {
                StringBuilder errors = new StringBuilder("Compiler Errors :\r\n");
                foreach (CompilerError error in results.Errors)
                {
                    errors.AppendFormat("Line {0},{1}\t: {2}\n", error.Line, error.Column, error.ErrorText);
                }
                throw new Exception(errors.ToString());
            }
            else
            {
                return results.CompiledAssembly;
            }
        }
    }
}


Step 3: call the code above

Invocation.CodeCompiler cc = new Invocation.CodeCompiler();
// We need to add the necessary references !!!!!
cc.addReference(typeof(System.Text.RegularExpressions.Regex).Assembly.Location);
string result = (string)cc.ExecuteCode(
    createRegexClassInMemory(
        input,
        expr,
        lambda,
        regexOptions: RegexOptions.IgnoreCase),
    GetType().Namespace,
    "RegexLambdaClass",
    "RegexLambda", true);



Note that if you don’t need lambda expressions, you could add the parameters as proper ‘method’ parameters. The build-up and execution code could then be as follows:

private string createRegexClassInMemory() {
    StringBuilder classbuilder = new StringBuilder("using System;”);
    classbuilder.Append(“using System.Text;");
    classbuilder.Append(“using System.Text.RegularExpressions;");

    // Note that we need to double escape 'escapes' and double accolade 'accolades'
    classbuilder.AppendFormat("namespace {0}{{", GetType().Namespace);
    classbuilder.Append(@"
        public class RegexLambdaClass{
        public static string RegexNoLambda(string input, string expr, MatchEvaluator me, RegexOptions regexOptions = RegexOptions.None){");
    classbuilder.Append("return Regex.Replace(input, expr, me, regexOptions);"); classbuilder.Append("}}}");
    return classbuilder.ToString();
}

Invocation.CodeCompiler cc = new Invocation.CodeCompiler();
// We need to add the necessary references !!!!!
cc.addReference(typeof(System.Text.RegularExpressions.Regex).Assembly.Location);
string result = (string)cc.ExecuteCode(
    createRegexClassInMemory(),
    GetType().Namespace,
    "RegexLambdaClass",
    "RegexLambda", true, 
    input, expr, new MatchEvaluator(delegate), RegexOptions.IgnoreCase);

Wednesday, December 7, 2011

Metro

you can check out any time you like, but you can never leave


Taking the metro

Embarking on new travels; mostly by car, sometimes by bus or train, this time by metro.
Living in the Netherlands I've given up on the train- and bus-thing, as it is simply to cumbersome, error prone and riddled with cost-incurring 'facilities'.
You can read all about it on the internet.
So the launch of a new metro was right up my alley, and I was privileged to be allowed a test-run. Which I did.
Before continuing, I want you to know that from buses and trains you can easily leave (note that you pay extra for not checking out; very much so in the case of trains).
By the way, leaving while it's still running falls in the category "don't try this at home dude"; which you couldn't anyway by virtue of not being at home, seeing as you're in the bus or train.
Well, it's all different in the metro. Oh yes. Checking out or not checking out makes hardly any difference (except for clotting up part of the brain). BUT YOU CAN NEVER LEAVE.


Here's my story

So I got on this brand new metro. It's not quite finished, and thus I need to stay flexible and open-minded. I thought I was.....
Drove around, tried some of the new features, got stuck in the features (eventually throwing them on a big heap and leaving them to rot), got expelled back to the good ol' black-top, been dragged along, managed to hop on the metro again, but couldn't find the exit. Oh bummer.
Heck, even in 'expelled' state there was no way to truly leave....
Who was it wrote: you can check out any time you like, but you can never leave.
But hey, that's okay, I didn't really wanna leave yet, fully trusting in finding the exit later (as I am writing this from home, I clearly did, so don't worry your pants off - it would leave me with a mental image I am not too keen on).
Played the piano (cool eh, a piano in the metro), threw it on the heap...
Played sudoku, threw it on the heap...
Tried the ink-pad...
Hey, hold on a minute.... sudoku I can tear up and throw in the bin.... ah, got that sorted neatly (notice that in this brave new world I can conjure up a new sudoku in no time flat)
Hmmm.... a piano lid can be slammed shut.
Chess boards can be smashed to smithereens....
Seems like every feature can be destructed if one truly wishes to do so....
Ahh.... thus one worry less.
Would this work for the whole metro? Nope. It will not allow itself to be destroyed so easily. Leaving metro is a whole different story. I eventually manged by crawling in a far corner, prying open a panel, clicking a button, and finally leaving...
It was a trip I'm not likely going to forget any time soon... Will I be sufficiently disturbed to try it again some other time? No doubt.....


Microsoft Windows 8 with the new Metro UI

If you like to try Windows 8 with the new Metro UI yourself, then please do so in a Virtual Machine. The latest VirtualBox (by Oracle) has knowledge of Windows 8 making the installation a breeze.
Some Metro-apps throw you right back to the old desktop when they're started, which is rather confusing.
Most (if not all) 'metro-apps' can be closed by using Alt-F4 (yes, the very old Windows Alt-F4), but you'll probably loose any unsaved data. (Alt-F4 ends up in a blank screen - green in my case - on which you can click to get back to Metro).
Shutting down Windows is possible, but it requires some searching (left-bottom corner hover, followed by settings, followed by power, followed by shutdown).

Still, Microsoft is working on the environment as we speak, hopefully ironing out most of the issues.
I'll give it another test-drive in 2012.

Be brave

Thursday, September 8, 2011

Ferrari: a Point to Share


What's the point of having a great system if you're not allowed to use it. Here's my take on it.

What ?!??!

A Ferrari going 10 miles an hour on 3 wheels, while listening to your favorite music on the latest iPod-Nano, plugged into universal media connector. What ?!?!!???

My birthday

I was going to be 18, and was ecstatic to learn that my mom and dad had bought me a Ferrari. No, not a matchbox type (that would really ruin this story), but a real one (yellow if you care to know). Didn’t believe it at first, thought I was dreaming. But I wasn’t….  So I called all my friends to take her for a spin….

feRRaRi

The motor puRRed……Nice!!!!!! A gentle start…. 2 Mph, 5 Mph, 7 Mph….. really getting the hang of it now…. 10 Mph… 10 Mph… 10 Mph…. terrible scraping noise…. and sitting crooked to boot.
Ok, so there’s clearly something wrong here. I already thought something fishy was going on when I spotted only 3 wheels; but not being able to go faster than 10Mph in a Ferrari?!??!? Surely the machine must be broken. Techno-savvy as I am I assumed a reboot was needed, so I stopped the car, got out (that’s important, as otherwise the sensors might think you’re still driving, and thus failing on the reboot) , got back in (equally important, as otherwise driving gets really difficult, esp. seeing as my dad had removed the remote control for safety reasons), started the car again, and of we went… at a whopping 10 Mph.

My dad; Gotta love this guy

So what happened? Being conscientious as he is, he had decided to limit to speed of my brand-new-all-out-honest-to-god Ferrari. For two reasons: environment and safety. Furthermore, he had removed one wheel so that he could save on the cost of this fantastic machine.

My Ferrari being a Share Point

Having gotten to grips with the situation, we decided to talk about all this in my new car (or what’s left of it). I mean, what else could I use it for, other than having a cup of coffee and collaborate on possible changes that would render the machine somewhat useful (or at least a bit less useless). Funny though, my dad had a UMC specially fitted. One in the front and one in the back. If was perfect for my Nano, giving us some really nice background music. Not to mention the coffee-maker stuck on the ceiling…

Back to business

If you believed the story above to be true, then this story really is for you. You see, the above actually does happen on a regular basis. Not with Ferrari’s I’d have to admit, but with the Ferrari amongst Web-Portal-Collaboration-Software called SharePoint.
Companies go all out buying this rather expensive piece of tooling, and then strip it down, because they don’t trust the users to behave. So where the tooling is designed to allow end-users to interact with, and setup new web-sites and pages, companies try to remove the options for fear of “web-site proliferation”. Unwarranted fear in my mind. True, when given the rights, end-users will experiment and create new sites. But isn’t that the whole idea? Isn’t that why you bought SharePoint… so that new sites can be easily created. And who knows better what they want from a new site then the actual user?
Instead, a small group of individuals has been tasked with deciding what users want, and create the sites for them (including some fancy shmancy functionality no one really needs); invariably resulting in sites that are painfully underused. An embarrassment even. Is the truth hurting already? Are you feeling the pain I deliver?
And why the fear? Take the following scenario:
Project teams have a need to collaborate. The team-lead or project-lead gets accredited with the rights to create project sites.
Now how many project-sites is he going to create? One, five, one hundred? I dare bet that (s)he will create a maximum of five project sites. No more…
“But” you say “what if he creates 25; and all his friends as well”. Here’s the parry: have administrators monitor the amount of sites per user. Should this fall above a threshold, then issue a warning. Does the culprit not listen? Then remove the rights to create sites. Now this is time and money well spent; and proliferation will soon die out, I assure you.

Educate

The point I’m trying to make is that it seems much wiser to not cripple the tooling you spent so much money on (and the action of crippling typically costs way more than the initial outlay), but instead educate people in the proper usage of your great piece of software. Use it to its full potential, and give end-users what they really want (not what you believe they should want), by giving them freedom and flexibility. Sure, provide them with extras, shortcuts, easy to use modules, and perhaps fix the branding. By all means, help them along by setting up an initial site for them. But do not limit the potential.
Don’t give your employees a 3-wheeled Ferrari doing a max of 10Mph, out of pure fear. Instead give them the Ferrari you can afford, and teach them how to drive and use it safely and properly. Keep an eye on them, and let them know when they cross the line. Revoke their license if they don’t listen. But please don’t punish the good citizens by limiting functionality. Punish the culprits instead by revoking their license….. 

Don't be like my dad.