Convert string to executable code performance - c#

I'm looking into solutions to convert a string into executeable code. My code is extremely slow and executes at 4.7 seconds. Is there any faster way of doing this?
String: "5 * 5"
Output: 25
The code:
class Program {
static void Main(string[] args) {
var value = "5 * 5";
Stopwatch sw = new Stopwatch();
sw.Start();
var test = Execute(value);
sw.Stop();
Debug.WriteLine($"Execute string at: {sw.Elapsed}");
}
private static object Execute(string content) {
var codeProvider = new CSharpCodeProvider();
var compilerParameters = new CompilerParameters {
GenerateExecutable = false,
GenerateInMemory = true
};
compilerParameters.ReferencedAssemblies.Add("system.dll");
string sourceCode = CreateExecuteMethodTemplate(content);
CompilerResults compilerResults = codeProvider.CompileAssemblyFromSource(compilerParameters, sourceCode);
Assembly assembly = compilerResults.CompiledAssembly;
Type type = assembly.GetType("Lab.Cal");
MethodInfo methodInfo = type.GetMethod("Execute");
return methodInfo.Invoke(null, null);
}
private static string CreateExecuteMethodTemplate(string content) {
var builder = new StringBuilder();
builder.Append("using System;");
builder.Append("\r\nnamespace Lab");
builder.Append("\r\n{");
builder.Append("\r\npublic sealed class Cal");
builder.Append("\r\n{");
builder.Append("\r\npublic static object Execute()");
builder.Append("\r\n{");
builder.AppendFormat("\r\nreturn {0};", content);
builder.Append("\r\n}");
builder.Append("\r\n}");
builder.Append("\r\n}");
return builder.ToString();
}
}

Well, there is an easier hack:
var _Result = new DataTable().Compute("5*5"); // _Result = 25
var _Result2 = new DataTable().Compute("5+5*5"); // _Result2 = 30
It also has more options. Please have a look at the Documentation.

Related

How to improve compile time using CSharpCodeProvider

I have an application where user can create its own formula it works fine
for all case but when i have DateTime Fields in my formula the compilation part of the code take around 132 ms for one formula where at the same time i
have 31 formula each with different value.
using Microsoft.CSharp;
using System;
using System.CodeDom.Compiler;
namespace abc
{
public class Abc
{
static void Main()
{
var code = #"
using System;
namespace First
{
public class program
{
public static int Main()
{
if(Convert.ToDateTime(""01-04-2019 10:25:00"")>Convert.ToDateTime(""01-04-2019 15:00:00""))
{
return 1;
}
else
{
return 0;
}
}
}
}";
Console.WriteLine(code);
var options = new CompilerParameters();
options.GenerateExecutable = false;
options.GenerateInMemory = false;
var provider = new CSharpCodeProvider();
var compile = provider.CompileAssemblyFromSource(options, code);
var type = compile.CompiledAssembly.GetType("First.program");
var abc = Activator.CreateInstance(type);
var method = type.GetMethod("Main");
var result = method.Invoke(abc, null);
Console.WriteLine(result); //output: abc
}
}
}
At this point
var compile = provider.CompileAssemblyFromSource(options, code);
It takes 132 ms but if don't use any date fields it takes 1 ms

Get line number of exception of dynamically loaded assembly

I have an external text file, it is C# class with some logic. In my main program I need to compile that file and run whatever method of that class.
Example of the external class:
using System;
public class DymamicClass
{
public string TestValue()
{
var items = new string[] { "item1", "item2" };
return items[9];
}
}
For loading external file I use these steps:
CSharpCodeProvider Compiler = new CSharpCodeProvider();
List<string> importDlls = new List<string>(new string[] { "System.dll", "System.Data.dll" });
CompilerParameters compilerPars = new CompilerParameters(importDlls.ToArray());
compilerPars.GenerateInMemory = true;
compilerPars.IncludeDebugInformation = true;
compilerPars.CompilerOptions += " /debug:pdbonly";
string path = Assembly.GetExecutingAssembly().Location;
compilerPars.ReferencedAssemblies.Add(path);
CompilerResults Results = Compiler.CompileAssemblyFromFile(compilerPars, codePath);
After the file loaded to the program, I try to execute TestValue() method from the loaded class. There is "index was outside the bounds of the array" exception in the method. I need to get the exact line that threw exception. But instead of line "return items[9];" I always get the line "public string TestValue()".
Here is sample how I process exception:
var trace = new System.Diagnostics.StackTrace(exception, true);
if (trace.FrameCount > 0)
{
var frame = trace.GetFrame(trace.FrameCount - 1);
var className = frame.GetMethod().ReflectedType.Name;
var methodName = frame.GetMethod().ToString();
var lineNumber = frame.GetFileLineNumber();
}
How to get the right line of exception?
you need to get Inner Exception. Try in this way :
using System;
using System.Collections.Generic;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;
public class Folders
{
public static void Main(string[] args)
{
try
{
var source = #"using System;
public static class DymamicClass
{
public static string TestValue()
{
var items = new string[] { ""item1"", ""item2"" };
return items[9];
}
}";
CSharpCodeProvider Compiler = new CSharpCodeProvider();
List<string> importDlls = new List<string>(new string[] { "System.dll", "System.Data.dll" });
CompilerParameters compilerPars = new CompilerParameters(importDlls.ToArray());
compilerPars.GenerateInMemory = true;
compilerPars.IncludeDebugInformation = true;
compilerPars.CompilerOptions += " /debug:pdbonly";
string path = Assembly.GetExecutingAssembly().Location;
compilerPars.ReferencedAssemblies.Add(path);
CompilerResults Results = Compiler.CompileAssemblyFromSource(compilerPars, source);
Assembly assembly = Results.CompiledAssembly;
Type program = assembly.GetType("DymamicClass");
MethodInfo main = program.GetMethod("TestValue");
main.Invoke(null, null);
}
catch (Exception e)
{
var trace = new System.Diagnostics.StackTrace(e.InnerException, true);
if (trace.FrameCount > 0)
{
var frame = trace.GetFrame(trace.FrameCount - 1);
var className = frame.GetMethod().ReflectedType.Name;
var methodName = frame.GetMethod().ToString();
var lineNumber = frame.GetFileLineNumber();
}
}
}
}

C# Compile at Runtime Visual Studio unusual behavior

Helo,
i have a strange behavior in Visual Studio.
My code is a compiling at runtime, in VS it
workts fine but if i start the programm from
release-folder the compiled dll seems to be blocked,
what is not that unusal but why does VS dont say that?
Can some one tell me also how to unload a compiled dll
in runtime?
VS:
Release-Folder:
My Test Code here:
static void Main(string[] args)
{
var property = "Value";
var key = "MyKey";
var binding = string.Format("{0}.{1}", key, property);
var dllName = string.Format("Condition_{0}_{1}.dll", key, property);
var input = "65535";
var condition = "Convert.ToInt64(input) > 0xFFFF ? 0 : 501";
for (var i = 0; i < 5; i++)
{
try
{
var conditionCompiled = BuildCondition(key, property, condition);
Type moduleType = null;
if (conditionCompiled.Errors.HasErrors)
{
if (conditionCompiled.Errors[0].ErrorNumber != "CS0042")
throw new ConditionCompileException(conditionCompiled.Errors[0].ErrorText);
var assemblyPath = Path.Combine(Directory.GetCurrentDirectory(), dllName);
var assembly = Assembly.LoadFile(assemblyPath);
moduleType = assembly.GetType("DynaCore.ConditionRunner");
}
else
{
var module = conditionCompiled.CompiledAssembly.GetModules()[0];
moduleType = module.GetType("DynaCore.ConditionRunner");
}
var method = moduleType.GetMethod("RunCondition");
var value = method.Invoke(null, new object[] { input });
Console.WriteLine("DynaCore.ConditionRunner.RunCondition({0}) => {1}", input, value);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
if (e.InnerException != null)
Console.WriteLine(e.InnerException.Message);
}
}
Console.ReadLine();
}
private static CompilerResults BuildCondition(string name, string property, string condition)
{
var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } });
var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, string.Format("Condition_{0}_{1}.dll", name, property), true);
parameters.GenerateExecutable = false;
var results = csc.CompileAssemblyFromSource(parameters, string.Format(
#" using System.Linq;
using System;
using DESFireSDK.Dependency;
namespace DynaCore
{{
class ConditionRunner
{{
public static object RunCondition(string input)
{{
return {0};
}}
}}
}}"
, condition));
return results;
}

C# CodeProvider Error: Constructor on type not found

I get the error: Constructor on type 'SimpleScript.Generator' not found.
I tried passing the correct parameters but i still get this error, this is my source code, and the script is a very simple piece of code that generates the Array of Element head and body. And also it is compiled successfully but it throws the error at the execution line.
string source = #"
using System;
using MSiteDLL;
namespace SimpleScript
{
public static class Generator
{
public static Document Generate(Data server)
{
"+script+ #"
Block[] blocks = {
new Block(""head"", head),
new Block(""body"", body),
};
return new Document(blocks);
}
}
}
";
Dictionary<string, string> providerOptions = new Dictionary<string, string>
{
{"CompilerVersion", "v4.0"}
};
CSharpCodeProvider provider = new CSharpCodeProvider(providerOptions);
CompilerParameters compilerParams = new CompilerParameters
{
GenerateInMemory = true,
GenerateExecutable = false,
ReferencedAssemblies = {
"System.dll",
"System.Core.dll",
"MSiteDLL.dll",
}
};
CompilerResults results = provider.CompileAssemblyFromSource(compilerParams, source);
if (results.Errors.Count != 0)
{
string output = "";
foreach (CompilerError y in results.Errors)
{
output += y.ErrorText + Environment.NewLine;
}
throw new Exception("Compile failed:" + output);
}
object o = results.CompiledAssembly.CreateInstance("SimpleScript.Generator");
MethodInfo mi = o.GetType().GetMethod("Generate");
Data[] parametersArray = new Data[] { server };
Document x = (Document)mi.Invoke(o, parametersArray);
return x;
Since your class is static, you should invoke the method in a static way.
So first, remove this line:
object o = results.CompiledAssembly.CreateInstance("SimpleScript.Generator");
And use those to invoke:
MethodInfo mi = Type.GetType("SimpleScript.Generator").GetMethod("Generate");
Data[] parametersArray = new Data[] { server };
Document x = (Document)mi.Invoke(null, parametersArray);

Compile Func<T1, TResult> from string on the fly

I have a string string input; with some code (all below is in that string)
var x = s.IndexOf("a");
return String.Format(s, x);
Now, I would like to achieve following scenario:
Func<string, string> f = Compile(input);
var test = "dcba - {0}";
var result = f(test);
// result = "dcba - 3";
I assume, that the actual T1, TResult are known (here: string, string), and that input is named "s". I can achieve it this way:
var input = "var x = s.IndexOf(\"a\"); return String.Format(s, x);";
var containerClass = #"using System; class TempClass {{ public string temp_func(string s){{ {0} }} }}";
var code = String.Format(containerClass, input);
// Create a new instance of the C# compiler
var compiler = new CSharpCodeProvider();
var params = new CompilerParameters
{
GenerateExecutable = false,
GenerateInMemory = true
};
params.ReferencedAssemblies.Add("System.dll");
var results = compiler.CompileAssemblyFromSource(params, code);
Func<string, string> f;
if (results.Errors.Count == 0)
{
f = s =>
{
var myClass = results.CompiledAssembly.CreateInstance("TempClass");
return (string) myClass.GetType().
GetMethod("temp_func").
Invoke(myClass, new object[] {s});
};
// test:
Console.WriteLine(f(Console.ReadLine()));
}
But it is fairly complicated way. Is there any way to simplify this, if I know that I just want a Func<T1, TResult>, not a whole compiled assembly, instancing classes (or calling a static method on one)?
I can take this code, of course, and dress it nicely - wrap it in a generic class, get T1, TResult type names to put into the TempClass template (String.Format("public {0} temp_func({1} s)",typeof(TResult).Name, typeof(T1).Name);), but it has a feel of greasing an axle of a square wheel to make ride smoother...
I went with something like this:
public class DynamicFunction
{
private static int _counter = 0;
private const string ClassBody = "{2} public static class DynamicFunctionHost{0} {{ {1} }}";
private const string ClassName = "DynamicFunctionHost{0}";
private const string FunctionName = "func";
private const string T1FuncBody = "public static {1} func({0} param1){{ {2} }}";
public static Func<T1, TResult> Get<T1, TResult>(string funcBody, string[] referenced, string[] usingNs)
{
var code = String.Format(ClassBody, _counter,
String.Format(T1FuncBody, typeof (T1).Name, typeof (TResult).Name, funcBody),
String.Join("\n", usingNs.Select(r => String.Format("using {0};", r))));
var result = Compile(code, referenced);
var host =
result.CompiledAssembly.DefinedTypes.Single(
typeinfo => typeinfo.FullName.Equals(String.Format(ClassName, _counter)));
++_counter;
return input => (TResult) host.GetMethod(FunctionName).Invoke(null, new object[] { input });
}
private static CompilerResults Compile(string code, string[] referenced)
{
var compiler = new CSharpCodeProvider();
var parameters = new CompilerParameters
{
GenerateExecutable = false,
GenerateInMemory = true
};
foreach (var r in referenced)
parameters.ReferencedAssemblies.Add(r);
var results = compiler.CompileAssemblyFromSource(parameters, code);
if (results.Errors.Count == 0) return results;
// else
var e = new ArgumentException("Errors during compilation", "code");
e.Data.Add("Errors", results.Errors);
throw e;
}
}
The use is fairly simple then:
var f = DynamicFunction.Get<string, string[]>("return param1.ToCharArray()", new []{"System.dll","System.Core.dll"}, new []{"System"});
var x = f("abcd"); // =[a,b,c,d]

Categories