I have the following IronPython code.
class Hello:
def __init__(self):
pass
def add(self, x, y):
return (x+y)
I could make the following C# code to use the IronPython code.
static void Main()
{
string source = GetSourceCode("ipyth.py");
Engine engine = new Engine(source);
ObjectOperations ops = engine._engine.Operations;
bool result = engine.Execute();
if (!result)
{
Console.WriteLine("Executing Python code failed!");
}
else
{
object klass = engine._scope.GetVariable("Hello");
object instance = ops.Invoke(klass);
object method = ops.GetMember(instance, "add");
int res = (int) ops.Invoke(method, 10, 20);
Console.WriteLine(res);
}
Console.WriteLine("Press any key to exit.");
Console.ReadLine();
}
Can I make this code simpler with dynamic DLR?
The IronPython In Action book has the simple explanation about it at <15.4.4 The future of interacting with dynamic objects>, but I couldn't find some examples.
ADDED
I attach the source/batch file for the program.
Program.cs
runme.bat
Yes, dynamic can make your code simplier
var source =
#"
class Hello:
def __init__(self):
pass
def add(self, x, y):
return (x+y)
";
var engine = Python.CreateEngine();
var scope = engine.CreateScope();
var ops = engine.Operations;
engine.Execute(source, scope);
var pythonType = scope.GetVariable("Hello");
dynamic instance = ops.CreateInstance(pythonType);
var value = instance.add(10, 20);
Console.WriteLine(value);
Console.WriteLine("Press any key to exit.");
Console.ReadLine();
Related
I would like to use the Microsoft.Extensions.Caching.Memory.MemoryCache in a .NET Core 2.0 console app (Actually, in a library that is either used in a console or in a asp.net app)
I've created a test app:
using System;
namespace ConsoleTest
{
class Program
{
static void Main(string[] args)
{
var cache = new Microsoft.Extensions.Caching.Memory.MemoryCache(new Microsoft.Extensions.Caching.Memory.MemoryCacheOptions());
int count = cache.Count;
cache.CreateEntry("item1").Value = 1;
int count2 = cache.Count;
cache.TryGetValue("item1", out object item1);
int count3 = cache.Count;
cache.TryGetValue("item2", out object item2);
int count4 = cache.Count;
Console.WriteLine("Hello World!");
}
}
}
Unfortunately, this is not working. The items are not added to the cache and they can not be retrieved.
I suspect I need to use DependencyInjection, doing something like this:
using System;
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleTest
{
class Program
{
static void Main(string[] args)
{
var provider = new Microsoft.Extensions.DependencyInjection.ServiceCollection()
.AddMemoryCache()
.BuildServiceProvider();
//And now?
var cache = new Microsoft.Extensions.Caching.Memory.MemoryCache(new Microsoft.Extensions.Caching.Memory.MemoryCacheOptions());
var xxx = PSP.Helpers.DependencyInjection.ServiceProvider;
int count = cache.Count;
cache.CreateEntry("item1").Value = 1;
int count2 = cache.Count;
cache.TryGetValue("item1", out object item1);
int count3 = cache.Count;
cache.TryGetValue("item2", out object item2);
int count4 = cache.Count;
Console.WriteLine("Hello World!");
}
}
}
Unfortunately, this is also not working, I suspect I shouldn't create a new memory cache, but get it from the service provider, but haven't been able to do that.
Any ideas?
After configuring the provider retrieve the cache via the GetService extension method
var provider = new ServiceCollection()
.AddMemoryCache()
.BuildServiceProvider();
//And now?
var cache = provider.GetService<IMemoryCache>();
//...other code removed for brevity;
From comments:
It's not needed to use dependency injection, the only thing needed was disposing the return value of CreateEntry().
The entry returned by CreateEntry needs to be disposed. On
dispose, it is added to the cache:
using (var entry = cache.CreateEntry("item2")) {
entry.Value = 2;
entry.AbsoluteExpiration = DateTime.UtcNow.AddDays(1);
}
IMemoryCache cache = new MemoryCache(new MemoryCacheOptions());
object result = cache.Set("Key", new object());
bool found = cache.TryGetValue("Key", out result);
See full Memory Cache Sample in GitHub.
You need to add NuGet Microsoft.Extensions.Caching.Memory packages in your project for use MemoryCache
Here is the complete console application code in .NET Core
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Primitives;
using System;
using System.Threading;
namespace InMemoryNetCore
{
class Program
{
static void Main(string[] args)
{
IMemoryCache cache = new MemoryCache(new MemoryCacheOptions());
object result;
string key = "KeyName";
// Create / Overwrite
result = cache.Set(key, "Testing 1");
result = cache.Set(key, "Update 1");
// Retrieve, null if not found
result = cache.Get(key);
Console.WriteLine("Output of KeyName Value="+result);
// Check if Exists
bool found = cache.TryGetValue(key, out result);
Console.WriteLine("KeyName Found=" + result);
// Delete item
cache.Remove(key);
//set item with token expiration and callback
TimeSpan expirationMinutes = System.TimeSpan.FromSeconds(0.1);
var expirationTime = DateTime.Now.Add(expirationMinutes);
var expirationToken = new CancellationChangeToken(
new CancellationTokenSource(TimeSpan.FromMinutes(0.001)).Token);
// Create cache item which executes call back function
var cacheEntryOptions = new MemoryCacheEntryOptions()
// Pin to cache.
.SetPriority(Microsoft.Extensions.Caching.Memory.CacheItemPriority.Normal)
// Set the actual expiration time
.SetAbsoluteExpiration(expirationTime)
// Force eviction to run
.AddExpirationToken(expirationToken)
// Add eviction callback
.RegisterPostEvictionCallback(callback: CacheItemRemoved);
//add cache Item with options of callback
result = cache.Set(key,"Call back cache Item", cacheEntryOptions);
Console.WriteLine(result);
Console.ReadKey();
}
private static void CacheItemRemoved(object key, object value, EvictionReason reason, object state)
{
Console.WriteLine(key + " " + value + " removed from cache due to:" + reason);
}
}
}
Source : In Memory cache C# (Explanation with example in .NET and .NET Core)
To the responses above, I wanted to add some concise alternatives for common cache operations:
using Microsoft.Extensions.Caching.Memory;
// ... (further down) ...
MemoryCache cache = new MemoryCache(new MemoryCacheOptions() );
// get a value from the cache
// both are equivalent
// obviously, replace "string" with the correct type
string value = (string)cache.Get("mykey");
string value = cache.Get<string>("mykey");
// setting values in the cache
// no expiration time
cache.Set("mykey", myVar);
// absolute expiration numMinutes from now
cache.Set("mykey", myVar, DateTimeOffset.Now.AddMinutes(numMinutes));
// sliding expiration numMinutes from now
// "sliding expiration" means that if it's accessed within the time period,
// the expiration is extended
MemoryCacheEntryOptions options = new MemoryCacheEntryOptions();
options.SetSlidingExpiration(TimeSpan.FromMinutes(numMinutes));
webcache.Set("mykey", myVar, options);
// or, if you want to do it all in one statement:
webcache.Set("mykey", myVar,
new MemoryCacheEntryOptions {SlidingExpiration = TimeSpan.FromMinutes(numMinutes)});
In the context of an AutomaticTest for a Asp net core controller I allocate an instance of MemoryCache in the test preparation Setup like below:
TestCaseController _sut;
long? NO_SIZE_LIMIT = null;
[SetUp]
public void Setup()
{
var options = new MemoryCacheOptions()
{
Clock = new SystemClock(),
CompactionPercentage = 1,
ExpirationScanFrequency = TimeSpan.FromSeconds(100),
SizeLimit = NO_SIZE_LIMIT
};
IOptions<MemoryCacheOptions> optionsAccessor = Options.Create(options);
IMemoryCache memoryCache= new MemoryCache(optionsAccessor);
_sut = new TestCaseController(memoryCache);
}
The Target Controller then use the cache like this:
[HttpGet("Active")]
public IEnumerable<TestCase> GetActive()
{
LogRequest();
IEnumerable<TestCase> ret;
var cacheKey = "TestCaseActive";
if (!_memoryCache.TryGetValue(cacheKey, out ret))
{
ret = _dbRepo.GetActive();
var cacheExpiryOptions = new MemoryCacheEntryOptions
{
AbsoluteExpiration = DateTime.Now.AddSeconds(60),
Priority = CacheItemPriority.High,
SlidingExpiration = TimeSpan.FromSeconds(50)
};
_memoryCache.Set(cacheKey, ret, cacheExpiryOptions);
}
else
{
_log.Debug("Fetch Cache");
}
_log.Debug(ret.Count() + " items returned");
return ret;
}
Has anybody ever had an issue where the SessionModSvcContractClient Logout function throws an Exception: Additional information:
Object reference not set to an instance of an object.
When I tried LogoutAsync and Close methods they worked fine.
Can anybody help me figure out why that's happening or the difference between the 3.
I'm basically trying to use create the test from the WCF guide
static void Main(string[] args)
{
//use a self-signed certificate in IIS, be sure to include the following code. This code speeds up calls to the services and prevents the method from trying to validate the certificate with the known authorities.
ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, errors) => { return true; };
//You can toggle the value assigned to this variable to test the two bindings: SOAPHttp or BasicHttp
EndpointBindingType bindingType = EndpointBindingType.SOAPHttp;
//Epicor credentials:
string epicorUserID = "XXX";
string epiorUserPassword = "XXX";
string scheme = "http";
if (bindingType == EndpointBindingType.BasicHttp)
{
scheme = "https";
}
UriBuilder builder = new UriBuilder(scheme, "localhost");
string webServicesLink = "XXX/";
builder.Path = webServicesLink + "Ice/Lib/SessionMod.svc";
SessionModSvcContractClient sessionModClient = GetClient < SessionModSvcContractClient, SessionModSvcContract > (builder.Uri.ToString(), epicorUserID, epiorUserPassword, bindingType);
builder.Path = webServicesLink + "Erp/BO/AbcCode.svc";
ABCCodeSvcContractClient abcCodeClient = GetClient<ABCCodeSvcContractClient, ABCCodeSvcContract>(builder.Uri.ToString(), epicorUserID, epiorUserPassword, bindingType);
Guid sessionId = Guid.Empty;
sessionId = sessionModClient.Login();
//Create a new instance of the SessionModSvc. Do this because when you call any method on the service
//client class, you cannot modify its Endpointbehaviors.
builder.Path = webServicesLink + "Ice/Lib/SessionMod.svc";
sessionModClient = GetClient < SessionModSvcContractClient, SessionModSvcContract > (builder.Uri.ToString(),epicorUserID,epiorUserPassword,bindingType);
sessionModClient.Endpoint.EndpointBehaviors.Add(new HookServiceBehavior(sessionId, epicorUserID));
abcCodeClient.Endpoint.EndpointBehaviors.Add(new HookServiceBehavior(sessionId, epicorUserID));
var ts = new ABCCodeTableset();
abcCodeClient.GetNewABCCode(ref ts);
var newRow = ts.ABCCode.Where(n => n.RowMod.Equals("A", StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
if (newRow != null)
{
newRow.ABCCode = "G";
newRow.CountFreq = 30;
newRow.StockValPcnt = 100;
abcCodeClient.Update(ref ts);
ts = null;
ts = abcCodeClient.GetByID("G");
if (ts != null && ts.ABCCode.Any())
{
ABCCodeRow backupRow = new ABCCodeRow();
var fields = backupRow.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var field in fields)
{
if (field.PropertyType == typeof(System.Runtime.Serialization.ExtensionDataObject))
{
continue;
}
var fieldValue = field.GetValue(ts.ABCCode[0]);
field.SetValue(backupRow, fieldValue);
}
ts.ABCCode.Add(backupRow);
ts.ABCCode[0].CountFreq = 45;
ts.ABCCode[0].RowMod = "U";
abcCodeClient.Update(ref ts);
ts = null;
ts = abcCodeClient.GetByID("G");
if (ts != null && ts.ABCCode.Any())
{
Console.WriteLine("CountFreq = {0}", ts.ABCCode[0].CountFreq);
ts.ABCCode[0].RowMod = "D";
abcCodeClient.Update(ref ts);
try
{
ts = abcCodeClient.GetByID("G");
}
catch (FaultException<Epicor.AbcCodeSvc.EpicorFaultDetail> ex)
{
if (ex.Detail.ExceptionKindValue.Equals("RecordNotFound", StringComparison.InvariantCultureIgnoreCase))
{
Console.WriteLine("Record deleted.");
}
else
{
Console.WriteLine(ex.Message);
}
}
}
}
}
if (sessionId != Guid.Empty)
{
sessionModClient.Logout();
}
Console.ReadLine();
}
Your code worked fine for me after I changed the config to suit my environment.
It looks like you followed the step 7 on page 15 of the Epicor10_techrefWCFServices_101400.pdf guide and correctly created a new instance of the SessionModSvc after Login(). However, if you copied the full code for the Main method from page 18 then this is missing and I can replicate your issue.
Check the code that you are compiling has created a new instance of the SessionModSvc after the call to .Login().
See my other answer, but as an alternative you may want to consider this method of accessing the services.
If you have access to the client DLLs then this code might be easier:
static void Main(string[] args)
{
// Hard-coded LogOn method
// Reference: Ice.Core.Session.dll
Ice.Core.Session session = new Ice.Core.Session("manager", "manager", "net.tcp://AppServer/MyCustomerAppserver-99999-10.0.700.2");
// References: Epicor.ServiceModel.dll, Erp.Contracts.BO.ABCCode.dll
var abcCodeBO = Ice.Lib.Framework.WCFServiceSupport.CreateImpl<Erp.Proxy.BO.ABCCodeImpl>(session, Erp.Proxy.BO.ABCCodeImpl.UriPath);
// Call the BO methods
var ds = abcCodeBO.GetByID("A");
var row = ds.ABCCode[0];
System.Console.WriteLine("CountFreq is {0}", row.CountFreq);
System.Console.ReadKey();
}
Just add references to:
Ice.Core.Session.dll
Epicor.ServiceModel.dll
Erp.Contracts.BO.ABCCode.dll
I have had success using this tutorial: http://www.codeproject.com/Tips/715891/Compiling-Csharp-Code-at-Runtime to set up a framework for runtime compilation and execution of C# code. Below is the code I currently have:
public static class CodeCompiler {
public static object InterpretString(string executable) {
string compilation_string =
#"
static class RuntimeCompilationCode {
public static void Main() {}
public static object Custom() {
/* CODE HERE */
}
}";
compilation_string = compilation_string.Replace("/* CODE HERE */", executable);
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters compiler_parameters = new CompilerParameters();
// True - memory generation, false - external file generation
compiler_parameters.GenerateInMemory = true;
// True - exe file generation, false - dll file generation
compiler_parameters.GenerateExecutable = true;
// Compile
CompilerResults results = provider.CompileAssemblyFromSource(compiler_parameters, compilation_string);
// Check errors
if (results.Errors.HasErrors) {
StringBuilder builder = new StringBuilder();
foreach (CompilerError error in results.Errors) {
builder.AppendLine(String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText));
}
throw new InvalidOperationException(builder.ToString());
}
// Execute
Assembly assembly = results.CompiledAssembly;
Type program = assembly.GetType("RuntimeCompilationCode");
MethodInfo execute = program.GetMethod("Custom");
return execute.Invoke(null, null);
}
}
I can pass a statement in the form of a string (ex. "return 2;") to InterpretString() and it will be compiled and executed as part of the Custom() function. However I am wondering if it is possible to use the same approach to execute a method that is in my original file. For instance, suppose the CodeCompiler class had another method returnsTwo() which returns the integer 2. Is there a way to call such a method by passing "CodeCompiler.returnsTwo();" or a similar string to InterpretString()?
Provided that the function is a static function this should not be a problem, as long as you add the appropriate reference to the compilation. I've done this short of thing on several projects.
If the CodeCompiler is in your current executable you have to include the references in this fashion:
string exePath = Assembly.GetExecutingAssembly().Location;
string exeDir = Path.GetDirectoryName(exePath);
AssemblyName[] assemRefs = Assembly.GetExecutingAssembly().GetReferencedAssemblies();
List<string> references = new List<string>();
foreach (AssemblyName assemblyName in assemRefs)
references.Add(assemblyName.Name + ".dll");
for (int i = 0; i < references.Count; i++)
{
string localName = Path.Combine(exeDir, references[i]);
if (File.Exists(localName))
references[i] = localName;
}
references.Add(exePath);
CompilerParameters compiler_parameters = new CompilerParameters(references.ToArray())
Currently I'm working on a custom importer for Ironpython, which should add an abstraction layer for writing custom importer. The abstraction layer is an IronPython module, which bases on PEP 302 and the IronPython zipimporter module. The architecture looks like this:
For testing my importer code, I've written a simple test package with modules, which looks like this:
/Math/
__init__.py
/MathImpl/
__init__.py
__Math2__.py
/Math/__init__.py:
print ('Import: /Math/__init__.py')
/Math/MathImpl/__init__.py:
# Sample math package
print ('Begin import /Math/MathImpl/__init__.py')
import Math2
print ('End import /Math/MathImpl/__init__.py: ' + str(Math2.add(1, 2)))
/Math/MathImpl/Math2.py:
# Add two values
def add(x, y):
return x + y
print ('Import Math2.py!')
If i try to import MathImpl like this in a script: import Math.MathImpl
My genericimporter get's called and searchs for some module/package in the find_module method. Which returns an instance of the importer if found, else not:
public object find_module(CodeContext/*!*/ context, string fullname, params object[] args)
{
// Set module
if (fullname.Contains("<module>"))
{
throw new Exception("Why, why does fullname contains <module>?");
}
// Find resolver
foreach (var resolver in Host.Resolver)
{
var res = resolver.GetModuleInformation(fullname);
// If this script could be resolved by some resolver
if (res != ResolvedType.None)
{
this.resolver = resolver;
return this;
}
}
return null;
}
If find_module is called the first time,fullname contains Math, which is ok, because Math should be imported first. The second time find_module is called, Math.MathImpl should be imported, the problem here is, that fullname has now the value <module>.MathImpl, instead of Math.MathImpl.
My idea was, that the module name (__name__) is not set correctly when Math was imported, but i set this in any case when importing the module in load_module:
public object load_module(CodeContext/*!*/ context, string fullname)
{
string code = null;
GenericModuleCodeType moduleType;
bool ispackage = false;
string modpath = null;
PythonModule mod;
PythonDictionary dict = null;
// Go through available import types by search-order
foreach (var order in _search_order)
{
string tempCode = this.resolver.GetScriptSource(fullname + order.Key);
if (tempCode != null)
{
moduleType = order.Value;
code = tempCode;
modpath = fullname + order.Key;
Console.WriteLine(" IMPORT: " + modpath);
if ((order.Value & GenericModuleCodeType.Package) == GenericModuleCodeType.Package)
{
ispackage = true;
}
break;
}
}
// of no code was loaded
if (code == null)
{
return null;
}
var scriptCode = context.ModuleContext.Context.CompileSourceCode
(
new SourceUnit(context.LanguageContext, new SourceStringContentProvider(code), modpath, SourceCodeKind.AutoDetect),
new IronPython.Compiler.PythonCompilerOptions() { },
ErrorSink.Default
);
// initialize module
mod = context.ModuleContext.Context.InitializeModule(modpath, context.ModuleContext, scriptCode, ModuleOptions.None);
dict = mod.Get__dict__();
// Set values before execute script
dict.Add("__name__", fullname);
dict.Add("__loader__", this);
dict.Add("__package__", null);
if (ispackage)
{
// Add path
string subname = GetSubName(fullname);
string fullpath = string.Format(fullname.Replace(".", "/"));
List pkgpath = PythonOps.MakeList(fullpath);
dict.Add("__path__", pkgpath);
}
else
{
StringBuilder packageName = new StringBuilder();
string[] packageParts = fullname.Split(new char[] { '/' });
for (int i = 0; i < packageParts.Length - 1; i++)
{
if (i > 0)
{
packageName.Append(".");
}
packageName.Append(packageParts[i]);
}
dict["__package__"] = packageName.ToString();
}
var scope = context.ModuleContext.GlobalScope;
scriptCode.Run(scope);
return mod;
}
I hope some one has an idea, why this happens. A few line which also may cause the problem are:
var scriptCode = context.ModuleContext.Context.CompileSourceCode
(
new SourceUnit(context.LanguageContext, new SourceStringContentProvider(code), modpath, SourceCodeKind.AutoDetect),
new IronPython.Compiler.PythonCompilerOptions() { },
ErrorSink.Default
);
and
mod = context.ModuleContext.Context.InitializeModule(modpath, context.ModuleContext, scriptCode, ModuleOptions.None);
Because i don't know, whether creating a module this way is completly correct.
The problem can be reproduced downloading this project/branch: https://github.com/simplicbe/Simplic.Dlr/tree/f_res_noid and starting Sample.ImportResolver. An exception in find_module will be raised.
Thank you all!
This problem is solved. Modpath what not allowed to contains /. In general only chars were allowed, which also can be in a file-name.
Maybe this is helpful for someone else...
I have followed the instructions here: https://github.com/sharwell/antlr4cs/wiki and here: http://www.antlr.org/wiki/display/ANTLR4/Getting+Started+with+ANTLR+v4
I have morphed my grammar from the opening example but it works with the Java tools (antlr4.bat and grun.bat) but not with my console app as configured by the sharwell wiki.
Java (inside a folder with only the Hello.g4 file):
antlr4 Hello.g4
javac *.java
grun Hello prog -gui
I then type "hello world" then Return then "^Z" then Return
The gui pops up an matches my grammar correctly
C# (only difference is the #parser and #lexer directives are not used in the Java version)
Hello.g4:
grammar MetaMeta;
#parser::members
{
protected const int EOF = Eof;
}
#lexer::members
{
protected const int EOF = Eof;
protected const int HIDDEN = Hidden;
}
prog : stmt NL* EOF;
stmt : hello eos;
hello : HELLO ID;
eos : ';' | NL;
HELLO : 'hello';
ID : [a-z]+;
NL : '\r'? '\n';
WS : [ \t]+ -> skip;
Program.cs:
private static void Main(string[] args)
{
(new Program()).Run();
}
public void Run()
{
var text = "hello world\n";
try
{
Console.WriteLine("START");
RunParser(text);
Console.Write("DONE. Hit RETURN to exit: ");
}
catch (Exception ex)
{
Console.WriteLine("ERROR: " + ex);
Console.Write("Hit RETURN to exit: ");
}
Console.ReadLine();
}
private void RunParser(string text)
{
var input = new AntlrInputStream(text);
var lexer = new MetaMetaLexer(input);
var tokens = new CommonTokenStream(lexer);
var parser = new MetaMetaParser(tokens);
var context = parser.prog();
var visitor = new MyVisitor();
visitor.VisitProg(context);
}
When running the program I get the following:
START
HelloVisitor VisitProg
Visit Symbol=<EOF>
DONE. Hit RETURN to exit:
My visitor is the same as the code example on the cs wiki. Thanks for any help. ;)
Scott
If you would have included your visitor code here, you would have received an answer much faster.
The visitor you are using does not override AbstractParseTreeVisitor<Result>.VisitTerminal(ITerminalNode). Therefore, the default implementation of this method is used each time a terminal node is reached, which simply returns DefaultResult() (which by default is just default(Result)).
The modified visitor code is as follows:
public class MyVisitor : AbstractParseTreeVisitor<object>
{
public override object VisitTerminal(ITerminalNode node)
{
var text = node.Symbol.Text;
if (text == "\n")
text = #"\n";
Console.WriteLine(" Visit Symbol={0}", text);
return base.VisitTerminal(node);
}
}
And the modified calling code is as follows:
private void RunParser(string text)
{
var input = new AntlrInputStream(text);
var lexer = new MetaMetaLexer(input);
var tokens = new CommonTokenStream(lexer);
var parser = new MetaMetaParser(tokens);
var context = parser.prog();
var visitor = new MyVisitor();
visitor.Visit(context); //changed to just Visit
}
I added this for completeness, but awarded the correct answer to Sam above.
Thanks,
Scott