Command Line Parser verb help not working? - c#

I have defined options as follows:
public class ArgumentsHeader
{
[VerbOption("configure", HelpText = "Sets configuration on server.")]
public ServerConfigurationArguments ServerConfigurationArguments { get; set; }
[HelpVerbOption]
public string GetUsage(string s)
{
return HelpText.AutoBuild(this, s);//always just 'help' or null showing up here.
}
}
public class ServerConfigurationArguments : ArgumentsBase
{
[Option('f', "filename", HelpText = "Path to JSON configuration file", DefaultValue = "config.json", Required = true)]
public string PathToConfig { get; set; }
}
Then parsing them like this:
string invokedVerb = null;
object invokedVerbInstance = null;
var parser = new Parser(x =>
{
x.MutuallyExclusive = true;
});
var options = new ArgumentsHeader();
if (!parser.ParseArguments(args, options,
(verb, subOptions) =>
{
// if parsing succeeds the verb name and correct instance
// will be passed to onVerbCommand delegate (string,object)
invokedVerb = verb;
invokedVerbInstance = subOptions;
}))
{
Exit(ExitStatus.InvalidArguments);
}
But if I try to run my exe with 'help configure' it will just print out entire help, AND in GetUsage(string) method there is only 'help' command showing up in debugger.
Is it a bug or what?

It IS a bug.
I checked with a program similar to yours and had the same (mis)behavior, then switched to the Command Line project itself, had the same but I think I found the problem.
If you are using the "source" version of Command Line Parser embedded in your project, you may fix it as follows (the code below is from class commandLine.Parser):
private bool TryParseHelpVerb(string[] args, object options, Pair<MethodInfo, HelpVerbOptionAttribute> helpInfo, OptionMap optionMap)
{
var helpWriter = _settings.HelpWriter;
if (helpInfo != null && helpWriter != null)
{
if (string.Compare(args[0], helpInfo.Right.LongName, GetStringComparison(_settings)) == 0)
{
// User explicitly requested help
// +++ FIX
// var verb = args.FirstOrDefault(); // This looks wrong as the first element is always the help command itself
var verb = args.Length == 1 ? null : args[1]; // Skip the help command and use next argument as verb
// --- FIX
if (verb != null)
{
var verbOption = optionMap[verb];
if (verbOption != null)
{
if (verbOption.GetValue(options) == null)
{
// We need to create an instance also to render help
verbOption.CreateInstance(options);
}
}
}
DisplayHelpVerbText(options, helpInfo, verb);
return true;
}
}
return false;
}
Unfortunately, if you link directly to the Command Line Parser DLL I don't think there is any workaround for it. In this case only the author can fix it...

If you are using the NuGet package, here is a quick work around. Store the args with the options so you can forward the actual verb to HelpText.AutoBuild. Also you will need to have an instance of your Verb Option for the HelpText.AutoBuild to inspect.
public class ArgumentsHeader
{
public string[] args { get; set; } = new string[0];
[VerbOption("configure", HelpText = "Sets configuration on server.")]
public ServerConfigurationArguments ServerConfigurationArguments { get; set; } = new ServerConfigurationArguments();
[HelpVerbOption]
public string GetUsage(string verb)
{
if (verb?.ToLower() == "help" && args.Length > 1)
{
verb = args[1];
}
return HelpText.AutoBuild(this, verb);
}
}
Then just create the options with the args.
var options = new ArgumentsHeader { args = args };

You can also pass the args omitting the first item of the array (which is the executable name).
For example (VB.NET):
Sub Main()
Dim args = Environment.GetCommandLineArgs().Skip(1)
Dim result = CommandLine.Parser.Default.ParseArguments(Of InstallOptions, UpdateOptions)(args)
...

Related

Is there a way to custom order endpoints in NSwag?

I am using NSwag to generate code documentation, but would like to be able to have custom control over which order the endpoints appear on the generated swagger endpoint. Possibly via an attribute on the controller methods.
I can't see any way to achieve this. A few other posts mention alphanumeric ordering, but I need to be able to define a custom ordering. Is there any way to do this?
My SwaggerConfig
public class SwaggerApiConfig
{
public Assembly TargetWebAssembly { get; }
public Action<SwaggerUi3Settings<WebApiToSwaggerGeneratorSettings>> ConfigureUiSettings { get; }
public SwaggerApiConfig(
string uiRoute,
Assembly targetWebAssembly,
string apiName,
string description,
string team = null,
string appDocumentationUrl = null)
{
if (!uiRoute.StartsWith("/"))
uiRoute = string.Format("/{0}", (object) uiRoute);
this.TargetWebAssembly = targetWebAssembly;
this.ConfigureUiSettings = SwaggerApiConfig.BuildSwaggerUiSettings(uiRoute, this.TargetWebAssembly, apiName, description, team, appDocumentationUrl);
}
public SwaggerApiConfig(
Assembly targetWebAssembly,
Action<SwaggerUi3Settings<WebApiToSwaggerGeneratorSettings>> configureUiSettings)
{
this.TargetWebAssembly = targetWebAssembly;
this.ConfigureUiSettings = configureUiSettings;
}
public static Action<SwaggerUi3Settings<WebApiToSwaggerGeneratorSettings>> BuildSwaggerUiSettings(
string uiRoute,
Assembly targetWebAssembly,
string apiName,
string description,
string team = null,
string appDocumentationUrl = null)
{
if (!uiRoute.StartsWith("/"))
uiRoute = string.Format("/{0}", (object) uiRoute);
return (Action<SwaggerUi3Settings<WebApiToSwaggerGeneratorSettings>>) (settings =>
{
settings.SwaggerUiRoute = uiRoute;
settings.SwaggerRoute = string.Format("{0}/swagger.json", (object) uiRoute);
settings.DocExpansion = "list";
settings.GeneratorSettings.Title = apiName;
settings.GeneratorSettings.Version = string.Format("{0}", (object) targetWebAssembly.GetName().Version);
settings.GeneratorSettings.Description = description;
settings.GeneratorSettings.IgnoreObsoleteProperties = false;
settings.GeneratorSettings.GenerateKnownTypes = true;
settings.GeneratorSettings.GenerateAbstractProperties = true;
settings.GeneratorSettings.DefaultEnumHandling = EnumHandling.String;
settings.GeneratorSettings.OperationProcessors.Add((IOperationProcessor) new CallerHeaderOperationProcessor());
settings.PostProcess = (Action<SwaggerDocument>) (x =>
{
x.Security = (ICollection<SwaggerSecurityRequirement>) new List<SwaggerSecurityRequirement>();
x.Info.Contact = new SwaggerContact()
{
Name = team,
Url = appDocumentationUrl
};
});
});
}
How it then gets applied in my application plumbing
if (swaggerApiOptions != null)
{
foreach (var swaggerApiConfig in swaggerApiOptions.Configurations)
{
appBuilder.UseSwaggerUi3(swaggerApiConfig.TargetWebAssembly, swaggerApiConfig.ConfigureUiSettings);
}
}
This most likely won't give your everything you need but it's worth pointing out that SwaggerUi3Settings class contains OperationsSorter setting.

Get field from a sub object in MongoDB

I have the following document in MongoDB and I want to check if the field FileName has a specific value.
Following are my classes:
public class Invoice
{
private InvoiceMetaData _metadata = null;
private List<InvoiceColumns> _invoiceFields = null;
public InvoiceMetaData Metadata
{
get
{
if (_metadata == null) _metadata = new InvoiceMetaData();
return _metadata;
}
set { _metadata = value; }
}
public List<InvoiceColumns> InvoiceFields
{
get
{
if (_invoiceFields == null)
_invoiceFields = new List<InvoiceColumns>();
return _invoiceFields;
}
set { _invoiceFields = value; }
}
}
public class InvoiceMetaData
{
public string FileName { get; set; }
public string FileProcessedOn { get; set; }
public string DirectoryPath { get; set; }
}
I've tried using the following but it's returning false even though documents with this filename exist.
string filename = "01.png";
var collection = myDB.GetCollection<Invoice>(collection_name);
var exists = collection.AsQueryable().Any(avm => avm.Metadata.FileName == filename);
I've also tried this but it's returning nothing i.e. List count is 0.
var query = Query<Invoice>.EQ(u => u.Metadata.FileName, filename).ToBsonDocument();
var exist = collection.Find(query).ToList();
Also tried this and list Count is 0,
var filter1 = Builders<Invoice>.Filter.Eq(u => u.Metadata.FileName, filename, filename);
var result = collection.Find(filter1).ToList();
Can anyone please tell me what am I doing wrong?
Any help will be much appreciated.
I haven`t worked with this for a while. But this can be helpful for you:
how to check if a field exists in a specific document Mongodb using C#?
As i remeber, you can do something like that:
var collection = myDB.GetCollection<BsonDocument>(collection_name); // you use Invoice, try BsonDocument
var documents = collection.Find(new BsonDocument()).ToList(); // this one should get you all documents
var fieldName = "Metadata" // name of field you need data from
foreach (var document in documents)
{
// this one should contain your metadata object from document, note it is BsonDocument
var metaDataObject= document.Contains(fieldName) ? document[fieldName].ToBsonDocument() : null;
var fileName= metaDataObject!= null ? metaDataObject["FileName"] : "No file name."; // should be your file name
// you also should be able to convert to dictionary, under this namespace MongoDB.Bson.BsonDocument
// var metadataDic= metaDataObject.ToDictionary();
// var fileName= metadataDic["FileName"]; // should be your file name
}
Filter examples:
var builder = Builders<BsonDocument>.Filter; // in your example invoice, try BsonDocument
var filter = builder.Eq("FileName", fileName);
var count = collection.Count(filter);

class member values are zero after a return to calling method

I am experimenting(First timer!) with external assemblies, and whenever I call a method from my external assembly I get all empty data... here are some snippits:
External assembly called like this:
FileOps.Outs O = new FileOps.Outs();
FileOps.ReadFile(LOC); // LOC = Filepath
foreach (string S in O.FileContainer) // O.FileContainer is List<string>
{
Console.WriteLine(S); // Prints Nothing
}
Readfile() method in assembly:
public static Outs ReadFile(string LOC)
{
Outs O = new Outs();
if (File.Exists(LOC))
{
foreach (string S in File.ReadLines(LOC))
{
O.FileContainer.Add(S);
}
O.Baal = true;
}
else
{
//O.Baal = false;
//O.FileContainer.Contains(null);
File.Create(LOC).Close();
string Header = "Useless text data here\n," + "Users:\n,";
O.FileContainer = new List<string>(Header.Split(O.Csep, StringSplitOptions.None)); //Csep is char[] {','}
foreach (string S in O.FileContainer)
{
File.AppendAllText(LOC, S);
}
}
return O;
}
Class used to store info:
public class Outs
{
public bool Baal { get; set; }
public List<string>FileContainer { get; set; }
public char[] Csep;
public Outs()
{
Baal = false;
FileContainer = new List<string>();
Csep = new char[] { ',' };
}
}
Upon returning to calling method all values are 0 or null... I have set a breakpoint and went through the code line by line. Prior to returning all data is good... any thoughts?
Instead of
FileOps.Outs O = new FileOps.Outs();
FileOps.ReadFile(LOC); // LOC = Filepath
Try this:
FileOps.Outs O = FileOps.ReadFile(LOC);
It's a static method that returns an instance. The caller isn't meant to use new to create their own instance.
If you are in control of the FileOps class, you can prevent the caller from making this mistake by marking the constructor private, e.g.
public class Outs
{
private Outs()
{
//etc.
This way, the static method ReadFile has the ability to create a FileOps (because it is a member of the class), but nobody outside of the class can instantiate one. This is a pretty common pattern.

Validating and parsing url parameters in ASP.NET

I'm maintaining a legacy WebForms application and one of the pages just serves GET requests and works with many query string parameters. This work is done in the code-behind and does a lot of this type of check and casting.
protected override void OnLoad(EventArgs e)
{
string error = string.Empty;
string stringParam = Request.Params["stringParam"];
if (!String.IsNullOrEmpty(stringParam))
{
error = "No parameter";
goto LoadError;
}
Guid? someId = null;
try
{
someId = new Guid(Request.Params["guidParam"]);
}
catch (Exception){}
if (!someId.HasValue)
{
error = "No valid id";
goto LoadError;
}
// parameter checks continue on
LoadError:
log.ErrorFormat("Error loading page: {0}", error);
// display error page
}
I'd like to create a testable class that encapsulates this parsing and validation and moves it out of the code-behind. Can anyone recommend some approaches to this and/or examples?
As a first big step, I'd probably create some form of mapper/translator object, like this:
class SpecificPageRequestMapper
{
public SpecificPageRequest Map(NameValueCollection parameters)
{
var request = new SpecificPageRequest();
string stringParam = parameters["stringParam"];
if (String.IsNullOrEmpty(stringParam))
{
throw new SpecificPageRequestMappingException("No parameter");
}
request.StringParam = stringParam;
// more parameters
...
return request;
}
}
class SpecificPageRequest
{
public string StringParam { get; set; }
// more parameters...
}
Then your OnLoad could look like this:
protected override void OnLoad(EventArgs e)
{
try
{
var requestObject = requestMapper.Map(Request.Params);
stringParam = requestObject.StringParam;
// so on, so forth. Unpack them to the class variables first.
// Eventually, just use the request object everywhere, maybe.
}
catch(SpecificPageRequestMappingException ex)
{
log.ErrorFormat("Error loading page: {0}", ex.Message);
// display error page
}
}
I've omitted the code for the specific exception I created, and assumed you instantiate a mapper somewhere in the page behind.
Testing this new object should be trivial; you set the parameter on the collection passed into Map, then assert that the correct parameter on the request object has the value you expect. You can even test the log messages by checking that it throws exceptions in the right cases.
Assuming that you may have many such pages using such parameter parsing, first create a simple static class having extension methods on NamedValueCollection. For example,
static class Parser
{
public static int? ParseInt(this NamedValueCollection params, string name)
{
var textVal = params[name];
int result = 0;
if (string.IsNullOrEmpty(textVal) || !int.TryParse(textVal, out result))
{
return null;
}
return result;
}
public static bool TryParseInt(this NamedValueCollection params, string name, out int result)
{
result = 0;
var textVal = params[name];
if (string.IsNullOrEmpty(textVal))
return false;
return int.TryParse(textVal, out result);
}
// ...
}
Use it as follows
int someId = -1;
if (!Request.Params.TryParseInt("SomeId", out someId))
{
// error
}
Next step would be writing page specific parser class. For example,
public class MyPageParser
{
public int? SomeId { get; private set; }
/// ...
public IEnumerable<string> Parse(NamedValueCollection params)
{
var errors = new List<string>();
int someId = -1;
if (!params.TryParseInt("SomeId", out someId))
{
errors.Add("Some id not present");
this.SomeId = null;
}
this.SomeId = someId;
// ...
}
}

C# attribute text from resource file?

I have an attribute and i want to load text to the attribute from a resource file.
[IntegerValidation(1, 70, ErrorMessage = Data.Messages.Speed)]
private int i_Speed;
But I keep getting
"An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type"
It works perfectly if i add a string instead of Data.Messages.Text, like:
[IntegerValidation(1, 70, ErrorMessage = "Invalid max speed")]
Any ideas?
Here is my solution. I've added resourceName and resourceType properties to attribute, like microsoft has done in DataAnnotations.
public class CustomAttribute : Attribute
{
public CustomAttribute(Type resourceType, string resourceName)
{
Message = ResourceHelper.GetResourceLookup(resourceType, resourceName);
}
public string Message { get; set; }
}
public class ResourceHelper
{
public static string GetResourceLookup(Type resourceType, string resourceName)
{
if ((resourceType != null) && (resourceName != null))
{
PropertyInfo property = resourceType.GetProperty(resourceName, BindingFlags.Public | BindingFlags.Static);
if (property == null)
{
throw new InvalidOperationException(string.Format("Resource Type Does Not Have Property"));
}
if (property.PropertyType != typeof(string))
{
throw new InvalidOperationException(string.Format("Resource Property is Not String Type"));
}
return (string)property.GetValue(null, null);
}
return null;
}
}
Attribute values are hard-coded into the assembly when you compile. If you want to do anything at execution time, you'll need to use a constant as the key, then put some code into the attribute class itself to load the resource.
Here is the modified version of the one I put together:
[System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple = false)]
public class ProviderIconAttribute : Attribute
{
public Image ProviderIcon { get; protected set; }
public ProviderIconAttribute(Type resourceType, string resourceName)
{
var value = ResourceHelper.GetResourceLookup<Image>(resourceType, resourceName);
this.ProviderIcon = value;
}
}
//From http://stackoverflow.com/questions/1150874/c-sharp-attribute-text-from-resource-file
//Only thing I changed was adding NonPublic to binding flags since our images come from other dll's
// and making it generic, as the original only supports strings
public class ResourceHelper
{
public static T GetResourceLookup<T>(Type resourceType, string resourceName)
{
if ((resourceType != null) && (resourceName != null))
{
PropertyInfo property = resourceType.GetProperty(resourceName, BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
if (property == null)
{
return default(T);
}
return (T)property.GetValue(null, null);
}
return default(T);
}
}
I came across this problem with the display name for attribute, and I made the following changes:
For our resource file I changed the custom tool property to PublicResXFileCodeGenerator
Then added this to the attribute:
[Display(Name = "MyResourceName", ResourceType = typeof(Resources.MyResources))]
If you're using .NET 3.5 or newer you can use ErrorMessageResourceName and ErrorMessageResourceType parameters.
For example
[Required(ErrorMessageResourceName ="attribute_name" , ErrorMessageResourceType = typeof(resource_file_type))]
Use a string which is the name of the resource. .NET does this with some internal attributes.
The nature of attributes is such that the data you put in attribute properties must be constants. These values will be stored within an assembly, but will never result in compiled code that is executed. Thus you cannot have attribute values that rely on being executed in order to calculate the results.
I have a similar case, where I need to put resource strings into attributes. In C# 6, we have the nameof() capability, and that seems to do the trick.
In my case, I can use [SomeAttribute(nameof(Resources.SomeResourceKey))] and it compiles fine. Then I just have to do a little work on the other end to use that value to get the correct string from the Resources file.
In your case, you might try:
[IntegerValidation(1, 70, ErrorMessageResourceKey = nameof(Data.Messages.Speed))]
private int i_Speed;
Then you can do something along the lines of (pseudo code):
Properties.Resources.ResourceManager.GetString(attribute.ErrorMessageResourceKey);
Here's something I wrote since I couldn't find anything else that does this.:
Input
Write a constant string class in project A.
[GenerateResource]
public static class ResourceFileName
{
public static class ThisSupports
{
public static class NestedClasses
{
[Comment("Comment value")]
public const string ResourceKey = "Resource Value";
}
}
}
Output
And a resource will be generated in the project that contains the constants class.
All you need to do is have this code somewhere:
Source
public class CommentAttribute : Attribute
{
public CommentAttribute(string comment)
{
this.Comment = comment;
}
public string Comment { get; set; }
}
public class GenerateResourceAttribute : Attribute
{
public string FileName { get; set; }
}
public class ResourceGenerator
{
public ResourceGenerator(IEnumerable<Assembly> assemblies)
{
// Loop over the provided assemblies.
foreach (var assembly in assemblies)
{
// Loop over each type in the assembly.
foreach (var type in assembly.GetTypes())
{
// See if the type has the GenerateResource attribute.
var attribute = type.GetCustomAttribute<GenerateResourceAttribute>(false);
if (attribute != null)
{
// If so determine the output directory. First assume it's the current directory.
var outputDirectory = Directory.GetCurrentDirectory();
// Is this assembly part of the output directory?
var index = outputDirectory.LastIndexOf(typeof(ResourceGenerator).Assembly.GetName().Name);
if (index >= 0)
{
// If so remove it and anything after it.
outputDirectory = outputDirectory.Substring(0, index);
// Is the concatenation of the output directory and the target assembly name not a directory?
outputDirectory = Path.Combine(outputDirectory, type.Assembly.GetName().Name);
if (!Directory.Exists(outputDirectory))
{
// If that is the case make it the current directory.
outputDirectory = Directory.GetCurrentDirectory();
}
}
// Use the default file name (Type + "Resources") if one was not provided.
var fileName = attribute.FileName;
if (fileName == null)
{
fileName = type.Name + "Resources";
}
// Add .resx to the end of the file name.
fileName = Path.Combine(outputDirectory, fileName);
if (!fileName.EndsWith(".resx", StringComparison.InvariantCultureIgnoreCase))
{
fileName += ".resx";
}
using (var resx = new ResXResourceWriter(fileName))
{
var tuples = this.GetTuplesRecursive("", type).OrderBy(t => t.Item1);
foreach (var tuple in tuples)
{
var key = tuple.Item1 + tuple.Item2.Name;
var value = tuple.Item2.GetValue(null);
string comment = null;
var commentAttribute = tuple.Item2.GetCustomAttribute<CommentAttribute>();
if (commentAttribute != null)
{
comment = commentAttribute.Comment;
}
resx.AddResource(new ResXDataNode(key, value) { Comment = comment });
}
}
}
}
}
}
private IEnumerable<Tuple<string, FieldInfo>> GetTuplesRecursive(string prefix, Type type)
{
// Get the properties for the current type.
foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Static))
{
yield return new Tuple<string, FieldInfo>(prefix, field);
}
// Get the properties for each child type.
foreach (var nestedType in type.GetNestedTypes())
{
foreach (var tuple in this.GetTuplesRecursive(prefix + nestedType.Name, nestedType))
{
yield return tuple;
}
}
}
}
And then make a small project that has a reference to all your assemblies with [GenerateResource]
public class Program
{
static void Main(string[] args)
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
string path = Directory.GetCurrentDirectory();
foreach (string dll in Directory.GetFiles(path, "*.dll"))
{
assemblies.Add(Assembly.LoadFile(dll));
}
assemblies = assemblies.Distinct().ToList();
new ResourceGenerator(assemblies);
}
}
Then your attributes can use the static class ResourceFileName.ThisSupports.NestedClasses.ResourceKey while other code can use the resource file.
You might need to tailor it to your specific needs.

Categories