Published by Marco on 27. Feb 2016 12:34:51
Updated by Marco on 27. Feb 2016 12:36:46
------------------------------------------------------------------------

In several articles last year [1], I went into a lot of detail about the
configuration and startup for Quino applications. Those posts discuss a lot
about what led to the architecture Quino has for loading up an application. 

[image]Some of you might be wondering: what if I want to start up and run an
application that doesn't use Quino? Can I build applications that don't use any
fancy metadata because they're super-simple and don't even need to store any
data? Those are the kind of utility applications I make all the time; do you
have anything for me, you continue to wonder?

As you probably suspected from the leading question: You're in luck. Any
functionality that doesn't need metadata is available to you without using any
of Quino. We call this the "Encodo" libraries, which are the base on which Quino
is built. Thanks to the fundamental changes made in Quino 2, you have a wealth
of functionality available in just the granularity you're looking for.

[Why use a Common Library?]

Instead of writing such small applications from scratch -- and we know we could
write them -- why would we want to leverage existing code? What are the
advantages of doing this?

  * Writing code that is out of scope takes time away from writing code that is
    in scope.
  * Code you never write has no bugs.
  * It also doesn't require maintenance or documentation.
  * While library code is not guaranteed to be bug-free, it's probably much
    better off than the code you just wrote seconds ago.
  * Using a library increases the likelihood of having robust, reliable and
    extendible code for out-of-scope application components. 
  * One-off applications tend to be maintainable only by the originator.
    Applications using a common library can be maintained by anyone familiar
    with that library.
  * Without a library, common mistakes must be fixed in all copies, once for
    each one-off application.
  * The application can benefit from bug fixes and improvements made to the
    library.
  * Good practices and patterns are encouraged/enforced by the library.

What are potential disadvantages?

  * The library might compel a level of complexity that makes it take longer to
    create the application than writing it from scratch
  * The library might force you to use components that you don't want.
  * The library might hamstring you, preventing innovation.

A developer unfamiliar with a library -- or one who is too impatient to read up
on it -- will feel these disadvantages more acutely and earlier.

[Two Sample Applications]

Let's take a look at some examples below to see how the Encodo/Quino libraries
stack up. Are we able to profit from the advantages without suffering from the
disadvantages?

We're going to take a look at two simple applications:

   1. An application that loads settings for Windows service-registration. We
      built this for a customer product.
   2. The Quino Code Generator that we use to generate metadata and ORM classes
      from the model

[Windows Service Installer]

The actual service-registration part is boilerplate generated by Microsoft
Visual Studio [2], but we'd like to replace the hard-coded strings with
customized data obtained from a configuration file. So how do we get that data?

  * The main requirement is that the user should be able to indicate which
    settings to use when registering the Windows service.
  * The utility could read them in from the command line, but it would be nicer
    to read them from a configuration file.

That doesn't sound that hard, right? I'm sure you could just whip something
together with an XMLDocument and some hard-coded paths and filenames that would
do the trick. [3] It might even work on the first try, too. But do you really
want to bother with all of that? Wouldn't you rather just get the scaffolding
for free and focus on the part where you load your settings?

[Getting the Settings]

The following listing shows the main application method, using the Encodo/Quino
framework libraries to do the heavy lifting.


[NotNull]
public static ServiceSettings LoadServiceSettings()
{
  ServiceSettings result = null;
  var transcript = new ApplicationManager().Run(
    CreateServiceConfigurationApplication,
    app => result = app.GetInstance()
  );

  if (transcript.ExitCode != ExitCodes.Ok)
  {
    throw new InvalidOperationException(
      "Could not read the service settings from the configuration file." + 
      new SimpleMessageFormatter().GetErrorDetails(transcript.Messages)
    );
  }

  return result;
}

If you've been following along in the other articles (see first footnote below),
then this structure should be very familiar. We use an ApplicationManager() to
execute the application logic, creating the application with
CreateServiceConfigurationApplication and returning the settings configured by
the application in the second parameter (the "run" action). If anything went
wrong, we get the details and throw an exception.

You can't see it, but the library provides debug/file logging (if you enable
it), debug/release mode support (exception-handling, etc.) and everything is
customizable/replaceable by registering with an IOC.

[Configuring the Settings Loader]

Soooo...I can see where we're returning the ServiceSettings, but where are they
configured? Let's take a look at the second method, the one that creates the
application.


private static IApplication CreateServiceConfigurationApplication()
{
  var application = new Application();
  application
    .UseSimpleInjector()
    .UseStandard()
    .UseConfigurationFile("service-settings.xml")
    .Configure(
      "service", 
      (settings, node) =>
      {
        settings.ServiceName = node.GetValue("name", settings.ServiceName);
        settings.DisplayName = node.GetValue("displayName",
settings.DisplayName);
        settings.Description = node.GetValue("description",
settings.Description);
        settings.Types = node.GetValue("types", settings.Types);
      }
    ).RegisterSingle();

  return application;
}

   1. First, we create a standard Application, defined in the Encodo.Application
      assembly. What does this class do? It does very little other than manage
      the main IOC (see articles linked in the first footnote for details).
   2. The next step is to choose an IOC, which we do by calling
      UseSimpleInjector(). Quino includes support for the SimpleInjector IOC out
      of the box. As you can see, you must include this support explicitly, so
      you're also free to assign your own IOC (e.g. one using Microsoft's
      Unity). SimpleInjector is very lightweight and super-fast, so there's no
      downside to using it.
   3. Now we have an application with an IOC that doesn't have any registrations
      on it. How do we get more functionality? By calling methods like
      UseStandard(), defined in the Encodo.Application.Standard assembly. Since
      I know that UseStandard() pulls in what I'm likely to need, I'll just use
      that. [4]
   4. The next line tells the application the name of the configuration file to
      use. [5]
   5. The very next line is already application-specific code, where we
      configure the ServiceSettings object that we want to return. For that,
      there's a Configure method that returns an object from the IOC along with
      a specific node from the configuration data. This method is called only if
      everything started up OK.
   6. The final call to RegisterSingle makes sure that the ServiceSettings
      object created by the IOC is a singleton (it would be silly to configure
      one instance and return another, unconfigured one).

Basically, because this application is so simple, it has already accomplished
its goal by the time the standard startup completes. At the point that we would
"run" this application, the ServiceSettings object is already configured and
ready for use. That's why, in LoadServiceSettings(), we can just get the
settings from the application with GetInstance() and exit immediately.

[Code Generator]

The code generator has a bit more code, but follows the same pattern as the
simple application above. In this case, we use the command line rather than the
configuration file to get user input.

[Execution]

The main method defers all functionality to the ApplicationManager, passing
along two methods, one to create the application, the other to run it.


internal static void Main()
{
  new ApplicationManager().Run(CreateApplication, GenerateCode);
}

[Configuration]

As before, we first create an Application, then choose the SimpleInjector and
some standard configuration and registrations with UseStandard(),
UseMetaStandardServices()  and UseMetaTools(). [6]

We set the application title to "Quino Code Generator" and then include objects
with UseSingle() that will be configured from the command line and used later in
the application. [7] And, finally, we add our own ICommandSet to the
command-line processor that will configure the input and output settings. We'll
take a look at that part next.


private static IApplication CreateApplication(
  IApplicationCreationSettings applicationCreationSettings)
{
  var application = new Application();

  return
    application
    .UseSimpleInjector()
    .UseStandard()
    .UseMetaStandardServices()
    .UseMetaTools()
    .UseTitle("Quino Code Generator")
    .UseSingle(new CodeGeneratorInputSettings())
    .UseSingle(new CodeGeneratorOutputSettings())
    .UseUnattendedCommand()
    .UseCommandSet(CreateGenerateCodeCommandSet(application))
    .UseConsole();
}

[Command-line Processing]

The final bit of the application configuration is to see how to add items to the
command-line processor.

Basically, each command set consists of required values, optional values and
zero or more switches that are considered part of a set.

The one for i simply sets the value of inputSettings.AssemblyFilename to
whatever was passed on the command line after that parameter. Note that it pulls
the inputSettings from the application to make sure that it sets the values on
the same singleton reference as will be used in the rest of the application.

The code below shows only one of the code-generator--specific command-line
options. [8]


private static ICommandSet CreateGenerateCodeCommandSet(
  IApplication application)
{
  var inputSettings = application.GetSingle();
  var outputSettings = application.GetSingle();

  return new CommandSet("Generate Code")
  {
    Required =
    {
      new OptionCommandDefinition
      {
        ShortName = "i",
        LongName = "in",
        Description = Resources.Program_ParseCommandLineArgumentIn,
        Action = value => inputSettings.AssemblyFilename = value
      },
      // And others...
    },
  };
}

[Code-generation]

Finally, let's take a look at the main program execution for the code generator.
It shouldn't surprise you too much to see that the logic consists mostly of
getting objects from the IOC and telling them to do stuff with each other. [9]

I've highlighted the code-generator--specific objects in the code below. All
other objects are standard library tools and interfaces.


private static void GenerateCode(IApplication application)
{
  var logger = application.GetLogger();
  var inputSettings = application.GetInstance();

  if (!inputSettings.TypeNames.Any())
  {
    logger.Log(Levels.Warning, "No types to generate.");
  }
  else
  {
    var modelLoader = application.GetInstance();
    var metaCodeGenerator = application.GetInstance();
    var outputSettings = application.GetInstance();
    var modelAssembly = AssemblyTools.LoadAssembly(
      inputSettings.AssemblyFilename, logger
    );

    outputSettings.AssemblyDetails = modelAssembly.GetDetails();

    foreach (var typeName in inputSettings.TypeNames)
    {
      metaCodeGenerator.GenerateCode(
        modelLoader.LoadModel(modelAssembly, typeName), 
        outputSettings,
        logger
      );
    }
  }
}

So that's basically it: no matter how simple or complex your application, you
configure it by indicating what stuff you want to use, then use all of that
stuff once the application has successfully started. The Encodo/Quino framework
provides a large amount of standard functionality. It's yours to use as you like
and you don't have to worry about building it yourself. Even your tiniest
application can benefit from sophisticated error-handling, command-line support,
configuration and logging without lifting a finger.

--------------------------------------------------------------------------------


[1] See Encodo’s configuration library for Quino "Part 1"
    , "Part 2"
     and "Part 3"
     as well as API
    Design: Running and Application "Part 1"
     and "Part 2"
     and, finally,
    "Starting up an application, in detail"
    .


[1] That boilerplate looks like this:
  var fileService = new ServiceInstaller();
   fileService.StartType = ServiceStartMode.Automatic;
   fileService.DisplayName = "Quino Sandbox";
   fileService.Description = "Demonstrates a Quino-based service.";
   fileService.ServiceName = "Sandbox.Services";
  
  See the ServiceInstaller.cs file in the Sandbox.Server project in Quino 2.1.2
  and higher for the full listing.


[1] The standard implementation of Quino's ITextKeyValueNodeReader supports XML,
    but it would be trivial to create and register a version that supports JSON
    ("QNO-4993" ) or YAML. The
    configuration file for the utility looks like this:
  
   
     
       Quino.Services
       Quino Utility
       The application to run all Quino backend
   services.
       All
     
   


[1] If you look at the implementation of the UseStandard method [10], it pulls
    in a lot of stuff, like support for BCrypt, enhanced CSV and enum-value
    parsing and standard configuration for various components (e.g. the file log
    and command line). It's called "Standard" because it's the stuff we tend to
    use in a lot of applications.
  
  But that method is just a composition of over a dozen other methods. If, for
  whatever reason (perhaps dependencies), you don't want all of that
  functionality, you can just call the subset of methods that you do want. For
  example, you could call UseApplication() from the Encodo.Application assembly
  instead. That method includes only the support for:
  
     * Processing the command line (ICommandSetManager)
     * Locating external files (ILocationManager)
     * Loading configuration data from file (IConfigurationDataLoader)
     * Debug- and file-based logging (IExternalLoggerFactory)
     * and interacting with the IApplicationManager.
  
  If you want to go even lower than that, you can try UseCore(), defined in the
  Encodo.Core assembly and then pick and choose the individual components
  yourself. Methods like UseApplication() and UseStandard() are tried and tested
  defaults, but you're free to configure your application however you want,
  pulling from the rich trove of features that Quino offers.


[1] By default, the application will look for this file next to the executable.
    You can configure this as well, by getting the location manager with
    GetLocationManager() and setting values on it.
  
  You'll notice that I didn't use Configure() for this
  particular usage. That's ordinarily the way to go if you want to make changes
  to a singleton before it is used. However, if you want to change where the
  application looks for configuration files, then you have to change the
  location manager before it's used any other configuration takes place. It's a
  special object that is available before the IOC has been fully configured. To
  reiterate from other articles (because it's important), the order of
  operations we're interested in here are:
  
      1. Create application (this is where you call Use*() to build the
         application)
      2. Get the location manager to figure out the path for
         LocationNames.Configuration
      3. Load the configuration file
      4. Execute all remaining actions, including those scheduled with calls to
         Configure()
  
  If you want to change the configuration-file location, then you have to get in
  there before the startup starts running -- and that's basically during
  application construction. Alternatively, you could also call
  UseConfigurationDataLoader() to register your own object to actually load
  configuration data and do whatever the heck you like in there, including
  returning constant data. :-)


[1] The metadata-specific analog to UseStandard() is UseMetaStandard(), but we
    don't call that. Instead, we call UseMetaStandardServices(). Why? The answer
    is that we want the code generator to be able to use some objects defined in
    Quino, but the code generator itself isn't a metadata-based application. We
    want to include the IOC registrations required by metadata-based
    applications without adding any of the startup or shutdown actions. Many of
    the standard Use*() methods included in the base libraries have analogs like
    this. The Use*Services() analogs are also very useful in automated tests,
    where you want to be able to create objects but don't want to add anything
    to the startup.


[1] Wait, why didn't we call RegisterSingle()? For almost any object, we could
    totally do that. But objects used during the first stage of application
    startup -- before the IOC is available -- must go in the other IOC, accessed
    with SetSingle() and GetSingle().


[1] The full listing is in Program.cs in the Quino.CodeGenerator project in any
    2.x version of Quino.


[1] Note that, once the application is started, you can use GetInstance()
    instead of GetSingle() because the IOC is now available and all singletons
    are mirrored from the startup IOC to the main IOC. In fact, once the
    application is started, it's recommended to use GetInstance() everywhere,
    for consistency and to prevent the subtle distinction between IOCs --
    present only in the first stage of startup -- from bleeding into your main
    program logic.


[1] If you have the Quino source code handy, you can look it up there, but if
    you have ReSharper installed, you can just F12 on UseStandard() to decompile
    the method. In the latest DotPeek, the extension methods are even displayed
    much more nicely in decompiled form.