Promise not completing when using Edge with Node - c#

I am working out how best to consume my C# dlls using Edgejs for Node.
One proxy function in Node looks like this (a class method in Typescript):
readSettings(args: ReadSettingsParams) : Promise<response> {
let $engine = this;
var proxy = edge.func({
assemblyFile: "C:\\Development\\IAStash\\GLReport\\GLReport\\bin\\x64\\Debug\\GLReportEngine.dll",
typeName: "GLReportEngine.edgeGLReport",
methodName: "readSettings"
});
return new Promise<response>(function (resolve, reject) {
args.instance = $engine.instance;
proxy(args, function(error, result) {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
}
When my complex task in c# is synchronous, everything works as expected, but when I move the core of the c# function into a task:
return await Task.Run<object>( () => { do some stuff });
When I use the above pattern, the resolve(result) line is hit, the result is correct in the watch window, but any .then or Q.all structures that are composed do not respond to resolve(result) having been executed.
I have found that if I console.log("x"); before the proxy callback returns, then my composed .then and Q.all structures fire as expected. i.e. this version works:
readSettings(args: ReadSettingsParams) : Promise<response> {
let $engine = this;
var proxy = edge.func({
assemblyFile: "C:\\Development\\IAStash\\GLReport\\GLReport\\bin\\x64\\Debug\\GLReportEngine.dll",
typeName: "GLReportEngine.edgeGLReport",
methodName: "readSettings"
});
return new Promise<response>(function (resolve, reject) {
args.instance = $engine.instance;
proxy(args, function(error, result) {
console.log("GLReportProxy.readSettings proxy returns, Err = " + (!!error));
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
}
My c# routine in this case reads an xml file, deserializes it and returns it:
public Task<object> readSettings(object args)
{
if (args != null && typeof(System.Dynamic.ExpandoObject).IsAssignableFrom(args.GetType()))
{
var argdict = (IDictionary<string, object>)args;
if (argdict.ContainsKey("report"))
{
reportsettingsfile = argdict["report"].ToString();
}
return Task.Run<object>(
() => {
if (File.Exists(reportsettingsfile))
{
var xser = new XmlSerializer(typeof(ReportSettings.report));
string settingstext = File.ReadAllText(reportsettingsfile);
using (var tre = new StringReader(settingstext))
{
reportSettings = (ReportSettings.report)xser.Deserialize(tre);
}
return new { result = true, model = reportSettings };
}
else
{
return new { result = false, error = "File not found" };
}
});
} else
{
return Task.FromResult<object>(new { result = false, error = "Expected (input) object but can't read it." });
}
}
This is fairly simple. I figure that I've got problems in my use of async/await, that causes problems in Node. To be fair though, I had expected promises to be robust. There could be issues with my use of Typescript, Node, Edge, Promises, Q.
If anybody knows what's happening, and why logging to the console fixes the problem. I'd appreciate any help!
Mark

I found that this is an issue when there is a http.listener and a c# edge proxy callback in the same process. There could be other combinations, but some interplay between libraries is the root cause.
Since this issue is unresolved according to edgejs, the console.log method is a good workaround, particularly if you don't mind writing some helpful message to the log window.
console.log is a good workaround, but I wanted something silent. I notice that console internally has _stdout. If you execute console._stdout.write(''); you get a silent unlock. in Typescript you have to do (console as any)._stdout.write('');.
I do this on entry to the edge proxy callback. e.g.
readSettings(args: ReadSettingsParams) : Promise<response> {
let $engine = this;
var proxy = edge.func({
assemblyFile: "C:\\Development\\IAStash\\GLReport\\GLReport\\bin\\x64\\Debug\\GLReportEngine.dll",
typeName: "GLReportEngine.edgeGLReport",
methodName: "readSettings"
});
return new Promise<response>(function (resolve, reject) {
args.instance = $engine.instance;
proxy(args, function(error, result) {
(console as any)._stdout.write('');
//console.log("GLReportProxy.readSettings proxy returns, Err = " + (!!error));
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
}
Clearly you could log something as indicated in the comment.

Related

CefSharp Javascript async response too fast

My approach is to visit each link and if all are visited to receive a return value from.
The problem is, when I start the code, I get instantly a response, clearly empty because not all the links are visited.
private async void ibtn_start_visiting_Click(object sender, EventArgs e)
{
string js = "var ele = document.querySelectorAll('#profiles * .tile__link');document.getElementsByClassName('js-scrollable')[0].scrollBy(0,30);ele.forEach(function(value,index){setTimeout(function(){if(index < ele.length-1){ele[index].click();}else{document.querySelectorAll('.search-results__item').forEach(e => e.parentNode.removeChild(e));document.getElementsByClassName('js-close-spotlight')[0].click();return 'hallo';}},1000 * index)})";
await browser.EvaluateScriptAsync(js).ContinueWith(x =>
{
var response = x.Result;
if (response.Success)
{
this.Invoke((MethodInvoker)delegate
{
var res = (string)response.Result;
Console.WriteLine("Response: " + res);
});
}
else {
Console.WriteLine("NO");
}
});
}
This is the javascript:
var ele = document.querySelectorAll('#profiles * .tile__link');
document.getElementsByClassName('js-scrollable')[0].scrollBy(0,30);
ele.forEach(function(value,index){
setTimeout(function(){
if(index < ele.length-1){
ele[index].click();
}
else{
document.querySelectorAll('.search-results__item').forEach(e => e.parentNode.removeChild(e));
document.getElementsByClassName('js-close-spotlight')[0].click();
alert('hallo');
}
},1500 * index)
})
Oh, I see. Your javascript is using setTimeout, which is kind of equivalent to making the function you pass to it also be async. CefSharp doesn't know when those setTimeout tasks are completed, hence the early return. The pended javascript code does execute, eventually. To know when that's been completed you've got a couple of options:
Make your async javascript code synchronous by getting rid of setTimeout completely.
Set some global variable in your async javascript code and periodically check your webpage in C# to see if that variable is set.
Register some JS handler and call that when your async javascript is completed.
#3 is my favorite, so you might register that handler in C# like so:
public class CallbackObjectForJs{
public void showMessage(string msg){
// we did it!
}
}
webView.RegisterJsObject("callbackObj", new CallbackObjectForJs());
And your JS might look something like:
var totalTasks = 0;
function beginTask() {
totalTasks++;
}
function completeTask() {
totalTasks--;
if (totalTasks === 0) {
callbackObj("we finished!"); // this function was registered via C#
}
}
var ele = document.querySelectorAll('#profiles * .tile__link');
document.getElementsByClassName('js-scrollable')[0].scrollBy(0,30);
ele.forEach(function(value,index){
beginTask(); // NEW
setTimeout(function(){
... // work
completeTask();
}, 1500 * index);
})
To make this cleaner you may want to look into Javascript's Promise.all().

How to break formflow in every moment i type exit or cancel?

I'm creating a chatbot in .Net C# using BotFramework. In one of my dialog when i start to fill a form flow i cannot exit from flowform till in the moment i will fill all the flow . Exist any possibility to exit and to leave form ?
This is my code :
LuisDialog.cs :
[LuisIntent("balance")]
public async Task balance(IDialogContext context, LuisResult result)
{
var balanca = new FormDialog<BalanceForm>(
new BalanceForm(),
BalanceForm.BuildForm,
FormOptions.PromptInStart,
result.Entities);
context.Call<BalanceForm>(balanca, BalanceCompleted);
BalanceForm.cs
namespace BasicMultiDialog
{
[Serializable]
public class BalanceForm
{
[Prompt("What is your contract number?")]
public string contract;
public static IForm<BalanceForm> BuildForm()
{
OnCompletionAsyncDelegate<BalanceForm> wrapUpRequest = async
(context, state) =>
{
string wrapUpMessage = "Dear " + house.Firstname + "," + "your balance is " + house.Balance;
await context.PostAsync(wrapUpMessage);
}
};
return new FormBuilder<BalanceForm>().Message
("We have to ask you some information")
.Field(nameof(contract), validate: async (state, response) =>
{
var result = new ValidateResult();
return result;
}
})
.OnCompletion(wrapUpRequest)
//.Confirm("Are you sure: Yes or No ")
.Build();
}
}
}
Actually it's quite easy to cancel a form. If you type "help" or "choices" you can see a list of builtin form commands, and one of these is "quit." There are many terms you can use to quit such as "finish" or "bye." If you want to define your own terms, you can can configure the form commands like this:
var builder = new FormBuilder<BalanceForm>().Message
("We have to ask you some information")
.Field(nameof(contract), validate: async (state, response) =>
{
var result = new ValidateResult();
return result;
})
.OnCompletion(wrapUpRequest)
// Set the command term configuration on its own line
builder.Configuration.Commands[FormCommand.Quit].Terms = new[] { "exit", "cancel" };
return builder.Build();
Keep in mind that when a form is canceled, a FormCanceledException<T> is thrown. If you don't want this to display a message like "Sorry, my bot code is having an issue," you can catch the exception like this:
var balanca = new FormDialog<BalanceForm>(
new BalanceForm(),
BalanceForm.BuildForm,
FormOptions.PromptInStart,
result.Entities)
.Catch<BalanceForm, FormCanceledException<BalanceForm>>((dialog, ex) =>
{
// Handle the cancellation here and return an IDialog<BalanceForm>
});

Delegate function error

I have following code. dont have any clue why I am getting this error - Delegate Function doesnot take 1 arguments.
I am implementing a global Response handler which will be called by saveral requests.
Here is the HandleResponse function:
Response<T> HandleResponse<T>(Func<Response<T>> func)
{
try
{
Response <T> response = func();
if(response != null)
{
return response;
}
else
{
//display response.message
return default(Response<T>);
}
}
catch(ConnectionException e)
{
// func = e.Message.ToString();
Console.WriteLine(e.Message);
return default(Response<T>);
}
}
And here is my Calling Function:
Request.MyRequest request = new
Request.MyRequest()
{
param1= comment, param2 = users
};
SomeResponse response = HandleResponse<SomeResponse>(x => client.DoRequest(request, revision.Name, revision.RevisionNumber));
I am a C++ developer and very new to C#. I am not sure why I am getting this error. Please someone explain.
Thanks in advance.
By using x => client.DoRequest() you are calling the delegate with 1 Parameter (the x before =>).
But your delegate has no parameters so you have to call it like this
() => client.DoRequest()

Call a aws Lambda function from another lambda function in C#

I am new to aws lambda functions with c#. I have two lambda functions and I want to call one lambda function from second function, I am using code as below:
public string Function1(JObject input)
{
string param = input["param"].ToString();
string param1 = input["param1"].ToString();
return param.ToUpper()+" "+param1.ToUpper();
}
public string Function2()
{
try
{
using (AmazonLambdaClient client = new AmazonLambdaClient(some region))
{
JObject ob = new JObject();
ob.Add("param", "hello");
ob.Add("param1", "Lambda");
var request = new InvokeRequest
{
FunctionName = "Function1",
Payload = ob.ToString()
};
var response = client.Invoke(request);
string result;
using (var sr = new StreamReader(response.Payload))
{
return result = sr.ReadToEnd();
}
}
}
catch (Exception ex)
{
return ex.Message.ToString();
}
}
And I am getting an error as below:
{
"errorType": "TypeLoadException",
"errorMessage": "Could not load type 'System.Net.HttpStatusCode' from assembly 'System, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes'.",
"stackTrace": [
"at AWSLambdaApp.Function.Function2()",
"at lambda_method(Closure , Stream , Stream , ContextInfo )"
]
}
And in function 2 application I have added a reference of "AWSSDK.Core" and "AWSSDK.Lambda" dlls.
Can any one tell me what I am doing wrong?
I don't have 'netcoreapp1.0' installed on my PC. So I have tried on 'netcoreapp2.0'.
And my code looks like:
public string FunctionHandler(JObject input, ILambdaContext context)
{
param = input["param"].Value<string>();
param1 = input["param1"].Value<string>();
param.ToUpper() + " " + param1.ToUpper();
}
and for Function2:
public async Task<string> FunctionHandler(string input, ILambdaContext context)
{
try
{
using (AmazonLambdaClient client = new AmazonLambdaClient(RegionEndpoint.USEast1))
{
JObject ob = new JObject { { "param", "hello" }, { "param1", "Lambda" } };
var request = new InvokeRequest
{
FunctionName = "Function1",//Function1:v1 if you use alias
Payload = ob.ToString()
};
var response = await client.InvokeAsync(request);
using (var sr = new StreamReader(response.Payload))
{
return await sr.ReadToEndAsync();
}
}
}
catch (Exception ex)
{
return ex.Message;
}
}
And the result of execution is "\"HELLO LAMBDA\"".
The reason for work of my code could be:
How I get parameter from input in Function1. It is most probably.
My async code. I can't use the synchronous code for call lambda with the latest SDK.
The version of .net core.
Also, I need send a simple string as the parameter of Function2.
I think you have to rethink your architecture. Keep in mind that the lambda function has limitations (time of execution & CPU usage).
It is better to use aws step function for such cases. In this case, you could use the Chain of Responsibility pattern. Lambda functions will be decoupled, and it is easier to support them. You will have something like as a result:
Moreover, aws step-functions has a lot of features like condition, parallel execution, waiters so on. It gives you powerful tools for improving your application.

Adding JS Validation on CompareValidator

is it possible to add a JS validation function for CompareValidator?
i can't use a CustomValidator.
I'm not sure whether you still need it or not, I assume not however since this is unanswered...
Well, you can't do it directly, however you can hide original function which is responsible for CompareValidator validation and introduce new one. This is possible because all validation functions comes from ASP.NET in global scope which is... controversial, but helpful in that case.
Please find following module which get the job done. It exposes you two methods. First called addFunction allows you to add one or more functions for custom validation. This function should returns Boolean and it consumes three parameters Validator object , TargetObject and CompareObject respectively. Second one called disableOldFunction allows you to completely get rid of old validation function which is called by the module if all your functions are valid.
var MyModules = {};
MyModules.CompareValExtension = function ()
{
var functions = new Array();
var oldFunc = null, disabled = false;
function myCompareValidatorEvaluateIsValid(val)
{
var valid = true;
if (oldFunc && functions.length)
{
for (i in functions)
{
valid = functions[i](val, document.getElementById(val.controltovalidate), document.getElementById(val.controltocompare));
if (!valid) { break; }
}
if (!disabled && valid)
{
valid = oldFunc(val);
}
}
return valid;
}
if (typeof CompareValidatorEvaluateIsValid != 'undefined')
{
oldFunc = CompareValidatorEvaluateIsValid;
window.CompareValidatorEvaluateIsValid = myCompareValidatorEvaluateIsValid;
}
var me = {};
me.addFunction = function (func) { if (typeof func == "function") { functions.push(func); } }
me.disableOldFunction = function () { disabled = true; }
return me;
} ();
Example usage:
MyModules.CompareValExtension.addFunction(function (val, elem, comp)
{
return elem.value == "my value";
});
MyModules.CompareValExtension.disableOldFunction();
Caveat: Please place this module somewhere on the page bottom to be sure that default validation scripts are in place already. You can also rewrite module a bit to defer initialization until document will be ready.

Categories

Resources