Reputation: 32694
I am using the CS-Script library to execute dynamic code. Rather than using it as a script engine, I want to use it to plug functionality into an application on the fly. Here's the hosting code...
using System;
using CSScriptLibrary;
using System.Reflection;
using System.IO;
namespace CSScriptTester
{
class Program
{
static void Main(string[] args)
{
// http://www.csscript.net/
Console.WriteLine("Running Script.");
CSScript.Evaluator.ReferenceAssembly(Assembly.GetAssembly(typeof(System.Windows.Forms.MessageBox)));
string code = File.ReadAllText("SomeCode/MyScript.cs");
dynamic block = CSScript.Evaluator.LoadCode(code);
block.ExecuteAFunction();
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
}
And here's the contents of SomeCode/MyScript.cs...
using System;
using System.Windows.Forms;
namespace CSScriptTester.SomeCode
{
class MyScript
{
public void ExecuteAFunction()
{
MessageBox.Show("Hello, world!");
}
}
}
This works fine. In the hosting code, I don't want the hosting code to be responsible for specifying assembly references. So I comment out CSScript.Evaluator.ReferenceAssembly(Assembly.GetAssembly(typeof(System.Windows.Forms.MessageBox)));
and run it and I get the error...
error CS0234: The type or namespace name
Forms' does not exist in the namespace
System.Windows'. Are you missing an assembly reference?
I know if I were executing this using the command line tools I could add this to the top of the script to add the reference...
//css_reference System.Windows.Forms.dll
But that seems to be ignored when executing it in the context of a .NET Application. How can I get it to resolve the references properly?
Upvotes: 6
Views: 9888
Reputation: 141
This is the expected usage pattern:
IScript script = CSScript.Evaluator.ReferenceAssembly("<local path to dependency>.dll");
.LoadFile<IScript>("<local path to script file");
or
string code = File.ReadAllText("SomeCode/MyScript.cs");
dynamic block = CSScript.Evaluator
.ReferenceAssembliesFromCode(code)
.LoadCode(code);
block.ExecuteAFunction();
As for "I would be very interested in the reason it doesn't do it automagically too", it is because this call is potentially expensive. If you do it the script engine will analyze every using
statement and try to look it up in all well known locations and GAC. It will do the analysis of all possible //css_*
directives.
That's why the intended use case is to reference the assemblies with the explicit API methods but not via the script code even though it is possible via the method ReferenceAssembliesFromCode
.
But what is even more important is that if the script engine does it automatically the the user have no way of specifying assembly probing directories.
That is why you are better off referencing the asseblies with any of these dedicated methods:
IEvaluator ReferenceAssembliesFromCode(string code, params string[] searchDirs);
IEvaluator ReferenceAssembly(string assembly);
IEvaluator ReferenceAssembly(Assembly assembly);
IEvaluator ReferenceAssemblyByName(string assemblyPartialName);
IEvaluator TryReferenceAssemblyByNamespace(string @namespace, out bool resolved);
IEvaluator ReferenceAssemblyByNamespace(string @namespace);
IEvaluator ReferenceAssemblyOf(object obj);
IEvaluator ReferenceAssemblyOf<T>();
IEvaluator ReferenceDomainAssemblies(DomainAssemblies assemblies = DomainAssemblies.AllStaticNonGAC);
Upvotes: 0
Reputation: 140
I have resolved this another way, I set my needed referenced assemblies "Copy Local" property to true and load them into my Evaluator before loading my scripts.
I recommend doing this as it will pre-compile and store the loaded assemblies making ad-hoc loading of scripts faster. Instead of looking up the referenced assemblies from the GAC or elsewhere every time you load a script, it simply has them and goes about loading the script as efficiently as possible.
CSScript.Evaluator.ReferenceAssembly("<local path to dependency>.dll");
IScript script = CSScript.Evaluator.LoadFile<IScript>("<local path to script file");
Where the "local path to dependency" is just the name of the referenced object which can be found as the "Description" string of any referenced assembly in your project.
Upvotes: 0
Reputation: 32694
Figured it out.
string code = File.ReadAllText("SomeCode/MyScript.cs");
CSScript.Evaluator.ReferenceAssembliesFromCode(code);
dynamic block = CSScript.Evaluator.LoadCode(code);
block.ExecuteAFunction();
I'm surprised that it doesn't automatically do this.
Upvotes: 3