Architecture of ELMA core

ELMA core is based on the Inversion of Control (IoC) concept and uses Autofac container. ELMA uses it to register the main system components (e.g. managers and services, extension components, web controllers).

Lifecycle types

Each object registered in Autofac has a specific lifecycle:

namespace EleWise.ELMA.ComponentModel
{
 
    /// <summary>
    /// Lifecycle Type in Container
    /// </summary>
    public enum ServiceScope
    {
 
        /// <summary>
        /// One object per application
        /// </summary>
        Application = 0x1,
 
        /// <summary>
        /// One object per container (container is recreated when switching extensions on and off)
        /// </summary>
        Shell = 0x0,
 
        /// <summary>
        /// One instance per each use
        /// </summary>
        Transient = 0x2,
 
        /// <summary>
        /// One instance per unit of work (Web will contain one instance of HTTP query)
        /// </summary>
        UnitOfWork = 0x3
 
    }
 
}

Automatically registered services

ELMA will automatically register in Autofac the classes that are marked with the EleWise.ELMA.Attributes.ServiceAttribute attribute:

namespace EleWise.ELMA.ComponentModel
{
 
    /// <summary>
    /// Attribute of the service, automatically registered in the Autofac container
    /// </summary>
    [AttributeUsage(AttributeTargets.Class)]
    public class ServiceAttribute : Attribute
    {
 
        /// <summary>
        /// Automatically assign property values
        /// </summary>
        [DefaultValue(true)]
        public bool InjectProperties { get; set; }
 
        /// <summary>
        /// Lifecycle type
        /// </summary>
        [DefaultValue(ServiceScope.Applcation)]
        public ServiceScope Scope { get; set; }
 
        /// <summary>
        /// Apply method interceptors to the class
        /// </summary>
        [DefaultValue(true)]
        public bool EnableInterceptors { get; set; }
 
    }
 
}

Below is an example of automatically registered service:

/// <summary>
/// Registered with SomeService and ISomeService types
/// </summary>
[Service]
public class SomeService : ISomeService
{
    ...
}

Automatically registered objects

The following objects are registered automatically in the Autofac container

Components (for more info see Component model)

Objects are registered with types of the component’s class and types of the implemented extension points:

/// <summary>
/// Component is registered with CustomMenuExtension and IEnumerable<IMenuExtension> types
/// </summary>
[Component]
public class CustomMenuExtension : IInitHandler, IMenuExtension
{
    ...
}

Automatically registered services

See above Automatically registered services.

Entity managers

These are classes that implement the IEntityManager interface.

/// <summary>
/// Controller of the web application – it is registered with the MenuController type
/// </summary>
public class MenuController : BaseController<Menu, long>
{
    ...
}

How to register system components manually

We recommend registering components manually only if automatic registration cannot be used for some reason.

There are two ways to register components manually: 

  1. During system initialization

To do that, you need to create a component with the implementation of EleWise.ELMA.ComponentModel.IInitHandler and register components in Init() method using the static property ComponentManager.Builder.

To access extension methods for registering components, you also need to add a reference to Autofac assembly: using Autofac;

When you register a component, you can:

  • Set a type of registration. To do that, you can use RegisterType() or RegisterType(typeof(TService)) methods;
  • Set types for accessing the registered object. To do that, you can use As()or As(typeof(TRegisrationType)) methods;
  • Set lifecycle type for the registered object. To do that, you can use SetScope() extension method from the Autofac By default (if you do not call SetScope() method), ELMA sets Transient type – one instance per use;
  • Automatically initialize object’s properties. To do that, you can use the PropertiesAutowired() method.

Below is the example:

using Autofac;
using EleWise.ELMA.ComponentModel;
 
/// <summary>
/// A manager who needs to be registered in the Autofac container.
/// </summary>
public class SomeManager
{
    ...
}
 
/// <summary>
/// A class that we use to register SomeManager
/// </summary>
[Component]
public class SomeManagerRegistrar : IInitHandler
{
 
    /// <summary>
    /// Starting initialization – we register SomeManager with the Application lifecycle type and automatic properties assigning.
    /// </summary>
    public void Init()
    {
        ComponentManager.Builder
            .RegisterType<SomeManager>()
            .SetScope(ServiceScope.Applcation)
            .PropertiesAutowired(false);
    }
 
    /// <summary>
    /// Finishing initialization
    /// </summary>
    public void InitComplete() { }
 
}

          2. At any time after the initialization

You should only use this method when the automatic registration and the method described above cannot be used. Use the static class EleWise.ELMA.Locator and one of the AddService() method. See Locator of ELMA services article to learn more about the Locator class.

namespace EleWise.ELMA.Services
{
 
    /// <summary>
    /// Service manager
    /// </summary>
    public static class Locator
    {
 
        /// <summary>
        /// Means that the manager is initialized
        /// </summary>
        public static bool Initialized
        {
            get;
        }
 
        /// <summary>
        /// Register the service
        /// </summary>
        /// <param name="type">Service type</param>
        /// <param name="obj">Service</param>
        public static void AddService(Type type, object obj);
 
        /// <summary>
        /// Register Service
        /// </summary>
        /// <param name="type">Service Type</param>
        /// <param name="obj">Service</param>
        /// <param name="resolveProperties">Process public properties when registering</param>
        public static void AddService(Type type, object obj, bool resolveProperties);
 
        /// <summary>
        /// Register existing service
        /// </summary>
        /// <typeparam name="T">Service type</typeparam>
        /// <param name="obj">Service</param>
        public static void AddService<T>(T obj);
 
        /// <summary>
        /// Register existing service
        /// </summary>
        /// <typeparam name="T">Service Type</typeparam>
        /// <param name="obj">Service</param>
        /// <param name="resolveProperties">Process public properties when registering</param>
        public static void AddService<T>(T obj, bool resolveProperties);
 
        /// <summary>
        /// Remove service
        /// </summary>
        /// <param name="type">Service type</param>
        public static void RemoveService(Type type);
 
    }
 
}

Below is the example: 

public class SomeClass
{
    public void SomeMethod()
    {
        ...
        if (!Locator.Initialized)
        {
            throw new InvalidOperationException("Locator not initialized");
        }
        Locator.AddService<SomeService>(new SomeService());
    }
}

How to receive objects from Autofac

There are two ways to receive objects from the Autofac container. We recommend using the first way if possible.

  1. Using auto-injected properties

Apply this method if you need to receive components (e.g. MenuController) from an object that is registered in Autofac and whose properties are initiated automatically (PropertiesAutowired(true).

In this case, to receive an object from the container, you need to declare a property:

public class MenuController : Controller
{
    ...
 
    public ISomeService SomeService{ get; set; }
 
    ...
}

Here, when ELMA creates MenuController object, the SomeService property will be initialized automatically if ISomeService type is registered. Otherwise, this property will be set to null.

      2. Using the locator of services

You can receive a property from Autofac using the following methods of the Locator class:

{
 
    /// <summary>
    /// Services Manager
    /// </summary>
    public static class Locator
    {
 
        /// <summary>
        /// Labelthat the manager is initialized
        /// </summary>
        public static bool Initialized
        {
            get;
        }
 
        /// <summary>
        /// Get the service with the specified type and name, with or without checking the services existance
        /// </summary>
        /// <param name="type">Service type</param>
        /// <param name="name">Service name</param>
        /// <param name="checkNotNull">Whether to check the service existance</param>
        /// <returns>Requested service</returns>
        /// <exception cref="EleWise.ELMA.Exceptions.NotInitializedException">If the services manager is not initialized</exception>
        /// <exception cref="EleWise.ELMA.Exceptions.ServiceNotFoundException">If the requested service is not found</exception>
        [CanBeNull]
        public static object GetService(Type type, string name, bool checkNotNull);
 
        /// <summary>
        /// Get the service with the specified type and name (without existance check)
        /// </summary>
        /// <param name="type">Service type</param>
        /// <param name="name">Service name</param>
        /// <returns>Requested Service</returns>
        /// <exception cref="EleWise.ELMA.Exceptions.NotInitializedException">If the services manager is not initialized</exception>
        [CanBeNull]
        public static object GetService(Type type, string name);
 
        /// <summary>
        /// Get the service with the specified type and name (with existance check)
        /// </summary>
        /// <param name="type">Service type</param>
        /// <param name="name">Service name</param>
        /// <returns>Requested Service</returns>
        /// <exception cref="EleWise.ELMA.Exceptions.NotInitializedException">If the services manager is not initialized</exception>
        /// <exception cref="EleWise.ELMA.Exceptions.ServiceNotFoundException">If the requested service is not found</exception>
        [NotNull]
        public static object GetServiceNotNull(Type type, string name);
 
        /// <summary>
        /// Get Service of the specified type (without existance check)
        /// </summary>
        /// <param name="type">Service type</param>
        /// <returns>Requested service</returns>
        /// <exception cref="EleWise.ELMA.Exceptions.NotInitializedException">If the services manager is not initialized</exception>
        [CanBeNull]
        public static object GetService(Type type);
 
        /// <summary>
        /// Get Service of the specified type (with existance check)
        /// </summary>
        /// <param name="type">Service type</param>
        /// <returns>Requested Service</returns>
        /// <exception cref="EleWise.ELMA.Exceptions.NotInitializedException">If the services manager is not initialized</exception>
        /// <exception cref="EleWise.ELMA.Exceptions.ServiceNotFoundException">If the requested service is not found</exception>
        [NotNull]
        public static object GetServiceNotNull(Type type);
 
        /// <summary>
        /// Get the service with the specified type and name (without existance check)
        /// </summary>
        /// <typeparam name="T">Service type</typeparam>
        /// <returns>Requested Service</returns>
        /// <exception cref="EleWise.ELMA.Exceptions.NotInitializedException">If the services manager is not initialized</exception>
        [CanBeNull]
        public static T GetService<T>();
 
        /// <summary>
        /// Get Service of the specified type (with existance check)
        /// </summary>
        /// <typeparam name="T">Service type</typeparam>
        /// <returns>Requested Service</returns>
        /// <exception cref="EleWise.ELMA.Exceptions.NotInitializedException">If the services manager is not initialized</exception>
        /// <exception cref="EleWise.ELMA.Exceptions.ServiceNotFoundException">If the requested service is not found</exception>
        [NotNull]
        public static T GetServiceNotNull<T>();
 
        /// <summary>
        /// Get the service with the specified type and name (without existance check)
        /// </summary>
        /// <typeparam name="T">Service type</typeparam>
        /// <param name="name">Service name</param>
        /// <returns>Requested service</returns>
        /// <exception cref="EleWise.ELMA.Exceptions.NotInitializedException">If the services manager is not initialized</exception>
        [CanBeNull]
        public static T GetService<T>(string name);
 
        /// <summary>
        /// Get the service with the specified type and name (with existance check)
        /// </summary>
        /// <typeparam name="T">Service type</typeparam>
        /// <param name="name">Service name</param>
        /// <returns>Requested service</returns>
        /// <exception cref="EleWise.ELMA.Exceptions.NotInitializedException">If the services manager is not initialized</exception>
        /// <exception cref="EleWise.ELMA.Exceptions.ServiceNotFoundException">If the requested service is not found</exception>
        [NotNull]
        public static T GetServiceNotNull<T>(string name);
 
    }
 
}

Below is the example:

using EleWise.ELMA.Services;
 
public class SomeClass
{
 
    public void SomeMethod()
    {
        var someService = Locator.GetService<ISomeService>();
        if (someService != null)
        {
            ...
        }
        /*

 or

var someService = Locator.GetServiceNotNull<ISomeService>();
        If the service not found, the EleWise.ELMA.Exceptions.ServiceNotFoundException error will be thrown
        */
    }
 
}