Article Dependency Injection

If you use a Dependency Injection (DI) framework in your code, or want to use one with your Verint Community customizations it is certainly possible.  There are a few things to take into consideration when implementing it however, specifically around when you create your service bindings in your framework(sometimes also called a container or kernel depending on the framework) and when you can access or use the framework for injection.  Frameworks themselves are beyond the scope of this topic as there are many options.  You should research each type carefully to see if suits your needs and to see if it will work with your Verint Community.

Verint Community itself uses its own dependency injection framework, however it is not something developers can bind services against or use to retrieve services for themselves.  As a developer you are working with APIs and we provide a loader specifically for those.  Each API in the Developer Documentation shows how to load a core API using this service. Additionally you can choose to register your own Custom APIs.

Binding Your Services

In most cases you initialize your DI framework when the application starts, however you do not have access to the application startup process within Verint Community.  Instead you can attempt to bind your services during plugin initialization which works for small customizations but can be unreliable if you wanted to use your container in other plugins during initialization since there is no guarantee on what order a plugins will initialize in.  Instead its best to use  a combination of a self initializing wrapper and the initialize event.  This will initialize your bindings on plugin initialization or the first time you access your wrapper.

Service Binding Wrapper

This example creates a simple static class called ServiceLocator.  Static is optional, if you wish to not have a static class or the framework you use would not work well with a static class, that is fine.  For the purposes of demonstration only it uses a fake DI framework called DependencyInjector.  You would substitute your DI framework  binding code as is required.   This class only exposes a single method and that returns the DI framework container(this may have different names depending on the framework but is essentially the container where you can retrieve your bindings from).  This way we can now interact with our DI framework directly.   What is important is the fact we wrap it in a property that always calls EnsureInitialized().  This method determines if all the services have been bound and if not it binds them.  It determines if initialization has run by checking to see if an equivalent container has been created.

namespace Samples
{
    public static class ServiceLocator
    {
        private static DependencyInjectorContainer _injector = null;
        private static readonly object _lock = new object();

        public static DependencyInjectorContainer Service
        {
            get
            {
                EnsureInitialized(); //Make sure services are bound
                return _injector;
            }
        }

        public static void EnsureInitialized()
        {
            if (_injector != null)
                return; //Nothing to do

            lock (_lock)
            {
                if (_injector != null)
                    return; // Nothing to do
                    
                //Create your container for your DI framework
                var injector = new DependencyInjectorContainer();

                //Replace this with whatever your DI framework binding logic is.
                injector.AddBindingFor<ISampleService>(new SampleService());
                
                _injector = injector;
            }
        }
    }
}

Whenever you access your DI framework from the property of the wrapper class it will automatically ensure your services are bound correctly.  You can in turn force your services to bind if you choose by calling ServiceLocator.EnsureInitialized().  This could be done in the Initialize() method of a custom plugin.

Loading Your Service

Getting a service from your DI framework will depend on how this is done in an individual framework.  Because we are simply exposing your DI framework container, once you have received a reference to it you interact with it like you would any other time.   The benefit however is that this will invoke the binding process if it is necessary.

var service = ServiceLocator.Service.{Service Loading Method}(...);

Dependency Injection and Plugins

You can use DI with any custom service you write if its written in a way that works with whatever framework you chose.   Plugins work differently and while you can still utilize DI to an extent there are some limitations.

First you should not add a class implementing any derivative of IPlugin to your DI framework bindings for 2 reasons.  First, plugins are loaded automatically and stored in memory by the Verint Community application.  They would not be read from your DI container meaning you would have potentially 2 instances out of sync.  Second you can retrieve plugins from memory using the PluginManager and have no need to load them any other way.  If you have a service that is also a plugin, the best solution is separate them or leave it up to the plugin framework to manage.

You can't inject into plugins using traditional constructor injection.  Plugins themselves are reflected by the platform and require a default(parameterless) constructor.  If you were to try to include a non parameterless constructor on a plugin it would result in an exception whenever the Verint Community platform loads plugins.  You can however create separate constructors where your parameterless constructor invokes the injectable constructor using the service locator wrapper class.  This allows the plugin to load but you can still maintain a way to inject services if needed.

 public class SamplePlugin : IPlugin
    {
        private ISampleService _sampleService = null;
        public SamplePlugin(ISampleService sampleService)
        {
            _sampleService = sampleService;
        }

        //This calls our service locator
        public SamplePlugin():this(ServiceLocator.Service.GetBindingFor<ISampleService>())
        {
            
        }
        //... Plugin members left out for brevity
       
    }