Dependency Injections on Parameters

Use Dependency Injections to define and set CMS Function parameters of specified types automatically

When using CMS Functions in content and code editors in C1 CMS, you can set values of their parameters via the GUI – in the “Function Properties” window.

If a function comes with parameters set to default values, these parameters are not shown in the Basic view of the “Function Properties” window. You can, however, switch to the Advanced view where all the parameters are shown and thus override these default values.

You can specify default values in a function itself (for example, by setting the “DefaultValue” property on the [FunctionParameter] attribute in a Razor function), but you can also set a default value programmatically, via the API, by using dependency injections.

This functionality implements the Dependency Injection pattern. By passing some service via a function parameter, you can:

  1. change the service without changing the function,
  2. use different implementations in different environments (development / staging / production),
  3. reuse the same function across multiple websites with different service implementations,
  4. apply unit tests to the service.

As with the default values set in the function itself, you can always override the default values set via the API in the Advanced view of the “Function Properties” window.

Let’s take a look at how you can make use of dependency injections to set default values of CMS Function parameters.

Using dependency injections

You can set CMS Function parameters to default values via the API by implementing the “dependency injection” pattern. (Please see "Dependency injections in C1 CMS".)

In terms of CMS Functions, an input parameter will serve as a “client” that will be “injected” with a “service” you are supposed to define and register in advance

To set CMS Function parameters to default values using dependency injections:

  1. Define a service.
  2. Register the service.
  3. Use the registered service to define a parameter.

Defining a service

You can define your service in a custom DLL / class library (built and placed in /Bin).

To define a service:

  1. Define an interface.
  2. Declare a class that implements this interface.

In the following example, we'll define the interface IRandomNumberGenerator and implement it in the internal class RND.

public interface IRandomNumberGenerator
{
    int GetNext();
}

internal class RND : IRandomNumberGenerator
{
    readonly Random _random = new Random();
	
    public int GetNext()      
	{   
		return _random.Next(1000);
	}
}

Registering the service

To register your service, in your custom DLL make use of the [ApplicationStartup] attribute and add the optional parameter IServiceCollection serviceCollection to the initialization handlers:

[ApplicationStartup]
public static class Startup
{
    public static void OnBeforeInitialize(IServiceCollection serviceCollection)
    {
        serviceCollection.Add(ServiceDescriptor.Instance(typeof (IRandomNumberGenerator), new RND()));
    }

    public static void OnInitialized(IServiceCollection serviceCollection)
    {
        
    }
}

Using the service with a CMS Function parameter

The injected interfaces can be used by declaring a parameter of a registered type on a CMS Function.

In the following example, we'll use the interface in a Razor function to output a random number:

@inherits RazorFunction
@using ClassLibrary1;     

@functions {
    public override string FunctionDescription
    {
        get  { return "Shows a random integer value"; }
    }
     
    [FunctionParameter]
    public IRandomNumberGenerator RandomNumberGenerator { get; set; }

}

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://www.composite.net/ns/function/1.0">
    <head>
    </head>
    <body>
        <h1>Random value of the moment is: @RandomNumberGenerator.GetNext() </h1>
    </body>
</html>

Dependency injections in C1 CMS

C1 CMS uses Microsoft’s dependency injection library for this. It supports different “lifestyles” for the provider services as well as per-HTTP-request scopes.

(Please see "Dependency Injection in ASP.NET vNext".)

The public class Composite.Core.Application.ServiceLocator exposes properties to get / set IServiceCollection and IServiceProvider, used for dependency in function parameters.

The DI library also allows replacing the standard IServiceProvider with AutoFac, Ninject and other 3rd-party / custom DI containers if needed, by overwriting the value of the Composite.Core.Application.ServiceLocator.ApplicationServices property.

(Please see the section "Bring Your Own IoC Container" in "Dependency Injection In ASP.NET 5 – One Step Deeper".)

For using the locator, there are 2 public service providers available:

  • ServiceLocator.ApplicationServices – for global scopes
  • ServiceLocator.RequestServices – for per-HTTP-request scopes

Overriding default values

You can replace the value set with a dependency injection in the Advanced view of the Function Properties window by using a CMS Function that implements the interface and returns a different value.

In the following example, we'll use the C# function TestFunction to implement our interface IRandomNumberGenerator and return a hard-coded value.

public static IRandomNumberGenerator TestFunction()
{
    return new NotSoRandom();
}

internal class NotSoRandom : IRandomNumberGenerator
{
    public int GetNext()
    {
        return 1234;
    }
}

Requirements:

C1 CMS version 5.0 or later