System.CommandLine Command custom validator: how to search - c#

I am writing a C# console application project (.NET 7.0) using Visual Studio for macOS 2022 v17.4 and System.CommandLine (2.0.0-beta4.22272.1).
I am trying to add a custom validator to one of my Commands, and found the following code snippet on the project's GitHub site:
this.AddValidator(commandResult =>
{
if (commandResult.Children.Contains("one") &&
commandResult.Children.Contains("two"))
{
commandResult.ErrorMessage = "Options '--one' and '--two' cannot be used together.";
}
});
However, it will not compile in my command, due to Error CS1929: 'IReadOnlyList' does not contain a definition for 'Contains' and the best extension method overload 'MemoryExtensions.Contains(ReadOnlySpan, string)' requires a receiver of type 'ReadOnlySpan' (CS1929).
I have looked for the appropriate extension method in the various System.CommandLine namespaces, but I don't see anything. (Yes, I included the System.Linq namespace.)
Can anyone suggest what is amiss?
Edit: The original code which I "borrowed" is this:
command.AddValidator(commandResult =>
{
if (commandResult.Children.Contains("one") &&
commandResult.Children.Contains("two"))
{
return "Options '--one' and '--two' cannot be used together.";
}
return null;
});
Note that this delegate returns a value, while the updated one does not, but instead sets ErrorMessage. That's one breaking change in System.CommandLine, and it's plausible that the issue here is due to another breaking change. The System.CommandLine project does note that the project is in a state of flux and subject to changes.

I have no experience with this specific library, but peeking its source hints that the collection you are calling .Contains() on doesn't contain Strings, but rather instances of SymbolResult, hence it cannot find suitable overload for the method you are trying to call.
I couldn't find the code you provided anywhere from mentioned GitHub repo, but instead found this piece of code from this test file of library:
command.Validators.Add(commandResult =>
{
if (commandResult.Children.Any(sr => sr.Symbol is IdentifierSymbol id && id.HasAlias("--one")) &&
commandResult.Children.Any(sr => sr.Symbol is IdentifierSymbol id && id.HasAlias("--two")))
{
commandResult.ErrorMessage = "Options '--one' and '--two' cannot be used together.";
}
});

The extension method you need is defined as part of the class System.Linq.Enumerable. So you need using System.Linq; at the top of your class file.
But that won't help if your project doesn't reference the assembly System.Linq.dll. (I don't know why it wouldn't, just out of the box, but perhaps it doesn't.) So you may need to add a reference for that.

Related

What's the easiest way to create a managed visualiser in C#?

I have a background in C++ and recently I started working in C#.
I have written following pieces of code (in Visual Studio):
var list_Loads = database.GetData<Load>().ToList();
var test_list = list_Loads.Where(o => (o.Name.Substring(0, 3) == "123")).ToList();
When I run the program and I move my mouse over both lists, first I get the count, which is very useful, but when I ask for the entries, this is what I get:
0 : namespace.Load
1 : namespace.Load
2 : namespace.Load
...
Not very useful, as you can imagine :-)
So my question: how can I show the Name attributes of those objects?
I thought: no problem. I have a background in native visualisers, so it should be rather easy to turn this into useful information, but then it comes:
In order to alter the way that those objects are represented, there is the first proposal to add a [DebuggerDisplay] "tag" to the definition of that class in source code.
However, as those classes are part of a framework I'm just referring to, I don't have access to the source code and hence I can't modify this.
Then I found another solution, which comes down to: "Write an entire C# project, debug, test and install it and it might work" (see documentation on "Custom visualisers of data" on the Microsoft website).
I almost choked in my coffee: writing an entire project, just for altering the view of an object??? (While, in C++, you just create a simple .natvis file, mention the classname and some configuration, launch .nvload and that's it.
Does anybody know a simple way to alter the appearance of C# object, without needing to pass through the whole burden of creating an entire C# project?
By the way, when I try to load a natvis file in Visual Studio immediate window, this is what I get:
.nvload "C:\Temp_Folder\test.natvis"
error CS1525: Invalid expression term '.'
What am I doing wrong?
Thanks in advance
OP (my emphasis):
In order to alter the way that those objects are represented, there is the first proposal to add a [DebuggerDisplay] "tag" to the definition of that class in source code.
However, as those classes are part of a framework I'm just referring to, I don't have access to the source code and hence I can't modify this.
Does anybody know a simple way to alter the appearance of C# object, without needing to pass through the whole burden of creating an entire C# project?
If you just want to specify [DebuggerDisplay] on a type, you don't have to have access to the source code. You can make use of [assembly:DebuggerDisplay()] and control how a type appears in the debugger. The only downside is that [assembly:DebuggerDisplay()] naturally only affects the current assembly whose code your mouse is hovering over. If you wish to use the customised display in other assemblies that you own, then you must repeat the [assembly:DebuggerDisplay()] definition.
Here's an easy before-and-after example with DateTime. I picked DateTime because we generally don't have access to the source code and it has some interesting properties:
var items = new List<DateTime>
{
DateTime.Now.AddDays(-2),
DateTime.Now.AddDays(-1),
DateTime.Now
};
...which on my machine defaults to:
Maybe I'm fussy and I just want to see:
Day of the week and
Day of the year
...I can do that via:
using System.Diagnostics;
[assembly: DebuggerDisplay("{DayOfWeek} {DayOfYear}", Target = typeof(DateTime))]
...which results in:
Example:
namespace DebuggerDisplayTests
{
public class DebuggerDisplayTests
{
public DebuggerDisplayTests()
{
var items = new List<DateTime>
{
DateTime.Now.AddDays(-2),
DateTime.Now.AddDays(-1),
DateTime.Now
};
}
}
.
.
.
}
Overrides
[assembly:DebuggerDisplay()] can also be used as a means to override pre-existing [DebuggerDisplay] on a 3-rd party type. Don't like what style they have chosen? Is the type showing far too much information? Change it with [assembly:DebuggerDisplay()].

ClosedXml.Excel HasDuplicates Extension Method Missing following Upgrade

I've been tasked with taking a legacy project written with WPF, that was integrated into a bigger application and making it stand-alone. It hasn't been touched for some time, and from experience, this will always lead to issues when trying to bring the associated packages up to date. My team regularly do maintenance to keep technologies up to date, but the codebase was maintained by a different team, so they haven't followed any maintenenace schedules. I'm finding an issue with ClosedXml whereby part of our code references an extension method HasDuplicates() (found in ClosedXml.Excel) that was available in 0.74.0 but when upgrading the the latest version (0.95.4) isn't present. Searching for help isn't returning anything useful, but am sure somewhere there must be a release note that states it has been removed or renamed but I can't find it!
Using 0.74.0, this code compiles:
if (serializedPeriodAllocations.Select(o => o.Period).HasDuplicates())
{
throw new Exception("Duplicate period allocations found!");
}
But when I upgrade ClosedXml to 0.95.4 or above, it doesn't and I get the following error:
CS1061 'IEnumerable<int>' does not contain a definition for 'HasDuplicates' and no accessible extension method 'HasDuplicates' accepting a first argument of type 'IEnumerable<int>' could be found (are you missing a using directive or an assembly reference?)
Does anyone know if there is any documentation that will help me figure out why it no longer works, and if it's possible to upgrade?
After further investigation, I found an Extensions class within the project that contained a HasDuplicates extension, which was coded like this:
public static bool HasDuplicates<T>(this ICollection<T> source)
{
return source.Distinct().Count() != source.Count;
}
I added in an override for the extension that accepted IEnumerable:
public static bool HasDuplicates<T>(this IEnumerable<T> source)
{
var enumerable = source as T[] ?? source.ToArray();
return enumerable.Distinct().Count() != enumerable.Length;
}
After removing the using for ClosedXml.Excel, the HasDuplicates() doesn't return an error, as expected.

In an Intent Architect template, can I resolve a type (from a string) so it will include appropriate using statements?

In a template which generates code dynamically based on certain conditions, some of the code uses types which require additional using statements to compile correctly. How do I reference/use such a type such that the using will be included in the generated output iff that specific code is generated?
e.g. Given this code, how would I make sure the correct using is added?
if (attribute.IsEnum()) {
// Resolve the EnumToStringConverter type?
statements.Add($".HasConversion(new EnumToStringConverter<{attribute.Type.Element.Name}>());");
}
One way to do this is to use the UseType(...) method that is available on the CSharpTemplateBase base class (available in Intent.Modules.Common.CSharp.3.0.10 nuget package).
For example, you could try something like this:
if (attribute.IsEnum()) {
// Resolve the EnumToStringConverter type?
statements.Add($".HasConversion(new {UseType("EnumToStringConverter", "<your-required-namespace>"}<{attribute.Type.Element.Name}>());");
}
There is also another overload which would take in the fully qualified name. For example:
UseType("Microsoft.EntityFrameworkCore.Storage.ValueConversion.EnumToStringConverter")
(assuming you're using the EnumToStringConverter from EF Core)

Activator.CreateInstance returning incorrect type?

I'd be grateful if you could help with this:
I have a base class for a Provider.
This is subclassed to create a
DefaultProvider.
DefaultProvider implements an interface called “IExportProvider”
DefaultProvider is subclassed to create a
DanishProvider.
The base class has various virtual methods that return string values in English.
The DanishProvider overrides these to return strings in Danish.
At runtime, I load either the DefaultProvider or the DanishProvider depending on a config. value. I do this by scanning the current assembly for the type name, which is either “DefaultProvider” or “DanishProvider”, and which implements “IExportProvider”
var importServiceType = assembly.DefinedTypes.FirstOrDefault(type => (type.Name.Equals(providerServiceName, StringComparison.OrdinalIgnoreCase)) && type.ImplementedInterfaces.Any(i => i == typeof(I)));
I then use Activator.CreateInstance to create an instance of the type:
return (IExportProvider)Activator.CreateInstance(importServiceType, …)
Problem
I use the code in a .Net 4.8 Web application, where I load the DanishProvider. This mostly works OK and I can see the Danish strings are returned. However, occasionally (after several days) the application appears to incorrectly load the DefaultProvider, and I see English strings returned.
Once it has switched to English it appears to stay on the DefaultProvider.
Twice I have done an IISRESET which fixes the problem immediately.
The settings
are not being changed.
There is only one type in the assembly called “DanishProvider”
The code is executing on a single UAT server. I have not seen the issue on my local PC.
I do not believe that the issue is related to caching.
Q. Is there a problem with the code approach described above?
Thank you

Two Grid Packages Installed: The call is ambiguous between the following methods or properties

I inherited a rather old MVC project that had a Grid package installed, Grid.MVC. It is used extensively, and taking it out or replacing it is not an option (client won't pay for it.)
We just built out a new portal section to the site, and in it, we used a new (and better) grid, NonFactors.Grid.Core.MVC5. Much more features and options.
But here's the problem. In all the places where the old grid is used, I now get this run-time error:
The call is ambiguous between the following methods or properties: 'NonFactors.Mvc.Grid.MvcGridExtensions.Grid(System.Web.Mvc.HtmlHelper, System.Collections.Generic.IEnumerable)' and 'GridMvc.Html.GridExtensions.Grid(System.Web.Mvc.HtmlHelper, System.Collections.Generic.IEnumerable)'
This should be a simple fix. All I need to do is tell the old grids which one they are. But i'm not figuring out the syntax. In the view's, they all have a #using which points to the correct (old) version.
#using GridMvc.Html
....
#Html.Grid(Model.Leads).Named("userGrid").Selectable(false).Columns(c =>
{
....
}
A pic might be more helpful:
I've tried various means of a full path in the view, but the syntax is never right...
#GridMvc.Html.GridExtensions.Grid(Model.Leads).... // Nope
#Html.GridMvc.Html.GridExtensions.Grid(Model.Leads).... // Nope
etc etc etc.
Again, this is probably simple. I'm just not getting it.
Try passing the HtmlHelper instance as first argument to the extension method:
#NonFactors.Mvc.Grid.MvcGridExtensions.Grid(Html, Model.Leads)
#GridMvc.Html.GridExtensions.Grid(Html, Model.Leads)
Extension methods are callable as standard static methods, but just take the parent type as a first parameter, so you should be able to pass the HtmlHelper as so:
#GridMvc.Html.GridExtensions.Grid(Html, Model.Leads)
Disable one of the grid templates from the web.config
in the views folder.
-->

Categories