DLR: Do i really need code generation here? - c#

spent some time again with the scripting interface of my app.
i guess i now have an advanced dlr problem here.
I have a python script
I have an .NET object [o1]
I call a method on the python script from .NET via Iron Python.
The python code creates an object [o2]
The python code calls an method on object [o1] passing [o2] as an argument (Via subclassing DynamicMetaObject)
In the .NET code of the o1-method i want to dynamically call methods on o2
For example i could do that via
((dynamic)o2).FuncInPythonScript
so far so good thats all working.
.NET calls Python (step 3)
Python calls back .NET (step 5)
So i have a basic biderectional control flow between .NET and Python.
We go further:
In the [o1]-method I use LanguageContext.GetMemberNames on [o2]
I wanna call these members somehow via reflection or expressions.
Meaning i dont wanna use the dynamic keyword as in step 7.
Instead somehow call the methods via reflection.
Problem is:
a) I do not know how to get the RuntimeType of the Python-Type, meaning i have no System.Reflection.MethodInfo so i stuck here
b) I try to use LanguageContext.CreateCallBinder and MetaObject.BindInvokeMember so i should have the method 'FuncInPythonScript' bound
But then i'm stuck in how to finally call the bound method.
I see i could use code generation to just generate the code as in step 7, just with the member names from step 8.
But is that really necessary?
I do not see wether approach a) or b) might work or maybe there is somthing i did not think of.
Please do not answer with basic "How do i invoke a python method from .NET" hints.
That is done in steps 1-7 and i have no problem doing this. It's really an advanced problem.
namespace DynamicMetaObjectTest
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Dynamic;
using System.Linq.Expressions;
using Microsoft.Scripting.Hosting;
using Microsoft.Scripting.Hosting.Providers;
class Program
{
internal sealed class CDotNetObject : IDynamicMetaObjectProvider
{
DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression aExp)
{
return new CInvoker(this, aExp);
}
private sealed class CInvoker : DynamicMetaObject
{
internal CInvoker(CDotNetObject aGws, Expression aExp) : base(aExp, BindingRestrictions.Empty, aGws)
{
this.DotNetObject = aGws;
}
private readonly CDotNetObject DotNetObject;
public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
{
var aMethodInfo = this.GetType().GetMethod("GetSetResultDelegate");
var aExp = Expression.Call(Expression.Constant(this), aMethodInfo);
var aRestrictions = BindingRestrictions.GetTypeRestriction(this.Expression, this.LimitType);
var aMetaObject = new DynamicMetaObject(aExp, aRestrictions);
return aMetaObject;
}
public Action<object> GetSetResultDelegate()
{
return this.DotNetObject.SetResultProvider;
}
}
public void SetResultProvider(object aPythonObject_O2)
{
var aResult = ((dynamic)aPythonObject_O2).GetResult(); // this is for noobs. ;-)
var aMetaObjectProvider = (IDynamicMetaObjectProvider)aPythonObject_O2;
var aMetaObject = aMetaObjectProvider.GetMetaObject(Expression.Constant(aPythonObject_O2));
var aLanguageContext = HostingHelpers.GetLanguageContext(gScriptEngine);
var aMemberNames = aLanguageContext.GetMemberNames(aPythonObject_O2);
var aNonSystemMembers = from aMemberName in aMemberNames where !aMemberName.StartsWith("__") select aMemberName;
foreach (var aMemberName in aNonSystemMembers)
{
Console.WriteLine("Getting function result from Python script: " + aMemberName);
// Now problem:
// P1) How to determine wether its an function or an member variable?
// P2) How to invoke the method respectively get the value of the member variable?
// Your turn ;-)
// some of my failures:
{ // does not work:
//var aVar1Binder = aLanguageContext.CreateGetMemberBinder("GetVar1", false);
//var aVar1Bound = aMetaObject.BindGetMember(aVar1Binder);
//var aCallInfo = new CallInfo(0 , new string[]{});
//var aInvokeBinder = aLanguageContext.CreateCallBinder("GetVar1", false, aCallInfo);
//var aInvokeBound = aMetaObject.BindInvokeMember(aInvokeBinder, new DynamicMetaObject[]{ aVar1Bound});
////var aInvokeExp = Expression.Invoke(Expression.Constant(aInvokeBound), new Expression[] { });
}
{ // does not work
//var aExpandable = (IronPython.Runtime.Binding.IPythonExpandable)aMetaObject;
}
}
}
}
static ScriptEngine gScriptEngine;
static void Main(string[] args)
{
var aScriptRuntime = IronPython.Hosting.Python.CreateRuntime();
// That's the python script from step 1:
var aCode = "class CustomView(object) :" + Environment.NewLine +
"\tdef GetResult(self) :" + Environment.NewLine +
"\t\treturn 42;" + Environment.NewLine + // cuz 42 is the answer to everything ;-)
"DotNetObject.SetResultProvider(CustomView())";
var aEngine = aScriptRuntime.GetEngine("py");
gScriptEngine = aEngine;
var aScope = aEngine.CreateScope();
var aDotNetObject = new CDotNetObject();
aScope.SetVariable("DotNetObject", aDotNetObject);
// That's the invoke to pything from step 3:
aEngine.Execute(aCode, aScope);
}
}
}

Related

Parsing Json information from a Json file as a string

I am looking for some help with regards to Parsing the the value "mppdemo" in the below json file (See screenshot)
{
"client":{
"isWebLogin":false,
"registryName": "mpdemo",
"walletCode": "Local"
}
}
I have done some research in and arround the webs but alot of the examples wither are out dated or dont work.
This is what i have tried
//JObject T = JObject.Parse(File.ReadAllText(DownloadConfigFilelocation));
var source = File.ReadAllText(DownloadConfigFilelocation);
var JavaScriptSerializer MySerializer = new JavaScriptSerializer();
var myObj = MySerializer.Deserialize<T>(source);
var RegistryName = myObj.searchResults[0].hotelID;
MessageBox.Show(RegistryName);
The above doesnt pick up the JavaScriptSerializer function from the library even though im using the using System.Web.Script.Serialization;
Can someone help me get this code segment to work
I hope i have provided enough info
EDIT: I just realized that you're having another problem - that your compiler does not recognize the System.Web.Script.Serialization.JavaScriptSerializer type. You'll need to add a reference to System.Web.Extensions.dll to your project. I don't know what IDE you are using, but for example in SharpDevelop you can right click References > Add Reference > in filter start typing "System.Web.Extensions" > among results find "System.Web.Extensions" and double click it (it will be moved to lower window) > hit OK and compile your project.
If you still want to use System.Web.Script.Serialization.JavaScriptSerializer, I'd probably do it like this:
using System;
using System.Text.RegularExpressions;
using System.Web.Script.Serialization;
namespace jsonhratky
{
public static class Program {
public static void Main(string[] args)
{
var instance = new JsonParsingTest();
}
}
public class JsonParsingTest
{
class Response {
public Client client;
}
class Client {
public bool isWebLogin;
public string registryName;
public string walletCode;
}
const string JSON_EXAMPLE = ("{" + ("\"client\":{" + ("\"isWebLogin\":false," + ("\"registryName\": \"mpdemo\"," + ("\"walletCode\": \"Local\"" + ("}" + "}"))))));
public JsonParsingTest() {
// Solution #1 using JavaScriptSerializer
var serializer = new JavaScriptSerializer();
Response parsed = serializer.Deserialize<Response>(JSON_EXAMPLE);
Console.WriteLine("parsed isWebLogin: " + parsed.client.isWebLogin);
Console.WriteLine("parsed registryName: " + parsed.client.registryName);
Console.WriteLine("parsed walletCode: " + parsed.client.walletCode);
// Solution #2 (not recommended)
var matches = Regex.Match(JSON_EXAMPLE, "registryName\":.*?\"([^\"]+)\"", RegexOptions.Multiline);
if (matches.Success) {
Console.WriteLine("registryName parsed using Regex: " + matches.Groups[1].Value);
} else {
Console.WriteLine("Solution using Regex failed.");
}
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
}
}
You need to create a "POJO" class (there's probably another term in C# for plain old classes) with fields matching those in your string response. Since your fields isWebLogin, registryName and walletCode are not directly part of main object (Response) but they belong to sub-class (Client), you need two classes: Response (or call it whatever you want) and then the field "client" must match string in response (as well as the fields of the sub-class).
Result:
Anyway, I also included a solution using Regex, but I absolutely don't recommend that. It's suitable only as a workaround and only then if you know that your response will never contain more than one "client" objects.
The problem seems to be in this line of your code var myObj = MySerializer.Deserialize<T>(source); You need to give the type of object instead of T.

C# - Convert a string (which contains method or function) to an actual function or method in C#

I have a string. For example
string str="if(a>b) {return a;} else {return b;}"
I want to evaluate or make function, say func(int a, int b) which will have the code of 'str'.
you may need to use CSharpCodeProvider as in this answer
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
class Program
{
static void Main(string[] args)
{
var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } });
var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, "foo.exe", true);
parameters.GenerateExecutable = true;
CompilerResults results = csc.CompileAssemblyFromSource(parameters,
#"using System.Linq;
class Program {
public static void Main(string[] args) {
var q = from i in Enumerable.Range(1,100)
where i % 2 == 0
select i;
}
}");
results.Errors.Cast<CompilerError>().ToList().ForEach(error => Console.WriteLine(error.ErrorText));
}
}
In general, this is not an easy thing to do, but the System.CodeDom namespace is where your journey will start.
Look at the following CodeProject article on the matter as a start: http://www.codeproject.com/Articles/26312/Dynamic-Code-Integration-with-CodeDom
The basics of it is as follows (as taken from the codeproject article):
private static Assembly CompileSource( string sourceCode )
{
CodeDomProvider cpd = new CSharpCodeProvider();
CompilerParameters cp = new CompilerParameters();
cp.ReferencedAssemblies.Add("System.dll");
//cp.ReferencedAssemblies.Add("ClassLibrary1.dll");
cp.GenerateExecutable = false;
// Invoke compilation.
CompilerResults cr = cpd.CompileAssemblyFromSource(cp, sourceCode);
return cr.CompiledAssembly;
}
The resultant assembly will have the class/method/code you are interested in, and then you can use reflection to call your method. Since your example just uses a code fragment, you will probably have to wrap it in a class/method before passing it to this method.
I hope that helps, but dynamic code generation in C# is not easy and this is just a start.

Embedding Python in C# via resources

I have been working on a problem for a while now which I cannot seem to resolve so I need some help! The problem is that I am writing a program in C# but I require a function from a Python file I created. This in itself is no problem:
...Usual Stuff
using IronPython.Hosting;
using IronPython.Runtime;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;
namespace Program
{
public partial class Form1 : Form
{
Microsoft.Scripting.Hosting.ScriptEngine py;
Microsoft.Scripting.Hosting.ScriptScope s;
public Form1()
{
InitializeComponent();
py = Python.CreateEngine(); // allow us to run ironpython programs
s = py.CreateScope(); // you need this to get the variables
}
private void doPython()
{
//Step 1:
//Creating a new script runtime
var ironPythonRuntime = Python.CreateRuntime();
//Step 2:
//Load the Iron Python file/script into the memory
//Should be resolve at runtime
dynamic loadIPython = ironPythonRuntime.;
//Step 3:
//Invoke the method and print the result
double n = loadIPython.add(100, 200);
numericUpDown1.Value = (decimal)n;
}
}
}
However, this requires for the file 'first.py' to be wherever the program is once compiled. So if I wanted to share my program I would have to send both the executable and the python files which is very inconvenient. One way I thought to resolve this is by adding the 'first.py' file to the resources and running from there... but I don't know how to do this or even if it is possible.
Naturally the above code will not work for this as .UseFile method takes string arguments not byte[]. Does anyone know how I may progress?
Lets start with the simplest thing that could possibly work, you've got some code that looks a little like the following:
// ...
py = Python.CreateEngine(); // allow us to run ironpython programs
s = py.CreateScope(); // you need this to get the variables
var ironPythonRuntime = Python.CreateRuntime();
var x = py.CreateScriptSourceFromFile("SomeCode.py");
x.Execute(s);
var myFoo = s.GetVariable("myFoo");
var n = (double)myFoo.add(100, 200);
// ...
and we'd like to replace the line var x = py.CreateScriptSourceFromFile(... with something else; If we could get the embedded resource as a string, we could use ScriptingEngine.CreateScriptSourceFromString().
Cribbing this fine answer, we can get something that looks a bit like this:
string pySrc;
var resourceName = "ConsoleApplication1.SomeCode.py";
using (var stream = System.Reflection.Assembly.GetExecutingAssembly()
.GetManifestResourceStream(resourceName))
using (var reader = new System.IO.StreamReader(stream))
{
pySrc = reader.ReadToEnd();
}
var x = py.CreateScriptSourceFromString(pySrc);

Read the value of a string in the dll that is created dynamicly

I am trying to create a DLL file in runtime ,as a matter of fact i need to save an encoded data to DLL .My code is like this :
class DllFile
{
public static void CreateDllFile(string source)
{
source = #"using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LicensecodeDll
{
class Check
{
public string returnValue()
{
return " + source + ";" + "}}}";
var provider = new CSharpCodeProvider();
var options = new CompilerParameters
{
OutputAssembly = "test.dll"
};
var results = provider.CompileAssemblyFromSource(options, new[] { source });
}
}
}
every thing works fine and my ddl is created ,but i need to read the value that is saved on the dll file ,i mean i need the returnValue.how can i do that?
Best regards.Any ideas will be appreciated.
You can load the assembly dynamically and use reflection to call that method. The code should be like this.
Assembly a = Assembly.Load("test.dll");
Type myType = a.GetType("LicensecodeDll.Check");
MethodInfo myMethod = myType.GetMethod("returnValue");
object obj = Activator.CreateInstance(myType);
myMethod.Invoke(obj, null);
More detail is on MSDN: How to: Load Assemblies into an Application Domain

What's the difference between Marshal.GenerateGuidForType(Type) and Type.GUID?

Type classType = typeof(SomeClass);
bool equal = Marshal.GenerateGuidForType(classType) == classType.GUID;
I haven't found a case that fail this condition.
So why and when should I use the Marshal method instead of simply getting the GUID property?
see http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.generateguidfortype.aspx
... GenerateGuidForType provides the same functionality as the Type.GUID property.
So according to documentation they are the same. However, Marshal.GenerateGuidForType works only for RuntimeType objects, while Type.GUID is provided for some other Type implementations as well.
E.g.:
using System;
using System.CodeDom;
using System.Runtime.InteropServices;
using System.Workflow.ComponentModel.Compiler;
namespace Samples
{
class Program
{
static CodeCompileUnit BuildHelloWorldGraph()
{
var compileUnit = new CodeCompileUnit();
var samples = new CodeNamespace("Samples");
compileUnit.Namespaces.Add(samples);
var class1 = new CodeTypeDeclaration("Class1");
samples.Types.Add(class1);
return compileUnit;
}
static void Main(string[] args)
{
var unit = BuildHelloWorldGraph();
var typeProvider = new TypeProvider(null);
typeProvider.AddCodeCompileUnit(unit);
var t = typeProvider.GetType("Samples.Class1");
Console.WriteLine(t.GUID); // prints GUID for design time type instance.
Console.WriteLine(Marshal.GenerateGuidForType(t)); // throws ArgumentException.
}
}
}
According to MSDN, "GenerateGuidForType provides the same functionality as the Type.GUID property". It should be safe to use the one that suits you the best.

Categories

Resources