How To Implement Simple Command

This topic describes how to implement a two simple commands to be executed via the RunCmd.exe command-line utility. The commands made elementary manipulations with data of the XAF-application RunCmdExample.Win.

The complete sample project is available at RunCmdExample.zip. It includes BO business class and Updater populates the table with some initial data. The application is equipped with security system DevExpress.ExpressApp.Security.SecurityStrategyComplex and authentication mechanism DevExpress.ExpressApp.Security.AuthenticationActiveDirectory.

run_cmd_ht_1

To become familiar with the idea of RunCmd.exe and commands, follow the steps described below.

Commands Implementation

Add a new XAF Module Project to the solution and name it CommandModule.

Note:

In general, it does not necessarily have to add a new module for the command, the command can be implemented in any application module. Since the CommandModule is designed for console commands exclusively, it is better to remove the extra folders and files for convenience.

For the CommandModule project, invoke the Module Designer and add RunCmdExample.Module to the Required Modules section. Add DevExpress.ExpressApp.Security vXX.X to the References. This is required to provide access to the BO objects and security system when implementing the commands.

The next step is to add commands classes to the project. Add a new folder and name it Commands. Then, invoke the Add New Item dialog and select the Xafari vXX.X.XXXX Command template in the right pane, set Name to the "SecurityInformation" and click OK.

run_cmd_ht_2

Add the DataInformation class in the same way. Required references will be added automatically.

The figure below shows the CommandModule project after the described steps.

run_cmd_ht_3

In the Solution Explorer, right-click the SecurityInformation.cs file and replace the autogenerated file content with the following code:

  • c#
  • VB

public class SecurityInformation : CommandExtIOBase
{
  public override string Description
  {
    get
    {
      return "SInfo command shows information about Roles and Users from DataBase";
    }
  }
  protected override void ExecuteCore(IDictionary<string, object> parameters)
  {
    var parameterValue = parameters["Param"].ToString();
    if (parameterValue.Equals("Users", StringComparison.OrdinalIgnoreCase))
      DisplayUsers();
    else if (parameterValue.Equals("Roles", StringComparison.OrdinalIgnoreCase))
      DisplayRoles();
    else
      Out.WriteLine("Invalid parameter value!");
  }
  private void DisplayRoles()
  {
    Out.WriteLine("\nRoles:");
    var roles = Application.CreateObjectSpace().GetObjects<SecuritySystemRole>();
    foreach (var role in roles)
      Out.WriteLine(" {0}", role.Name);
  }
  private void DisplayUsers()
  {
    Out.WriteLine("\nUsers:");
    var users = Application.CreateObjectSpace().GetObjects<SecuritySystemUser>();
    foreach (var user in users)
      Out.WriteLine(" {0}", user.UserName);
  }
  public override string Name
  {
    get
    {
      return "SInfo";
    }
  }
  public override IEnumerable<CommandParameterDescription> ParametersDescription
  {
    get
    {
      yield return new CommandParameterDescription("Param", "<Users>, <Role>", "Show information about Roles and Users from DB.");
    }
  }
  public override void ValidateParameters(IDictionary<string, object> parameters)
  {
    base.ValidateParameters(parameters);
    var tableName = parameters["param"].ToString();
    if (!tableName.Equals("Users", StringComparison.OrdinalIgnoreCase) && !tableName.Equals("Roles", StringComparison.OrdinalIgnoreCase))
    {
      throw new ArgumentException(string.Format("Invalid parameter value Param:\"{0}\"", tableName));
    }
  }
  private IObjectSpace objectSpace { get; set; }
}

Public Class SecurityInformation
  Inherits CommandExtIOBase
  Public Overrides ReadOnly Property Description As String
    Get
      Return "SInfo command shows information about Roles and Users from DataBase"
    End Get
  End Property
  Protected Overrides Sub ExecuteCore(ByVal parameters As IDictionary(Of String, Object))
    Dim parameterValue = parameters("Param").ToString()
    If parameterValue.Equals("Users", StringComparison.OrdinalIgnoreCase) Then
      DisplayUsers()
    Else
      If parameterValue.Equals("Roles", StringComparison.OrdinalIgnoreCase) Then
        DisplayRoles()
      Else
        Out.WriteLine("Invalid parameter value!")
      End If
    End If
  End Sub
  Private Sub DisplayRoles()
    Out.WriteLine("" + vbLf + "Roles:")
    Dim roles = Application.CreateObjectSpace().GetObjects(Of SecuritySystemRole)()
    For Each role In roles
      Out.WriteLine(" {0}", role.Name)
    Next
  End Sub
  Private Sub DisplayUsers()
    Out.WriteLine("" + vbLf + "Users:")
    Dim users = Application.CreateObjectSpace().GetObjects(Of SecuritySystemUser)()
    For Each user In users
      Out.WriteLine(" {0}", user.UserName)
    Next
  End Sub
  Public Overrides ReadOnly Property Name As String
    Get
      Return "SInfo"
    End Get
  End Property
  Public Overrides ReadOnly Property ParametersDescription As IEnumerable(Of CommandParameterDescription)
    Get
    End Get
  End Property
  Public Overrides Sub ValidateParameters(ByVal parameters As IDictionary(Of String, Object))
    MyBase.ValidateParameters(parameters)
    Dim tableName = parameters("param").ToString()
    If Not tableName.Equals("Users", StringComparison.OrdinalIgnoreCase) AndAlso Not tableName.Equals("Roles", StringComparison.OrdinalIgnoreCase) Then
      Throw New ArgumentException(String.Format("Invalid parameter value Param:" + Chr(34) + "{0}" + Chr(34) + "", tableName))
    End If
  End Sub
  Private Property objectSpace As IObjectSpace
End Class

SecurityInformation command depends on one parameter Param, if its value is "Users", the console window will display a list of users from the application database. If its value is "Roles", it will return a list of roles. The execution of command and the results are described below.

In the Solution Explorer, right-click the DataInformation.cs file and replace the autogenerated file content with the following code:

  • c#
  • VB

public class DataInformation : CommandExtIOBase
{
  public override string Description
  {
    get
    {
      return "DInfo command shows information about BO object from DataBase";
    }
  }
  protected override void ExecuteCore(IDictionary<string, object> parameters)
  {
    var parameterValue = parameters["Column"].ToString();
    if (parameterValue.Equals("Name", StringComparison.OrdinalIgnoreCase))
      DisplayNameColumn();
    else if (parameterValue.Equals("Description", StringComparison.OrdinalIgnoreCase))
      DisplayDescriptionColumn();
    else if (parameterValue.Equals("All", StringComparison.OrdinalIgnoreCase))
      DisplayAllColumns();
    else
      Out.WriteLine("Invalid parameter value!");
  }
  private void DisplayAllColumns()
  {
    if (objectSpace == null)
      objectSpace = Application.CreateObjectSpace();
    var businesObject = objectSpace.GetObjects<BO>();
    Out.WriteLine("\n{0,-20} {1}", "Name:", "Description:");
    foreach (var obj in businesObject)
      Out.WriteLine(" {0,-20} {1}", obj.Name, obj.Description);
  }
  private void DisplayDescriptionColumn()
  {
    if (objectSpace == null)
      objectSpace = Application.CreateObjectSpace();
    var businesObject = objectSpace.GetObjects<BO>();
    Out.WriteLine("\nDescription:");
    foreach (var obj in businesObject)
      Out.WriteLine(obj.Description);
  }
  private void DisplayNameColumn()
  {
    if (objectSpace == null)
      objectSpace = Application.CreateObjectSpace();
    var businesObject = objectSpace.GetObjects<BO>();
    Out.WriteLine("\nName:");
    foreach (var obj in businesObject)
      Out.WriteLine(obj.Name);
  }
  public override string Name
  {
    get
    {
      return "DInfo";
    }
  }
  public override IEnumerable<CommandParameterDescription> ParametersDescription
  {
    get
    {
      yield return new CommandParameterDescription("Column", "<Name>, <Description>, <All>", "Show content of BO object current column from DB.");
    }
  }
  public override void ValidateParameters(IDictionary<string, object> parameters)
  {
    base.ValidateParameters(parameters);
    var tableName = parameters["Column"].ToString();
    if (!tableName.Equals("Name", StringComparison.OrdinalIgnoreCase) && !tableName.Equals("Description", StringComparison.OrdinalIgnoreCase) && !tableName.Equals("All", StringComparison.OrdinalIgnoreCase))
    {
      throw new ArgumentException(string.Format("Invalid parameter value Column:\"{0}\"", tableName));
    }
  }
  private IObjectSpace objectSpace { get; set; }
}

Public Class DataInformation
  Inherits CommandExtIOBase
  Public Overrides ReadOnly Property Description As String
    Get
      Return "DInfo command shows information about BO object from DataBase"
    End Get
  End Property
  Protected Overrides Sub ExecuteCore(ByVal parameters As IDictionary(Of String, Object))
    Dim parameterValue = parameters("Column").ToString()
    If parameterValue.Equals("Name", StringComparison.OrdinalIgnoreCase) Then
      DisplayNameColumn()
    Else
      If parameterValue.Equals("Description", StringComparison.OrdinalIgnoreCase) Then
        DisplayDescriptionColumn()
      Else
        If parameterValue.Equals("All", StringComparison.OrdinalIgnoreCase) Then
          DisplayAllColumns()
        Else
          Out.WriteLine("Invalid parameter value!")
        End If
      End If
    End If
  End Sub
  Private Sub DisplayAllColumns()
    If objectSpace Is Nothing Then
      objectSpace = Application.CreateObjectSpace()
    End If
    Dim businesObject = objectSpace.GetObjects(Of BO)()
    Out.WriteLine("" + vbLf + "{0,-20} {1}", "Name:", "Description:")
    For Each obj In businesObject
      Out.WriteLine(" {0,-20} {1}", obj.Name, obj.Description)
    Next
  End Sub
  Private Sub DisplayDescriptionColumn()
    If objectSpace Is Nothing Then
      objectSpace = Application.CreateObjectSpace()
    End If
    Dim businesObject = objectSpace.GetObjects(Of BO)()
    Out.WriteLine("" + vbLf + "Description:")
    For Each obj In businesObject
      Out.WriteLine(obj.Description)
    Next
  End Sub
  Private Sub DisplayNameColumn()
    If objectSpace Is Nothing Then
      objectSpace = Application.CreateObjectSpace()
    End If
    Dim businesObject = objectSpace.GetObjects(Of BO)()
    Out.WriteLine("" + vbLf + "Name:")
    For Each obj In businesObject
      Out.WriteLine(obj.Name)
    Next
  End Sub
  Public Overrides ReadOnly Property Name As String
    Get
      Return "DInfo"
    End Get
  End Property
  Public Overrides ReadOnly Property ParametersDescription As IEnumerable(Of CommandParameterDescription)
    Get
    End Get
  End Property
  Public Overrides Sub ValidateParameters(ByVal parameters As IDictionary(Of String, Object))
    MyBase.ValidateParameters(parameters)
    Dim tableName = parameters("Column").ToString()
    If Not tableName.Equals("Name", StringComparison.OrdinalIgnoreCase) AndAlso Not tableName.Equals("Description", StringComparison.OrdinalIgnoreCase) AndAlso Not tableName.Equals("All", StringComparison.OrdinalIgnoreCase) Then
      Throw New ArgumentException(String.Format("Invalid parameter value Column:" + Chr(34) + "{0}" + Chr(34) + "", tableName))
    End If
  End Sub
  Private Property objectSpace As IObjectSpace
End Class

This command has one parameter a Column which can be equal to "Name", "Description" or "All". These values determine which fields of the objects will be extracted from the table and displayed in the console window.

The next step is to modify the CommandsModuleModule class to implement the ICommandEnumerator interface. This is to make it possible to refer to the module’s commands from RunCmd.exe. The code below shows how to implement ICommandEnumerator interface:

  • c#
  • VB

public sealed partial class CommandsModuleModule : ModuleBase, ICommandEnumerator
{
  //...
  public IEnumerable<ICommand> commands
  {
    get
    {
      yield return new SecurityInformation();
      yield return new DataInformation();
    }
  }
}

Public Partial NotInheritable Class CommandsModuleModule
  Inherits ModuleBase
  Implements ICommandEnumerator
  '...
  Public ReadOnly Property commands As IEnumerable(Of ICommand)
    Get
    End Get
  End Property
End Class

Now, the commands are defined correctly and you only need to build the CommandsModule project.

RunCmd Configuration

To execute the commands in the context of an XAF application, you must use command-line utility RunCmd.exe. By default, the RunCmd.exe is installed in c:\Program Files (x86)\Galaktika\Xafari Framework vXX.X.XXXX\Tools\RunCmd\ folder. The easiest way to use a console application is to host it in the same directory with the required assemblies. Therefore, copy RunCmd.exe and RunCmd.exe.config files to the …\CommandModule\bin\debug folder of the solution.

run_cmd_ht_4

The next step is to configure RunCmd.exe for proper operating with the main application, in particular, to define the interaction with the authentication mechanism and security system. For this we need to edit the RunCmd.exe.config file.

For detailed information about RunCmd.exe settings, see the Console Application. RunCmd Utility topic.

In this example, you will need to configure the following sections:

  • In the <securityConfiguration> section, set the required property securityAssemblies that lists all the required assemblies with the types of Security System. If necessary, specify optional properties roleTypeName and userTypeName.
  • Configure the <authentificationClass> subsection. This information should be enough that the utility has created an instance of the logon-parameters class, set its properties and then use this instance for authentication.
  • In the <appSettings> section, specify the commandsModule module in Modules key.
  • In the <connectionStrings> section, specify the same connection string to the database as the main application.
  • In the <applicationSettings> section, set DbApplicationName parameter. Set the name of the main app, this name is used when checking the compatibility with the database.

The snippet below shows RunCmd.exe.config settings for the described scenario:

  • xml

<?xml version="1.0"?>
 <configuration>
  <configSections>
     <section name="securityConfiguration" type="Xafari.Tools.RunCmd.SecurityConfiguration, RunCmd"/>

     <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
      <section name="Xafari.Tools.RunCmd.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
     </sectionGroup>
  </configSections>

  <securityConfiguration
     securityAssemblies="DevExpress.ExpressApp.Security.v16.1; DevExpress.Persistent.BaseImpl.v16.1"
     roleTypeName="DevExpress.ExpressApp.Security.Strategy.SecuritySystemRole"
     userTypeName="DevExpress.ExpressApp.Security.Strategy.SecuritySystemUser">

     <authentificationClass typeName="DevExpress.ExpressApp.Security.AuthenticationActiveDirectory">
      <properties>
         <add name="CreateUserAutomatically" value="true"/>
         <add name="LogonParametersType" value="DevExpress.ExpressApp.Security.AuthenticationStandardLogonParameters"/>
         <add name="UserType" value="DevExpress.Persistent.BaseImpl.SimpleUser"/>
      </properties>
     </authentificationClass>
  </securityConfiguration>

  <appSettings>

     <add key="Modules" value="
 CommandModule
     " />

     <add key="ClientSettingsProvider.ServiceUri" value=""/>

  </appSettings>

  <connectionStrings>
  <add name="ConnectionString" connectionString="Integrated Security=SSPI;Pooling=false;Data Source=(localdb)\v11.0;Initial Catalog=RunCmdExample" />
  </connectionStrings>

  <applicationSettings>
     <Xafari.Tools.RunCmd.Properties.Settings>
      <setting name="DbApplicationName" serializeAs="String">
         <value>RunCmdExample</value>
      </setting>
     </Xafari.Tools.RunCmd.Properties.Settings>
  </applicationSettings>
  <system.diagnostics>
     <switches>
      <!-- Use the one of predefined values: 0-Off, 1-Errors, 2-Warnings, 3-Info, 4-Verbose. The default value is 3. -->
      <add name="eXpressAppFramework" value="4" />
     </switches>
  </system.diagnostics>
 </configuration>

<system.diagnostics> section specifies that the results of the work RunCmd.exe will be written to a log file.

Commands Execution

To execute commands, it is necessary to run RunCmd.exe at the command prompt and pass the appropriate parameters. Instead of typing the text in the console window directly, it is more convenient to use the bat-file.

Add Run.bat file to the CommandModule\bin\debug folder and write scripts as follows:

  • example

@echo.
 RunCmd.exe /dbupdate silent /h /cmdlist
 @echo.
 RunCmd.exe @sinfo.txt
 @echo.
 RunCmd.exe /batch dinfo.txt

This file starts RunCmd.exe and pass parameters in three different ways.

The first method is the classic call from the console.

The /dbupdate silent key checks the compatibility with the database and, if necessary, performs the update. This is the case, if there is a mismatch with the database.

The /h key allows to see the help information on RunCmd.exe.

The /cmdlist key displays a list of commands available in the current configuration.

run_cmd_ht_5

The second method starts RunCmd.exe with those arguments that are predefined in the sinfo.txt file. This requires to add sinfo.txt file to the folder and specify keys as follows:

  • example

/paramlist SInfo /c SInfo Param:Users /c SInfo Param:Roles

The /paramlist key shows the parameters description for the specified command SInfo.

The /c key executes the SInfo command with the specified parameter values.

The third method allows to run a sequence of commands from the dinfo.txt file. This requires to add sinfo.txt file to the folder and specify keys as follows:

  • example

DInfo Column:All
 DInfo Column:Name
 DInfo Column:Description

run_cmd_ht_6

Each command is listed in a separate row. The DInfo command will be executed 3 times, obtaining different values of the Column parameter.

To learn more about the RunCmd.exe keys, refer to the Console Application. RunCmd Utility topic.

After all of the required files are added and populated, you can double-click on the batch file and see the result.