Extension: .netstandard - quandis/qbo3-Documentation GitHub Wiki

Overview

Components designed to work with .netstandard, including use of IConfiguration, IServiceProvider and ILogger can be incorporated into qbo3.

Registering configuration and services

While developers may not modify a Startup.cs class as they would in a traditional .netcore application, qbo exposes an ApplicationStartup class that exposes IConfiguration, IServiceProvider to calling code.

To inject configuration data and services into ApplicationStartup:

  • include the qbo4.Configuration.HttpApplication Nuget package in your project
  • add configuration by creating an extension method tagged with a ConfigurationMethod attribute:
[ConfigurationMethod]
public static IConfigurationBuilder AddMyServices(this IConfigurationBuilder builder)
{
    builder.AddJsonObject(new { Foo = "Bar" });
    return builder;
}
  • add services by creating an extension method tagged with a ServiceCollectionMethod attribute:
[ServiceCollectionMethod]
public static IServiceCollection AddMyServices(this IServiceCollection services, IConfiguration configuration)
{
    var providers = new List<string>();
    configuration.GetSection("MyServices").Bind(providers);
    foreach (var name in providers)
    {
        services.AddSingleton<MyService>(sp => { return new MyService(name); });
    }
    return services;
}

Middleware in qbo3

While the qbo3 stack is still running on traditional ASP.NET, it's straight forward to port Middleware to a qbo3 BaseHandler. For example, the qbo4.Document module includes a ConvertMiddleware to handle document conversion:

public async Task Invoke(HttpContext context, Transformers transformers)
{
    _transformers = transformers;
    var toMimeType = (context.Request.Headers.ContainsKey("Accept")) ? context.Request.Headers["Accept"].ToString() : throw new ArgumentNullException("Accept", "You must specify an Accept header with the mime type you wish to convert to.");
    using (var cache = new CacheStream())
    { 
      // ... omitted for brevity...
    }
}

To port this to a BaseHandler:

public override async Task ProcessRequestAsync(HttpContext context)
{
    // Dependency injection via ApplicationStartup
    var _transformers = ApplicationStartup.Instance.Services.GetRequiredService<Transformers>();

    var toMimeType = (context.Request.Headers.ContainsKey("Accept")) ? context.Request.Headers["Accept"].ToString() : throw new ArgumentNullException("Accept", "You must specify an Accept header with the mime type you wish to convert to.");
    var fromMimeType = context.Request.ContentType;
    using (var cache = new CacheStream())
    { 
      // ... omitted for brevity...
    }
}

One your custom handler is deployed, create a custom route to tell qbo3 to route requests to it:

<ConfigurationEntryCollection>
  <ConfigurationEntryItem>
    <ConfigurationEntry>CustomRoutes/qbo4_document_convert</ConfigurationEntry>
    <Source>CustomRoutes.config</Source>
    <ConfigurationType>qbo.Application.Configuration.CustomRouteCollection</ConfigurationType>
    <ConfigurationKey>qbo4_document_convert</ConfigurationKey>
    <ConfigurationXml>
      <CustomRoute Name="qbo4_document_convert" Url="api/qbo4/document/convert" Version="1" Handler="qbo4.Document.Bridge.ConvertHandler, qbo4.Document.Bridge" />
    </ConfigurationXml>
  </ConfigurationEntryItem>
</ConfigurationEntryCollection>

Extension methods for existing qbo3 objects

If you wish to extend existing qbo3 classes, simply write an extension method for them:

[DbVoidMethod(RequireStream = true)]
public static async Task ConvertAsync(this AttachmentObject attachment, IDictionary<string, object> parameters)
{
    var transformers = ApplicationStartup.Instance.Services.GetRequiredService<Transformers>();
    await attachment.SelectAsync(parameters);
    var toMimeType = (parameters.ContainsKey("Accept")) ? parameters["Accept"].ToString() 
        : throw new ArgumentNullException("Accept", "You must specify an Accept parameter with the mime type you wish to convert to.");
    var responseStream = (parameters.ContainsKey("ResponseStream")) ? parameters["ResponseStream"] as Stream 
        : throw new ArgumentNullException("ResponseStream", "No response stream is available in the parameters.");

    using (var cache = await attachment.CacheStreamAsync())
    {
        // ... omitted for brevity ...
    }
}

IServices vs Extension Methods and Custom Routes

There is a large overlap in the use cases around which one might choose to create an IService vs an extension method vs a custom route. Consider:

Technique Guidance
Extension Methods Create extension methods when the functionality is specific to a QBO module. Attachment.ViewHtmlAsync that converts a document to an HTML viewer requires a document.
Custom Routes Create custom routes when the functionality crosses multiple QBO models (or none). Single page applications are an excellent example of this.
IServices Create an IService when you need QBO to transform data into a request payload, or transform the response payload. IService has hooks for this which would be cumbersome to re-implement in code.
  • If the person develping
⚠️ **GitHub.com Fallback** ⚠️