3

I have a command handler which basically works like this:

ControlList.Handlers[CommandType.MyCommandComesHere].Handle(data);

Handlers is a Dictionary<CommandType, ICommandHandler> and CommandType is a enum.

Handle by its turn would lead it to this:

using System;
using log4net;

namespace My_Application
{
    public class MyCommand : ICommandHandler
    {
        private static readonly ILog Logger = LogManager.GetLogger(typeof(MyCommand));

        public void Handle(Events data)
        {
            Console.WriteLine("I can load cs files on the fly yay!!");
        }
    }
}

My question is how can I make so my application would compile and let me use that cs file while its running?

Any simple example of this would be greatly appreciated but not required as long as I can get some pointers as to what I need to look for as I am not even sure what do I need to make this happen.

To put it simple I am currently trying to understand how could I load a cs file into my application that is already compiled and is currently running.

leppie
  • 115,091
  • 17
  • 196
  • 297
Guapo
  • 3,446
  • 9
  • 36
  • 63
  • a cs file is never compiled because it is a source file. The only Thing you can do in this case is to load the Content of your cs file and compile it in runtime to use it. You schould declare an Interface your sourcecode inherits to work with. – S.L. Aug 15 '14 at 08:03
  • 1
    I'd use script hosting in cs-script, or look at its source for some inspiration: https://csscriptsource.codeplex.com/ – EventHorizon Aug 15 '14 at 08:06
  • @S.L. I am not entirely sure of the process this entails but I don't mind if it needs to be compiled or not as long as I am able to load and reload that specific code at wish as I change it without having turn off my application to compile something new etc, basically a plugin interface that accepts c# code – Guapo Aug 15 '14 at 08:06
  • @Guapo look at the first answer :) there it is explained – S.L. Aug 15 '14 at 08:08
  • Have you considered MEF? http://msdn.microsoft.com/en-us/library/dd460648(v=vs.110).aspx – CarlHembrough Aug 15 '14 at 08:09

2 Answers2

5

Using CodeDOM, you need to first create a compiler provider. (You might want to set GenerateExecutable to false and GenerateInMemory to true for your purposes.)

    var csc = new CSharpCodeProvider();
    var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, "foo.exe", true);
    parameters.GenerateExecutable = false;
    parameters.GenerateInMemory = true;

Then, you can compile the assembly using CompileAssemblyFromSource and get the CompilerResults returned from it. From this returned object, get a reference to the generated assembly, using its CompiledAssembly property.

    var results = csc.CompileAssemblyFromSource(parameters, "contents of the .cs file");
    var assembly = results.CompiledAssembly;

Then you can use reflection to create instances from that assembly and call methods on them.

    var instance = assembly.CreateInstance("MyCommand");
    // etc...

Alternatively, if you're only interested in short code snippets, it might be worth it to use Roslyn instead. You need to create a ScriptEngine first.

var engine = new ScriptEngine();

Then you can just Execute strings on it - or Execute<T> if you're confident that the expression in the string returns a type assignable to T.

var myObject = engine.Execute("1+1");
var myInt = engine.Execute<int>("1+1");

It's definitely more immediate, so it's worth looking into if it serves your purpose.

Community
  • 1
  • 1
Theodoros Chatzigiannakis
  • 28,773
  • 8
  • 68
  • 104
  • +1 This looks very interesting, what if I wanted to reload the same file, will the methods and stuff be cache or not allow me to reload a given file over and over? – Guapo Aug 15 '14 at 09:01
  • No, I don't think it's cached (but it's been a long time since I last used it, so I might be wrong). – Theodoros Chatzigiannakis Aug 15 '14 at 09:06
1

I have looked for different ways to achieve this and found cs script library lightweight and usable. Here is code snippet how I use it. It runs cs code within app domain so it presumes, that the cs script being compiled comes form trusted source.

using CSScriptLibrary;
using csscript;
using System.CodeDom.Compiler;
using System.Reflection;

    //Method example - variable script contains cs code
    //This is used to compile cs to DLL and save DLL to a defined location
    public Assembly GetAssembly(string script, string assemblyFileName)
    {
        Assembly assembly;
        CSScript.CacheEnabled = true;            
        try
        {
            bool debugBuild = false;
#if DEBUG
            debugBuild = true;
#endif
            if (assemblyFileName == null)
                assembly = CSScript.LoadCode(script, null);
            else
                assembly = CSScript.LoadCode(script, assemblyFileName, debugBuild, null);
            return assembly;
        }
        catch (CompilerException e)
        {
            //Handle compiler exceptions
        }
    }

    /// <summary>
    /// Runs the code either form script text or precompiled DLL
    /// </summary>
    public void Run(string script)
    {
        try
        {
            string tmpPath = GetPathToDLLs();  //Path, where you store precompiled DLLs

            string assemblyFileName;
            Assembly assembly = null;
            if (Directory.Exists(tmpPath))
            {
                assemblyFileName = Path.Combine(tmpPath, GetExamScriptFileName(exam));
                if (File.Exists(assemblyFileName))
                {
                    try
                    {
                        assembly = Assembly.LoadFrom(assemblyFileName); //Načtení bez kompilace
                    }
                    catch (Exception exAssemblyLoad)
                    {
                        Tools.LogError(exAssemblyLoad.Message);
                        assembly = null;
                    }
                }
            }
            else
                assemblyFileName = null;

            //If assembly not found, compile it form script string
            if (assembly ==null) 
                assembly = GetAssembly(script, assemblyFileName);
            AsmHelper asmHelper = new AsmHelper(assembly);

            //This is how I use the compiled assembly - it depends on your actual code
            ICalculateScript calcScript = (ICalculateScript)asmHelper.CreateObject(GetExamScriptClassName(exam));
            cex = calcScript.Calculate(this, exam);
            Debug.Print("***** Calculated {0} ****", exam.ZV.ZkouskaVzorkuID);
        }
        catch (Exception e)
        {
            //handle exceptions
        }
    }
Vojtěch Dohnal
  • 7,867
  • 3
  • 43
  • 105