Instantiating a ruby class that is in a Module from within C# - c#

I'm attempting to re-use some ruby classes I wrote a while back within an ASP.NET MVC 2 project. The issue I'm having is if a class is within a Module I can't seem to instantiate it. If I move the class outside of the module it works fine. Here is a scaled down version of the class I want to instantiate:
module Generator
class CmdLine
attr_accessor :options
def initialize(output)
end
def run(args=[])
end
end
end
If comment out the module portion I can create the object. Am I doing something wrong? Here is the C# code:
var engine = Ruby.CreateEngine();
var searchPaths = engine.GetSearchPaths().ToList();
searchPaths.Add(#"c:\code\generator\lib");
searchPaths.Add(#"C:\Ruby-ri-192\lib\ruby\1.9.1");
engine.SetSearchPaths(searchPaths);
engine.ExecuteFile(#"c:\code\generator\lib\generator\generator_cmd_line.rb");
var rubyCmdLineObj = engine.Runtime.Globals.GetVariableNames();
// These lines works when I comment out the module
// var genCmdLineObj = engine.Runtime.Globals.GetVariable("CmdLine");
// var cmdLineObj = engine.Operations.CreateInstance(genCmdLineObj);
// var results = engine.Operations.InvokeMember(cmdLineObj, "run");
// return Content(results);
var sb = new StringBuilder();
foreach (var name in rubyCmdLineObj)
{
sb.AppendFormat("{0} ", name);
}
return Content(sb.ToString());
I have a work around - creating a separate class that I can call from within C# but if I don't have to do that I'd rather not do it. Any guidance would be greatly appreciated.

I know this is kind of a hack/workaround, but I managed to do it this way:
Add the next code to the end of your ruby file:
def hack(s)
eval(s)
end
Now your C# code would look like that:
var engine = Ruby.CreateEngine();
var scope = engine.ExecuteFile(#"c:\code\generator\lib\generator\generator_cmd_line.rb");
var genCmdLineObj = engine.Execute(String.Format("hack('{0}::{1}')", "Generator", "CmdLine"), scope);
var cmdLineObj = engine.Operations.CreateInstance(genCmdLineObj);
var results = engine.Operations.InvokeMember(cmdLineObj, "run");
return Content(results);
Kind of a hack, but hey, it works! :)

I would create a new IronRuby project, take your original Ruby code and port/compile it into a .NET library.
No more need to call out at all. You have something that can be called natively from C#.

The solution #Shay Friedman proposed is unnecessary.
gen.rb
module Generator
class CmdLine
attr_accessor :options
def initialize(output)
#output = output
end
def run(args=[])
puts "Hello from cmdLine with #{#output} #{args}"
end
end
end
csharp
void Main()
{
var engine = Ruby.CreateEngine();
var buffer = new MemoryStream();
engine.Runtime.IO.SetOutput(buffer, new StreamWriter(buffer));
engine.ExecuteFile(#"c:\temp\gen.rb");
ObjectHandle handle = engine.ExecuteAndWrap("Generator::CmdLine.new('the output')");
var scope = engine.CreateScope();
scope.SetVariable("myvar", handle);
engine.Execute("myvar.run", scope);
engine.Operations.InvokeMember(handle.Unwrap(), "run", "InvokeMember");
buffer.Position = 0;
Console.WriteLine(new StreamReader(buffer).ReadToEnd());
}
output
Hello from cmdLine with the output []
Hello from cmdLine with the output InvokeMember

Related

C#.Net ClearsScript V8 enable modules

Is there a way to enable "import <module>.js;" statements in an embedded V8 Runtime using Microsoft ClearScript?
I couldn't find any examples and makes me think I'd have to parse the script file myself first to enable this.
Yes there is. See answer to this question as the basic approach. That question was about enabling CommonJS Require. For ES6 modules approach you will want something like this:
using System;
using Microsoft.ClearScript;
using Microsoft.ClearScript.JavaScript;
using Microsoft.ClearScript.V8;
namespace ConsoleApp1
{
class Program
{
static void Main()
{
V8ScriptEngine engine = new V8ScriptEngine();
engine.DocumentSettings.SearchPath = #"c:\temp\js\";
engine.DocumentSettings.AccessFlags = DocumentAccessFlags.EnableFileLoading;
engine.AddHostType(typeof(Console));
Console.WriteLine("Hello from C#");
engine.Execute(new DocumentInfo() { Category = ModuleCategory.Standard }, #"
Console.WriteLine('Hello from Javascript');
import {print} from 'a.js';
import {add} from 'b.js';
print(add(30, 12));
Console.WriteLine('Javascript signing off...');
");
Console.ReadKey();
}
}
}
Where the JS looks like this:
// File c:\temp\js\a.js
export function print(txt) {
Console.WriteLine('The answer to Life etc is ... ' + txt);
}
// File c:\temp\js\b.js
export function add(var1, var2) {
return var1 + var2;
}
Result is:
This example is reading files from the local file system as indicated by the AccessFlags = EnableFileLoading setting, with the SearchPath setting giving the path, or paths if given a comma-separated list of paths.
It is the 'ModuleCategory.Standard' option that enables ES6-stylee module loading.
You could also use a roll-your-own require() function similar to node.js. I wrote a blog article here and there's a working CodePen to illustrate the approach which was inspired by an article by Michele Nasti which was itself based on the ideas in chapter 10 of the book Eloquent JavaScript by Marijn Haverbeke.
Here is my coded example:
const myCode1 = `
let utils = function (){
this.say = function(x){
log('utils: says = ' + x)
};
return this;
}
exports.utils = utils;
`;
const myCode2 = `
let customer = function (){
this.say = function(x){
log('Customer: says = ' + x)
};
return this;
}
exports.customer = customer;
`;
/*
*/
// I am loading simple source code strings into an array - in the real solution
// you would use some kind of source code fetch / load to string process.
let sourceCode = {c1: myCode1, c2: myCode2};
myRequire.cache = Object.create(null);
function myRequire(name) {
log(`myRequire: You require file ${name}`)
if (!(name in myRequire.cache)) {
log(`myRequire: ${name} is not in cache; reading from disk`)
let code = sourceCode[name]; // load the code - this is bespoke to your env
let module = {exports: {}};
myRequire.cache[name] = module;
let wrapper = Function("require, exports, module", code);
wrapper(myRequire, module.exports, module);
}
log(`myRequire: ${name} is in cache. Returning it...`)
return myRequire.cache[name].exports;
}
// myRequire() produces an object from the 'exports' object in the loaded code.
//let myExports = new myRequire('c1');
// we can then refer to these as
// let util = myExports.utils();
// or just use
// let util = new myRequire('c1').utils();
// So...Require c1 will create an object with exports.
let util = new myRequire('c1').utils();
util.say('I am alive!')
log("");
// Require c2 will create an object with exports.
let cust = new myRequire('c2').customer();
cust.say('I am alive too!')
function log(msg){
$('#log').html($('#log').html() + "<br />" + msg);
}
The output is
myRequire: You require file c1
myRequire: c1 is not in cache; reading from disk
myRequire: c1 is in cache. Returning it...
utils: says = I am alive!
myRequire: You require file c2
myRequire: c2 is not in cache; reading from disk
myRequire: c2 is in cache. Returning it...
Customer: says = I am alive too!

How to serialize tensor input required by dnnclassifier (serving_input_reciever)

I want to be able to use the dnnclassifier (estimator) on top of IIS using tensorflowsharp. The model has previously been trained in python. I got so far that I can now generate PB files, know the correct input/outputs, however I am stuck in tensorflowsharp using string inputs.
I can create a valid .pb file of the iris dataset. It uses the following feate_spec:
{'SepalLength': FixedLenFeature(shape=(1,), dtype=tf.float32, default_value=None), 'SepalWidth': FixedLenFeature(shape=(1,), dtype=tf.float32, default_value=None), 'PetalLength': FixedLenFeature(shape=(1,), dtype=tf.float32, default_value=None), 'PetalWidth': FixedLenFeature(shape=(1,), dtype=tf.float32, default_value=None)}
I have created a simple c# console to try and spin it up. The input should be an "input_example_tensor" and the output is located in "dnn/head/predictions/probabilities". This I discoved after alex_zu provided help using the saved_model_cli command here.
As far as I am aware all tensorflow estimator API's work like this.
Here comes the problem: the input_example_tensor should be of a string format which will be parsed internally by the ParseExample function. Now i am stuck. I have found TFTensor.CreateString, but this doesn't solve the problem.
using System;
using TensorFlow;
namespace repository
{
class Program
{
static void Main(string[] args)
{
using (TFGraph tfGraph = new TFGraph()){
using (var tmpSess = new TFSession(tfGraph)){
using (var tfSessionOptions = new TFSessionOptions()){
using (var metaGraphUnused = new TFBuffer()){
//generating a new session based on the pb folder location with the tag serve
TFSession tfSession = tmpSess.FromSavedModel(
tfSessionOptions,
null,
#"path/to/model/pb",
new[] { "serve" },
tfGraph,
metaGraphUnused
);
//generating a new runner, which will fetch the tensorflow results later
var runner = tfSession.GetRunner();
//this is in the actual tensorflow documentation, how to implement this???
string fromTensorflowPythonExample = "{'SepalLength': [5.1, 5.9, 6.9],'SepalWidth': [3.3, 3.0, 3.1],'PetalLength': [1.7, 4.2, 5.4],'PetalWidth': [0.5, 1.5, 2.1],}";
//this is the problem, it's not working...
TFTensor rawInput = new TFTensor(new float[4]{5.1f,3.3f,1.7f,0.5f});
byte[] serializedTensor = System.Text.Encoding.ASCII.GetBytes(rawInput.ToString());
TFTensor inputTensor = TensorFlow.TFTensor.CreateString (serializedTensor);
runner.AddInput(tfGraph["input_example_tensor"][0], inputTensor);
runner.Fetch("dnn/head/predictions/probabilities", 0);
//start the run and get the results of the iris example
var output = runner.Run();
TFTensor result = output[0];
//printing response to the client
Console.WriteLine(result.ToString());
Console.ReadLine();
}
}
}
}
}
}
}
This example will give the following error:
An unhandled exception of type 'TensorFlow.TFException' occurred in TensorFlowSharp.dll: 'Expected serialized to be a vector, got shape: []
[[Node: ParseExample/ParseExample = ParseExample[Ndense=4, Nsparse=0, Tdense=[DT_FLOAT, DT_FLOAT, DT_FLOAT, DT_FLOAT], dense_shapes=[[1], [1], [1], [1]], sparse_types=[], _device="/job:localhost/replica:0/task:0/device:CPU:0"](_arg_input_example_tensor_0_0, ParseExample/ParseExample/names, ParseExample/ParseExample/dense_keys_0, ParseExample/ParseExample/dense_keys_1, ParseExample/ParseExample/dense_keys_2, ParseExample/ParseExample/dense_keys_3, ParseExample/Const, ParseExample/Const, ParseExample/Const, ParseExample/Const)]]'
How can I serialize tensors in such a way that I can use the pb file correctly?
I also posted the issue on github, here you can find the iris example python file, pb file and the console applications. In my opinion solving this creates a
neat solution for all tensorflow users having ancient production environments (like me).
The Expected serialized to be a vector, got shape: [] error can be fixed by using an overload of the TFTensor.CreateString function: Instead of directly taking a string, the model apparently expects a vector containing a single string:
TFTensor inputTensor = TFTensor.CreateString(new byte[][] { bytes }, new TFShape(1));
The input_example_tensor in your case now expects a serialized Example protobuf message (see also the docs and the example.proto file).
Using the protobuf compiler, I've generated a C# file containing the Example class. You can download it from here: https://pastebin.com/iLT8MUdR. Specifically, I used this online tool with CSharpProtoc and replaced the import "tensorflow/core/example/feature.proto"; line by the messages defined in that file.
Once you've added the file to your project, you'll need a package reference to Google.Protobuf. Then, you can pass serialized examples to the model like this:
Func<float, Tensorflow.Feature> makeFeature = (float x) => {
var floatList = new Tensorflow.FloatList();
floatList.Value.Add(x);
return new Tensorflow.Feature { FloatList = floatList };
};
var example = new Tensorflow.Example { Features = new Tensorflow.Features() };
example.Features.Feature.Add("SepalLength", makeFeature(5.1f));
example.Features.Feature.Add("SepalWidth", makeFeature(3.3f));
example.Features.Feature.Add("PetalLength", makeFeature(1.7f));
example.Features.Feature.Add("PetalWidth", makeFeature(0.5f));
TFTensor inputTensor = TFTensor.CreateString(
new [] { example.ToByteArray() }, new TFShape(1));
runner.AddInput(tfGraph["input_example_tensor"][0], inputTensor);
runner.Fetch("dnn/head/predictions/probabilities", 0);
//start the run and get the results of the iris example
var output = runner.Run();
TFTensor result = output[0];

Adding scripting functionality to existing C# project

I work in a research group and I've been tasked with adding in scripting functionality to a data acquisition program. Ideally, I want to have the ability to write scripts while the data acquisition software is running (and save those scripts as files on the go). A command line might also be nice.
I'm not very experienced at all with C#, but I do have quite a bit of coding experience in other language (Objective-C, Python). I saw this link https://blogs.msdn.microsoft.com/cdndevs/2015/12/01/adding-c-scripting-to-your-development-arsenal-part-1/ which details the "Roselyn Scripting Package" but I'm not sure if that's my best option.
Can anybody suggest the easiest way of getting full scripting functionality? (I'm trying to avoid losing months of my life here =p). Links to start at/advice is much appreciated.
Thanks!
You posted an interesting link for me because I just prototyped something like that some weeks ago, but have not implemented yet. My goal is to create a C# "immediate" console on a webpage.
Have some minor issues regarding loading some assemblies programatically and have to reference them explicitly.
Here is the code behind, please later post your solution, I would be interested to know.
This alows to write c# code at runtime and also get a String return.
protected void getImmediateResult_Click(object sender, EventArgs e)
{
//building the code
string source = #"using System;
class MyType{
public static String Evaluate(){
<!expression!>
}}";
string expression = this.txtimmediate.Text;
string finalSource = source.Replace("<!expression!>", expression);
textcodeCheck.Text = finalSource;
var compileUnit = new CodeSnippetCompileUnit(finalSource);
//preparing compilation
CodeDomProvider provider = new Microsoft.CSharp.CSharpCodeProvider();
// Create the optional compiler parameters
//this correctly references the application but no System.Web etc
string[] refArray = new string[2];
UriBuilder uri = new UriBuilder(Assembly.GetExecutingAssembly().CodeBase);
refArray[0] = uri.Path;
//this works
refArray[1] = "System.Web" + ".dll";
////NOT WORKING for non microsoft assemblies
//var allRefs = Assembly.GetExecutingAssembly().GetReferencedAssemblies();
//string[] refArray = new string[allRefs.Length + 1];
//int i = 1;
//foreach (AssemblyName refer in allRefs)
//{
// refArray[i] = refer.Name + ".dll";
// i++;
//}
var compilerParameters = new CompilerParameters(refArray);
CompilerResults compilerResults = provider.CompileAssemblyFromDom(compilerParameters, compileUnit);
if (compilerResults.Errors.Count > 0)
{
//1st error
this.txtResult.Text = compilerResults.Errors[0].ErrorText;
return;
}
//running it
Type type = compilerResults.CompiledAssembly.GetType("MyType");
MethodInfo method = type.GetMethod("Evaluate");
String result = (String)method.Invoke(null, null);
this.txtResult.Text = result;
}
If you're willing to use IronPython, you can execute scripts directly in C#:
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
private static void doPython()
{
ScriptEngine engine = Python.CreateEngine();
engine.ExecuteFile(#"test.py");
}
Get IronPython here.

Generating semantic code with roslyn

We try to figure out how to generate code with Roslyn. I'm not speaking about something like CSharpSyntaxTree.ParseText that will take some strings and convert them into an AST. Instead, I would like to build my model somehow like this (pseudo code):
Create file as compilation unit
Add class MyClass to file
Add method DoSomething to MyClass
Set body of DoSomething in a similar fashion like System.Linq.Expressions
We recently discovered Microsoft.CodeAnalysis.CSharp.SyntaxFactory, and it seemed to be promising. However, obviously we have to add trivia ourselves.
After building a tree with SyntaxFactory.CompilationUnit() and adding some members back and forth, the output of ToFullString() is just a bunch of text, that is neither readable, nor compilable (e.g., missing braces). Do we miss something when generating the text from the model?
EDIT:
When using workspaces, you can set options affecting the whitespace behavior:
public string Generate (CompilationNode rootNode)
{
var cw = new CustomWorkspace();
cw.Options.WithChangedOption (CSharpFormattingOptions.IndentBraces, true);
var formattedCode = Formatter.Format (CreateFile(rootNode), cw);
return formattedCode.ToFullString();
}
This already yields a better result. Can someone confirm this as a good solution or is it rather a hack?
One problem remains. We want to generate an auto-property, currently using SF.AccessorDeclaration but it misses the semicolon when converting to the full string.
You basically have to add block definitions, then Roslyn handles the trivia for you as long as you use the Formatter (as you have written)
Here is an example for a simple class that is generated correctly without myself having to specify any trivia
var consoleWriteLine = Syntax.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
Syntax.IdentifierName("Console"),
name: Syntax.IdentifierName("WriteLine"));
var arguments = Syntax.ArgumentList (
Syntax.SeparatedList (
new[]
{
Syntax.Argument (
Syntax.LiteralExpression (
SyntaxKind.StringLiteralExpression,
Syntax.Literal (#"""Goodbye everyone!""", "Goodbye everyone!")))
}));
var consoleWriteLineStatement = Syntax.ExpressionStatement (Syntax.InvocationExpression (consoleWriteLine, arguments));
var voidType = Syntax.ParseTypeName ("void");
var method = Syntax.MethodDeclaration (voidType, "Method").WithBody (Syntax.Block(consoleWriteLineStatement));
var intType = Syntax.ParseTypeName ("int");
var getterBody = Syntax.ReturnStatement (Syntax.DefaultExpression (intType));
var getter = Syntax.AccessorDeclaration (SyntaxKind.GetAccessorDeclaration, Syntax.Block (getterBody));
var property = Syntax.PropertyDeclaration (intType, "Property").WithAccessorList (Syntax.AccessorList (Syntax.SingletonList (getter)));
var #class = Syntax.ClassDeclaration ("MyClass").WithMembers (Syntax.List (new MemberDeclarationSyntax[] { method, property }));
var cw = new CustomWorkspace();
cw.Options.WithChangedOption (CSharpFormattingOptions.IndentBraces, true);
var formattedCode = Formatter.Format (#class, cw);
Console.WriteLine (formattedCode.ToFullString());
Note: Syntax = Microsoft.CodeAnalysis.CSharp.SyntaxFactory
This generates the following class definiton:
class MyClass
{
void Method()
{
Console.WriteLine("Goodbye everyone!");
}
int Property
{
get
{
return default(int);
}
}
}
Seems fine.
I had this same problem and found CustomWorkspace is now called AdhocWorkspace.
var cw = new AdhocWorkspace();
cw.Options.WithChangedOption(CSharpFormattingOptions.IndentBraces, true);
var formatter = Formatter.Format(cu, cw);
StringBuilder sb = new StringBuilder();
using (StringWriter writer = new StringWriter(sb))
{
formatter.WriteTo(writer);
}
var code = sb.ToString();

Modify programatically csproj files with Microsoft.Build.Evaluation (instead of Engine)

I would like to read, modify and write back csproj files.
I've found this code, but unfortunately Engine class is depreciated.
Engine engine = new Engine()
Project project = new Project(engine);
project.Load("myproject.csproj");
project.SetProperty("SignAssembly", "true");
project.Save("myproject.csproj");
So I've continued based on the hint I should use Evaluation.ProjectCollection instead of Engine:
var collection = new ProjectCollection();
collection.DefaultToolsVersion = "4.0";
var project = new Project(collection);
// project.Load("myproject.csproj") There is NO Load method :-(
project.FullPath = "myproject.csproj"; // Instead of load? Does nothing...
// ... modify the project
project.Save(); // Interestingly there is a Save() method
There is no Load method anymore. I've tried to set the property FullPath, but the project still seems empty. Missed I something?
(Please note I do know that the .csproj file is a standard XML file with XSD schema and I know that we could read/write it by using XDocument or XmlDocument. That's a backup plan. Just seeing the .Save() method on the Project class I think I missed something if I can not load an existing .csproj. thx)
I've actually found the answer, hopefully will help others:
Instead of creating a new Project(...) and trying to .Load(...) it, we should use a factory method of the ProjectCollection class.
// Instead of:
// var project = new Project(collection);
// project.FullPath = "myproject.csproj"; // Instead of load? Does nothing...
// use this:
var project = collection.LoadProject("myproject.csproj")
Since i can't comment:
This won't work in .net core without first setting the MSBuild.exe path variable. The code to do so can be found here
https://blog.rsuter.com/missing-sdk-when-using-the-microsoft-build-package-in-net-core/
and is written here
private static void SetMsBuildExePath()
{
try
{
var startInfo = new ProcessStartInfo("dotnet", "--list-sdks")
{
RedirectStandardOutput = true
};
var process = Process.Start(startInfo);
process.WaitForExit(1000);
var output = process.StandardOutput.ReadToEnd();
var sdkPaths = Regex.Matches(output, "([0-9]+.[0-9]+.[0-9]+) \\[(.*)\\]")
.OfType<Match>()
.Select(m => System.IO.Path.Combine(m.Groups[2].Value, m.Groups[1].Value, "MSBuild.dll"));
var sdkPath = sdkPaths.Last();
Environment.SetEnvironmentVariable("MSBUILD_EXE_PATH", sdkPath);
}
catch (Exception exception)
{
Console.Write("Could not set MSBUILD_EXE_PATH: " + exception);
}
}

Categories

Resources