-
Notifications
You must be signed in to change notification settings - Fork 0
Use objects and structs as the type of the argument
You can define arguments of any type, including custom objects.
In this page you will see how to
- use existing .net objects as argument
- use the
CustomInputString
attribute - create a custom object and use it as argument
- implememt it throught an example
InterAppConnector provides the possibility to use some object types that already exists in .net without writing custom code.
In particulr, the library checks if there is a constructor that accepts only one argument of type string
. If the constructor exists, the value assigned to the argument is passed to the constructor. Some examples of types that you can use without writing custom code are Guid
and DateTime
.
Let's assume that you want to define an argument that should be a valid GUID. All you have to do in the code is to define a property of type Guid
, as showed in the code below
public Guid Uid {get; set;}
Guid
has already a constructor that accepts a string as paramter and throws an exception if the string is not a valid GUID
If the object raises an exception, InterAppConnector consider the value passed to the argument not valid and will raise an error.
Is it also possible to check if the value respects a particular format. This is very useful if the data may vary according to user settings.
An example is the date an time validation, where the data format vary between countres to and you want to define a standard one.
In this case, you can add the CustomInputString
attribute to the property.
This attribute accepts a string as parameter, that is the format you want to use for that particular argument
Let's assume that you want to define an argument that should be a valid date. Define your argument as written in the code below
[CustomInputString("ddMMyyyy")]
public DateTime Date {get; set;}
When you define a custom input string as argument, the library will search for a method called ParseExact
that accepts three parameters:
- the value of the argument
- the format
- the format provider
and returns the object.
Then, the library pass the value of the argument as first parameter, the format defined as string in the CustomInputString
as second parameter and set the last one to CultureInfo.InvariantCulture
. The last parameter is hardcoded, and actually it is not possible to change it
In custom objects, you have to define your own ParseExact
method.
For instance, if you want to validate the value inserted as license plate you have to add a method in your class as described below (let's assume that the class name is LicensePlate
)
public class LicensePlate
{
public static LicensePlate ParseExact(string value, string format, IFormatProvider culture)
{
// write your validation code here
}
}
Previously, it is said that the library checks for a constructor or a specific method in order to pass the parameter to the object.
However, it is possible to define a custom method that can be used in alternative to the constructor.
In order to use this method, you have to add the CustomInputString
attribute to your method. There should be only one argument of type string
and you can choose the name of the method. Remember also that the method should be static and must return itself
There should be only one method with the CustomInputString
attribute in your class, otherwise the library will raise a DuplicateObjectException
exception.
For example, consider the following code:
public class Example
{
public int Number {get; set;}
public Example (string number)
{
Number = 5;
}
[CustomInputString]
public static Example SetCustomNumber (string number)
{
Example example = new Example();
example.Number = 15;
return this;
}
}
If you want to define a property called UserNumber
of type Example
and use it as argument of your application, the value assigned to Number
will be 15. If you delete the CustomInputString
attribute from the SetCustomNumber
method the value assigned to Number
will be 5
You can also add your ParseExact
method if you want to parse your string in the same way described above
It is also possible to define optional and mandatory arguments. However there is a slight difference respect to other types. InterAppConnector consider nullable objects as optional arguments. So if the type is passed by value (for instance structures, numbers, booleans and so on) is nullable, the argument is optional, otherwise it is mandatory. For example if you want to define as arguments two numbers, one mandatory an one optional you have to write the following code:
public int MandatoryNumber {get; set;}
public int? OptionalNumber {get; set;}
The first argument, mandatorynumber
is mandatory because the property MandatoryNumber
does not accept null
values, while the last one does, so it is optional.
Object types are passed by reference, so they can accept null
values. For this reason it is not possible to use the method described above.
Instead, if you want to define a mandatory argument, you have to assign a value to the respective property, otherwise it is considered as optional.
For example if you want to define as arguments two numbers, one mandatory an one optional you have to write the following code:
public string MandatoryString {get; set;} = string.Empty;
public string OptionalString {get; set;}
The first argument, mandatorystring
is mandatory because the property MandatoryString
is not null
, while the last one has its default value, null
, so it is optional.
In this example, you are creating a simple user management application that has two commands
CreateUserCommand
DeleteUserCommand
These commands have two arguments:
- usercode : contains the user code. This argument is mandatory for both commands
- name : contains the user name. This argument is mandatory in user creation and mandatory in user deletion.
For this example you are using Visual Studio 2022 and you are creating:
- A project library that contains the commands and a file containing the arguments necessary to run the application
- A console application that will be used to call the command via cli
For this example it is assumed that you have learned what is a command and how to implement it
Follow these steps to complete this example:
-
Open Visual Studio, create a blank solution and name it UserManager. Choose the language version (.NET 6 or above) and choose a location for your solution (for example C:\SampleProjects)
-
Create a new library project and call it UserManager.Library. Then follow the steps below to complete the class library
-
Add the InterAppConnector library as project reference
-
Add a file to the project and call it UserManagerDataModel.cs
-
Replace the content of UserManagerDataModel.cs with the code below
namespace UserManager.Library { public class UserManagerDataModel { public string UserCode { get; set; } = string.Empty; public string FileLocation { get; set; } public string Name { get; set; } } }
-
Rename Class1.cs to CreateUserCommand.cs and create two new files called DeleteUserCommand.cs and LicensePlate.cs. Change the visibility in the classes from internal to public
-
In LicensePlate.cs, replate the content of the file with the code below
using System.Text.RegularExpressions; namespace UserManager.Library { public class LicensePlate { private string _plate = ""; public string Plate { get { return _plate; } } public LicensePlate(string plate) { _plate = plate; } public static LicensePlate ParseExact(string value, string format, IFormatProvider culture) { LicensePlate plate; string licensePlate = ""; if (format.Length == value.Length) { string loweredFormat = format.ToLower(); for (int i = 0; i < value.Length; i++) { switch (loweredFormat[i]) { case 'l': if (Regex.IsMatch("" + value[i], @"[a-zA-Z]")) { licensePlate += value[i]; } else { throw new FormatException("Character " + i + ": Wrong character '" + value[i] + "'. It should be a letter"); } break; case 'n': if (Regex.IsMatch("" + value[i], @"[0-9]")) { licensePlate += value[i]; } else { throw new FormatException("Character " + i + ": Wrong character '" + value[i] + "'. It should be a number"); } break; case 'x': if (Regex.IsMatch("" + value[i], @"[a-zA-Z0-9]")) { licensePlate += value[i]; } else { throw new FormatException("Character " + i + ": Wrong character '" + value[i] + ". It should be an alphanumeric character"); } break; default: throw new FormatException("Character " + i + ": Unrecognised character in format. The allowed characters are l for letters, n for numbers and x for alphanumerical characters"); } } } else { throw new FormatException("Value and format must have the same length"); } plate = new(licensePlate); return plate; } } }
-
Replace the content of UserManagerDataModel.cs with the code below
using InterAppConnector.Attributes; namespace UserManager.Library { public class UserManagerDataModel { public Guid UserCode { get; set; } = Guid.NewGuid(); public FileInfo FileLocation { get; set; } = new FileInfo(@".\"); [MandatoryForCommand(typeof(CreateUserCommand))] [CustomInputString("lllnnnn")] public LicensePlate LicensePlate { get; set; } } }
-
Replace the content of CreateUserCommand.cs with the code below
using InterAppConnector; using InterAppConnector.Attributes; using InterAppConnector.Interfaces; using System.Text.RegularExpressions; namespace UserManager.Library { [Command("create", Description = "Create a user in a file")] public class CreateUserCommand : ICommand<UserManagerDataModel> { public string Main(UserManagerDataModel arguments) { string message = ""; bool userAlreadyExists = false; if (arguments.FileLocation.Exists) { List<string> userList = new List<string>(File.ReadLines(arguments.FileLocation.FullName)); int rowsFound = (from item in userList where item.ToLower().StartsWith(arguments.UserCode.ToString() + ",") select item).Count(); if (rowsFound > 0) { userAlreadyExists = true; } } if (userAlreadyExists) { message = CommandOutput.Warning("The user code already exists. No row was added"); } else { File.AppendAllText(arguments.FileLocation.FullName, arguments.UserCode.ToString() + "," + arguments.LicensePlate.Plate + Environment.NewLine); message = CommandOutput.Ok("The user was added to the list"); } return message; } } }
-
Replace the content of DeleteUserCommand.cs with the code below
using InterAppConnector; using InterAppConnector.Attributes; using InterAppConnector.Interfaces; using System.Text.RegularExpressions; namespace UserManager.Library { [Command("delete", Description = "Delete a user in a file")] public class DeleteUserCommand : ICommand<UserManagerDataModel> { public string Main(UserManagerDataModel arguments) { string message = ""; if (Regex.IsMatch(arguments.UserCode.ToString(), @"^[a-z0-9-]+$", RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(1000))) { if (arguments.FileLocation.Exists) { string name = ""; bool isNameValid = true; List<string> userList = new List<string>(File.ReadLines(arguments.FileLocation.FullName)); List<string> rowsFound = new List<string>(); if (!string.IsNullOrEmpty(arguments.LicensePlate.Plate)) { if (Regex.IsMatch(arguments.LicensePlate.Plate, @"^[a-z0-9]+$", RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(1000))) { name = arguments.LicensePlate.Plate; rowsFound = (from item in userList where item.ToLower() == arguments.UserCode.ToString().ToLower() + "," + name.ToLower() select item).ToList(); } else { isNameValid = false; } } else { rowsFound = (from item in userList where item.ToLower().StartsWith(arguments.UserCode.ToString().ToLower() + ",") select item).ToList(); } if (isNameValid) { if (rowsFound.Count > 0) { userList.Remove(rowsFound[0]); File.WriteAllLines(arguments.FileLocation.FullName, userList); message = CommandOutput.Ok("User deleted successfully"); } else { message = CommandOutput.Warning("The user does not exist. No rows was deleted"); } } else { message = CommandOutput.Error("The name contains invalid characters. Only letters and spaces are allowed"); } } else { message = CommandOutput.Error("The file does not exist"); } } else { message = CommandOutput.Error("The user code contains invalid characters. Only letters and numbers are allowed"); } return message; } } }
-
-
Create a new console application project and name it UserManager.CLI. Then follow the steps below to complete the console application:
-
Add the InterAppConnector library as project reference
-
Add the UserManager.Library project as project reference
-
Replace the content of Program.cs with the code below (
CommandManager
andInterAppConnector
are explained in detail in Integration with console applications page)using InterAppConnector; using UserManager.Library; namespace UserManager.CLI { public class Program { static void Main(string[] args) { CommandManager commandManager = new CommandManager(); commandManager.AddCommand<CreateUserCommand, UserManagerDataModel>() .AddCommand<DeleteUserCommand, UserManagerDataModel>(); InterAppCommunication interAppCommunication = new InterAppCommunication(commandManager); interAppCommunication.ExecuteAsInteractiveCLI(args); } } }
-
-
Build the solution (you can leave the debug mode if you want)
-
Open the Command Prompt and go to the location of the executable. For instance, if you have placed your solution in C:\SampleProjects you can go to the executable by typing the above command
cd C:\SampleProjects\UserManager\UserManager.CLI\bin\Debug\net6.0
-
Type the executable name with the extension (for instance UserManager.CLI.exe). You should see an output similar to the one below
---------------------------------------------------------------------------------- UserManager.CLI 1.0.0.0 ---------------------------------------------------------------------------------- Available actions: - create Create a user in a file -usercode (required) : No description provided -filelocation (required) : No description provided -licenseplate (required) : No description provided - delete Delete a user in a file -usercode (required) : No description provided -filelocation (required) : No description provided -licenseplate (optional) : No description provided
-
Create a new user and try to insert a wrong guid or file path. For instance, type UserManager.CLI.exe create -usercode xxx -location .\addresses.txt. You should see this error
[25/11/2023 16:29:06] ERROR (3) : The value provided to argument usercode is not acceptable. Reason: Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx). (Parameter 'usercode')
-
If you type all mandatory parameters and are in the correct format, the user will be added in the list
- Getting started
- Create Commands
- Manage arguments
- Argument basics
- Shared arguments between different commands
- Argument aliases
- Argument description
- Exclude an argument from argument list
- Use enumerations as argument
- Use objects and structs as argument type
- Validate argument value before command execution
- Customize the command example in command help
- Define rules