System.CommandLine argument not being passed to handler - c#

I'm trying to get to grips with System.CommandLine.
I can parse boolean options on the commandline such as -x and I can pass string options, eg -f myfile however what I'm having trouble with is the string argument that can optionally be passed to the program- not as an option.
The parser is on some level understanding the argument. If I provide zero or one strings on the commandline then there's no error. If I provide an extra string then I get the error "Unrecognized command or argument". In the handler, however, the parameter is always null. Even if I've specified a default and not marked the string as nullable.
The sandpit code I'm working with is below. Can anyone suggest what I'm doing wrong? I've found lots of discussion elsewhere about Options, but rather less about Arguments.
using System.CommandLine;
using System.CommandLine.IO;
using System.CommandLine.NamingConventionBinder;
namespace ConsoleApp1
{
public static class Program
{
public static int Main(string[] args)
{
RootCommand _cmd = new()
{
new Option<bool>(new[] { "--immediatecalc", "-c" }, () => { return false; }, "Automatically run calculations when a file is loaded"),
new Option<bool>(new[] { "--autoexit", "-x" }, () => { return false; }, "Exit after carrying out directed tasks"),
new Option<bool>(new[] { "--saveJSON", "-s" }, () => { return false; }, "Export on deck JSON file after calculating"),
new Option<string?>(new[] { "--JSONfile", "-f" }, () => { return null; }, "File name for exported JSON file"),
new Argument<string?>("Filename", () => { return null; }, "The file to load.")
};
_cmd.Handler = CommandHandler.Create<bool, bool, bool, string?, string?, IConsole>(Handler);
return _cmd.Invoke(args);
}
static void Handler(bool immediatecalc, bool autoexit, bool saveJSON, string? jsonFile, string? sourceFile, IConsole console)
{
console.WriteLine("Immediate calc " + immediatecalc);
console.WriteLine("Autoexit " + autoexit);
console.WriteLine("saveJSON " + saveJSON);
console.WriteLine("Json file is " + jsonFile);
console.WriteLine("Source file is " + sourceFile);
}
}
}

This is how I would set it up using the latest System.CommandLine version 2.0.0-beta3.22114.1
using System.CommandLine;
namespace ConsoleApp1
{
public static class Program
{
public static int Main(string[] args)
{
var immediateCalcOption = new Option<bool>(new[] { "--immediatecalc", "-c" }, () => { return false; }, "Automatically run calculations when a file is loaded");
var autoExitOption = new Option<bool>(new[] { "--autoexit", "-x" }, () => { return false; }, "Exit after carrying out directed tasks");
var saveJsonOption = new Option<bool>(new[] { "--saveJSON", "-s" }, () => { return false; }, "Export on deck JSON file after calculating");
var jsonFileOption = new Option<string?>(new[] { "--JSONfile", "-f" }, () => { return null; }, "File name for exported JSON file");
var fileNameArgument = new Argument<string?>("Filename", () => { return null; }, "The file to load.");
RootCommand _cmd = new()
{
immediateCalcOption,
autoExitOption,
saveJsonOption,
jsonFileOption,
fileNameArgument
};
_cmd.SetHandler<bool, bool, bool, string?, string?, IConsole>(Handler, immediateCalcOption, autoExitOption, saveJsonOption, jsonFileOption, fileNameArgument);
return _cmd.Invoke(args);
}
static void Handler(bool immediatecalc, bool autoexit, bool saveJSON, string? jsonFile, string? sourceFile, IConsole console)
{
console.WriteLine("Immediate calc " + immediatecalc);
console.WriteLine("Autoexit " + autoexit);
console.WriteLine("saveJSON " + saveJSON);
console.WriteLine("Json file is " + jsonFile);
console.WriteLine("Source file is " + sourceFile);
}
}
}
this worked for me with the args input filename -c -x -s -f hello gave me this output
Immediate calc True
Autoexit True
saveJSON True
Json file is hello
Source file is filename

Related

How to have an AWS Lambda/Rekognition Function return an array of object keys

This feels like a simple question and I feel like I am overthinking it. I am doing an AWS project that will compare face(s) on an image to a database (s3bucket) of other faces. So far, I have a lambda function for the comparefacerequest, a class library which invokes the function, and an UWP that inputs the image file and outputs a result. It has worked so far being based on boolean (true or false) functions, but now I want it to instead return what face(s) are recognized via an array. I struggling at implementing this.
Below is my lambda function. I have adjusted the task to be an Array instead of a bool and changed the return to be an array. At the bottom, I have created a global variable class with a testing array so I could attempt to reference the array elsewhere.
public class Function
{
//Function
public async Task<Array> FunctionHandler(string input, ILambdaContext context)
{
//number of matched faces
int matched = 0;
//Client setup
var rekognitionclient = new AmazonRekognitionClient();
var s3client = new AmazonS3Client();
//Create list of target images
ListObjectsRequest list = new ListObjectsRequest
{
BucketName = "bucket2"
};
ListObjectsResponse listre = await s3client.ListObjectsAsync(list);
//loop of list
foreach (Amazon.S3.Model.S3Object obj in listre.S3Objects)
{
//face request with input and obj.key images
var comparefacesrequest = new CompareFacesRequest
{
SourceImage = new Image
{
S3Object = new S3Objects
{
Bucket = "bucket1",
Name = input
}
},
TargetImage = new Image
{
S3Object = new S3Objects
{
Bucket = "bucket2",
Name = obj.Key
}
},
};
//compare with confidence of 95 (subject to change) to current target image
var detectresponse = await rekognitionclient.CompareFacesAsync(comparefacesrequest);
detectresponse.FaceMatches.ForEach(match =>
{
ComparedFace face = match.Face;
if (match.Similarity > 95)
{
//if face detected, raise matched
matched++;
for(int i = 0; i < Globaltest.testingarray.Length; i++)
{
if (Globaltest.testingarray[i] == "test")
{
Globaltest.testingarray[i] = obj.Key;
}
}
}
});
}
//Return true or false depending on if it is matched
if (matched > 0)
{
return Globaltest.testingarray;
}
return Globaltest.testingarray;
}
}
public static class Globaltest
{
public static string[] testingarray = { "test", "test", "test" };
}
Next, is my invoke request in my class library. It has so far been based on the lambda outputting a boolean result, but I thought, "hey, it is parsing the result, it should be fine, right"? I do convert the result to a string, as there is no GetArray, from what I know.
public async Task<bool> IsFace(string filePath, string fileName)
{
await UploadS3(filePath, fileName);
AmazonLambdaClient client = new AmazonLambdaClient(accessKey, secretKey, Amazon.RegionEndpoint.USWest2);
InvokeRequest ir = new InvokeRequest();
ir.InvocationType = InvocationType.RequestResponse;
ir.FunctionName = "ImageTesting";
ir.Payload = "\"" + fileName + "\"";
var result = await client.InvokeAsync(ir);
var strResponse = Encoding.ASCII.GetString(result.Payload.ToArray());
if (bool.TryParse(strResponse, out bool result2))
{
return result2;
}
return false;
}
Finally, here is the section of my UWP where I perform the function. I am referencing the lambda client via "using Lambdaclienttest" (name of lamda project, and this is its only instance I use the reference though). When I run my project, I do still get a face detected when it should, but the Globaltest.testingarray[0] is still equal to "test".
var Facedetector = new FaceDetector(Credentials.accesskey, Credentials.secretkey);
try
{
var result = await Facedetector.IsFace(filepath, filename);
if (result)
{
textBox1.Text = "There is a face detected";
textBox2.Text = Globaltest.testingarray[0];
}
else
{
textBox1.Text = "Try Again";
}
}
catch
{
textBox1.Text = "Please use a photo";
}
Does anyone have any suggestions?

How to directly get option in C# Command-Line

I'm testing C# console application cli tool (System.CommandLine) & I have this code:
using System.CommandLine.NamingConventionBinder;
using System.CommandLine;
internal class Program
{
private static async Task Main (string[] args)
{
var rootCommand = new RootCommand { };
var getCommand = new Command ("get")
{
new Option<string> (
"me",
description: "Gets the name of a takeep & shows its text.")
};
var setCommand = new Command ("set")
{
new Option<string> (
"the",
description: "Sets a takeep by name & text.")
};
getCommand.Handler = CommandHandler.Create<string> ((me) =>
{
Console.WriteLine ($"Build Version: {me}");
});
setCommand.Handler = CommandHandler.Create<string> ((set) =>
{
Console.WriteLine ($"Build Version: {set}");
});
rootCommand.Add (getCommand);
rootCommand.Add (setCommand);
await rootCommand.InvokeAsync (args);
}
}
When I call the command, this way:
.\takeep.cli.exe get me 'Name'
Is there a way to remove that "me", but keep its functionality (I mean, other commands also work)? Thanks for your help 💐

How to use project methods with Roslyn?

I am using Roslyn to run C# code from text.
It works, but I can't figure out how can I use my project class methods, didn't find a way to reference my project methods. I can't use a dll as there too many classes and forms I need to use.
For exmaple, using the Number set method, or the DoMultiAction() method inside the roslyn ExecuteUserCodeTest() text code. How to do that they will be referenced?
Is it even possible? Any explanation or example will be appreciated.
My Code:
namespace bot1
{
class Testing
{
private int number;
public int Number
{
get { return number; }
set { this.number = value; }
}
public int DoMultiAction(int num1, int num2)
{
return num1 * num2 * Number;
}
public void ExecuteUserCodeTest()
{
String text = #"
using System;
using System.Drawing;
using System.Windows.Forms;
namespace RoslynCompileSample
{
public class Writer
{
public void Write(String text)
{
MessageBox.Show(text);
}
}
}";
// define source code, then parse it (to the type used for compilation)
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(text);
// define other necessary objects for compilation
string assemblyName = Path.GetRandomFileName();
MetadataReference[] references = new MetadataReference[]
{
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location),
MetadataReference.CreateFromFile(typeof(System.Drawing.Point).Assembly.Location),
MetadataReference.CreateFromFile(typeof(System.Windows.Forms.MessageBox).Assembly.Location),
MetadataReference.CreateFromFile(typeof (ScriptManagerHandler.ScriptHandler).Assembly.Location)
};
// analyse and generate IL code from syntax tree
CSharpCompilation compilation = CSharpCompilation.Create(
assemblyName,
syntaxTrees: new[] { syntaxTree },
references: references,
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
using (var ms = new MemoryStream())
{
// write IL code into memory
EmitResult result = compilation.Emit(ms);
if (!result.Success)
{
// handle exceptions
IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error);
String error = "";
foreach (Diagnostic diagnostic in failures)
{
error += "" + diagnostic.Id + ", " + diagnostic.GetMessage() + "\n";
}
if (error != "")
MessageBox.Show(error);
}
else
{
// load this 'virtual' DLL so that we can use
ms.Seek(0, SeekOrigin.Begin);
Assembly assembly = Assembly.Load(ms.ToArray());
// create instance of the desired class and call the desired function
Type type = assembly.GetType("RoslynCompileSample.Writer");
object obj = Activator.CreateInstance(type);
type.InvokeMember("Write",
BindingFlags.Default | BindingFlags.InvokeMethod,
null,
obj,
new object[] { "Hello World" });
}
}
}
}
}

Exception in release but not debug

I have wrapped the C# FCM AdminSDK in a WCF. When I publish the code to my local using debug everything works as expected. When I publish the code using release I get a "Object reference not set to an instance of an object." when attempting to instantiate the "Message" object. Why does this happen?
The exception happens on the line "var fcmMessage = new Message()"
using FirebaseAdmin;
using FirebaseAdmin.Messaging;
using Google.Apis.Auth.OAuth2;
using ID.Service.PushNotification.Enums;
using ID.Service.PushNotification.Models;
using ID.Service.PushNotification.ServiceHelpers;
using System;
using System.Collections.Generic;
using System.Text;
using System.Web.Hosting;
namespace ID.Service.PushNotification.Helpers
{
public class FcmHelper
{
readonly static FirebaseApp app = FirebaseApp.Create(new AppOptions()
{
Credential = GoogleCredential.FromFile(HostingEnvironment.MapPath(#"~/App_Data/jq4bb-37597f7301.json"))
});
public static void BulkPushNotification(List<EnrolmentModel> enrolments, string message, int messageId, DeepLink path = DeepLink.None)
{
foreach (EnrolmentModel enrolment in enrolments)
{
PushNotification(enrolment, message, messageId, path);
}
}
public static async void PushNotification(EnrolmentModel enrolment, string message, int messageId, DeepLink path = DeepLink.None)
{
try
{
var pathLink = (path != DeepLink.None) ? path.GetPath() : "";
var registrationToken = Encoding.UTF8.GetString(Convert.FromBase64String(enrolment.DeviceToken));
LogHelper.Error("rt: " + registrationToken);
LogHelper.Error("msg: " + message);
LogHelper.Error("pl" + pathLink);
var fcmMessage = new Message()
{
Token = registrationToken,
Android = new AndroidConfig()
{
Notification = new AndroidNotification()
{
Body = message,
Title = "Title",
Sound = "bing"
//ClickAction = "rewards",
//Color = "#CA5151",
//Icon="",
},
Priority = Priority.Normal,
TimeToLive = TimeSpan.FromSeconds(2419200),
//Data = new Dictionary<string, string>()
//{
// { "deepLinkPath", pathLink }
//},
}
};
// Send a message to the device corresponding to the provided
// registration token.
string response = await FirebaseMessaging.DefaultInstance.SendAsync(fcmMessage);
bool successfullySent = false;
if (response.ToLower().Contains("projects/com-app/messages/0:"))
{
successfullySent = true;
}
ResultFeedbackServiceHelper.SaveResultFeedback(
response,
Convert.ToInt32(messageId),
Convert.ToInt32(enrolment.DeviceId),
successfullySent,
new List<string> { enrolment.DeviceToken }
);
}
catch (Exception ex)
{
ResultFeedbackServiceHelper.SaveResultFeedback(
ex.Message,
Convert.ToInt32(messageId),
Convert.ToInt32(enrolment.DeviceId),
false,
new List<string> { enrolment.DeviceToken }
);
LogHelper.Error("Error sending push messages to (fcm) gcn " + ex.ToString());
}
}
}
}
Exception:''2019-03-05 15:09:55,637 Thread:'[13]' Level:'ERROR' Message:'Error sending push messages to (fcm) gcn System.NullReferenceException: Object reference not set to an instance of an object.
at ID.Service.PushNotification.Helpers.FcmHelper.d__2.MoveNext() in D:\BuildAgents\Agent1_work\475\s\PNS\Main\ID.Service.PushNotification\Helpers\FCMHelper.cs:line 49'

Compile C# Roslyn

I'm using Roslyn to try and compile and run code at runtime. I've ysed some code I found online and have it somewhat working.
public Type EvalTableScript(string Script, CRMMobileFramework.EnbuUtils EnbuUtils, CRMMobileFramework.Includes.DBAdapter dbConn)
{
var syntaxTree = SyntaxTree.ParseText(Script);
var compilation = Compilation.Create("EnbuScript.dll",
options: new CompilationOptions(outputKind: OutputKind.DynamicallyLinkedLibrary),
references: new[]
{
new MetadataFileReference(typeof(object).Assembly.Location),
new MetadataFileReference(typeof(EnbuUtils).Assembly.Location),
new MetadataFileReference(typeof(DBAdapter).Assembly.Location),
MetadataFileReference.CreateAssemblyReference("System.Data"),
MetadataFileReference.CreateAssemblyReference("System.Linq"),
MetadataFileReference.CreateAssemblyReference("System"),
MetadataFileReference.CreateAssemblyReference("System.XML")
},
syntaxTrees: new[] { syntaxTree });
var diagnostics = compilation.GetDiagnostics();
foreach (var diagnostic in diagnostics)
{
Console.WriteLine("Error: {0}", diagnostic.Info.GetMessage());
}
Assembly assembly;
using (var stream = new MemoryStream())
{
EmitResult emitResult = compilation.Emit(stream);
assembly = Assembly.Load(stream.GetBuffer());
}
Type ScriptClass = assembly.GetType("EnbuScript");
// Pass back the entire class so we can call it at the appropriate time.
return ScriptClass;
}
Then I'm trying to call this:
string Script = #"
using System;
using System.Data;
using System.IO;
using System.Linq;
public class EnbuScript
{
public string PostInsertRecord(CRMMobileFramework.EnbuUtils EnbuUtils,CRMMobileFramework.Includes.DBAdapter dbConn)
{
string ScriptTable = ""QuoteItems"";
DataSet EntityRecord = dbConn.FindRecord(""*"", ScriptTable, ""QuIt_LineItemID='"" + EnbuUtils.GetContextInfo(ScriptTable) + ""'"", """", 1, 1, false);
string OrderId = EntityRecord.Tables[""item""].Rows[0][""QuIt_orderquoteid""].ToString();
string UpdateOrderTotalCommand = ""UPDATE Quotes SET Quot_nettamt = (select SUM(QuIt_listprice * quit_quantity) from QuoteItems where quit_orderquoteid = "" + OrderId + "" ) where Quot_OrderQuoteID = "" + OrderId;
dbConn.ExecSql(UpdateOrderTotalCommand);
return ""Complete"";
}
}";
Type EnbuScript = EnbuUtils.EvalTableScript(Script, EnbuUtils, dbConn);
MethodInfo methodInfo = EnbuScript.GetMethod("InsertRecord");
object[] parameters = { EnbuUtils, dbConn };
string InsertRecordResult = methodInfo.Invoke(null, parameters).ToString();
As you can see I've been messing around with trying to pass parameters to the compilation.
Basically I've got 4 functions I need to support, that will come in as a string. What I'm trying to do is create a class for these 4 functions and compile and run them. This part works.
What I now need to be able to do is pass class instances to this. In the code you'll see a dbConn which is basically my database connection. I need to pass the instance of this to the method I'm calling at runtime so it has it's correct context.
I have another implementation of this where I'm using the Roslyn session. I originally tried to use this and override my function at runtime but that didn't work either. See below what I tried:
public static void EvalTableScript(ref EnbuUtils EnbuUtils, DBAdapter dbConn, string EvaluateString)
{
ScriptEngine roslynEngine = new ScriptEngine();
Roslyn.Scripting.Session Session = roslynEngine.CreateSession(EnbuUtils);
Session.AddReference(EnbuUtils.GetType().Assembly);
Session.AddReference(dbConn.GetType().Assembly);
Session.AddReference("System.Web");
Session.AddReference("System.Data");
Session.AddReference("System");
Session.AddReference("System.XML");
Session.ImportNamespace("System");
Session.ImportNamespace("System.Web");
Session.ImportNamespace("System.Data");
Session.ImportNamespace("CRMMobileFramework");
Session.ImportNamespace("CRMMobileFramework.Includes");
try
{
var result = (string)Session.Execute(EvaluateString);
}
catch (Exception ex)
{
}
}
I tried to call this using:
string PostInsertRecord = "" +
" public override void PostInsertRecord() " +
"{ " +
" string ScriptTable = \"QuoteItems\"; " +
"DataSet EntityRecord = dbConn.FindRecord(\"*\", ScriptTable, \"QuIt_LineItemID='\" + EnbuUtils.GetContextInfo(ScriptTable) + \"'\", \"\", 1, 1, false); " +
"string OrderId = EntityRecord.Tables[\"item\"].Rows[0][\"QuIt_orderquoteid\"].ToString(); " +
"string UpdateOrderTotalCommand = \"UPDATE Quotes SET Quot_nettamt = (select SUM(QuIt_listprice * quit_quantity) from QuoteItems where quit_orderquoteid = \" + OrderId + \" ) where Quot_OrderQuoteID = \" + OrderId; " +
"dbConn.ExecSql(UpdateOrderTotalCommand); " +
"} ";
The function is declared as a public virtual void in the EnbuUtils class but it says it doesn't have a suitable method to override.
Safe to say, I'm stumped!
Any help appreciated!
Thanks
I got this in the end - this first method was very close to what I actually needed. Changed the method to static and had to add a few references including the full namespace.

Categories