Dependency Injection

In the code of MYOB Acumatica Framework-based applications, you can use dependency injection to encapsulate particular logic as a service, which you can then use anywhere in your application. This technique can be used in graphs, graph extensions, attributes, custom action classes, PXSelectBase-derived classes, and PX.Tests.Unit.TestBase-derived classes, as described below.
Important:
  • Dependency injection in MYOB Acumatica Framework-based applications requires the use of the external Autofac library. MYOB does not guarantee the backward compatibility of this library. For details about the Autofac library, see https://autofac.readthedocs.io/en/latest/.
  • In your code, you need to use the same version of the Autofac library as the version provided in the Bin folder of the MYOB Acumatica instance to which you publish the customization project. You do not need to include the file of the Autofac library in your customization project.
Attention:
The dependency injection can be implemented in a project of your MYOB Acumatica extension library, which is compiled to an external DLL file. You cannot include the implementation of the dependency injection in a Code item in a customization project.
Tip:
For information about dependency injection in unit tests and PX.Tests.Unit.TestBase-derived classes, see Test Method: Registration of Services and Test Method: To Register a Service.

Definition of the Service for Dependency Injection

To define the service for dependency injection, you define an interface for the service and a class that implements this interface. The following example shows a definition of a service for dependency injection.
using System;
using Autofac;
using PX.Data;

namespace MyNamespace
{
    //An interface for the service
    public interface IMyService
    {
        void ProvideServiceFunctions();
    }
    //A class that implements the interface
    public class MyService : IMyService
    {
        public void ProvideServiceFunctions()
        {
            //An implementation
        }
    }
}

Registration of the Service

To register the service in your application, you do the following:
  1. Implement a registration class derived from the Autofac.Module class.
  2. In this registration class, override the Module.Load() method. You do not need to call the base method in the overriding method.
Tip:
  • You can register multiple implementations of an interface for the service in one registration class or multiple registration classes. You can also use other Autofac capabilities to inject dependencies in a class.
  • To make it possible to override the service in a customization project, you should register the service with PreserveExistingDefaults() appended to the registration method.
The following code shows an example of a registration class.
using System;
using Autofac;
using PX.Data;
namespace MyNamespace
{
    //A class that registers the implementation class with Autofac
    public class MyServiceRegistrarion : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterType<MyService>().As<IMyService>();
        }
    }
}

Use of Dependency Injection

You can use dependency injection in a graph, a graph extension, an attribute derived from PXEventSubscriberAttribute, a custom action class derived from the PXAction class or its descendants, a PXSelectBase-derived class, or a PX.Tests.Unit.TestBase-derived class. To use dependency injection in one of these classes, you define a property of this class and assign the InjectDependency attribute to this property, as shown in the following examples.

Attention:
  • Dependency injection in constructors is not supported. The system injects dependencies after the constructor is executed.
  • The properties to which the InjectDependency attribute is assigned cannot be used in graph constructors. If you need to use these properties during the initialization of a graph, you need to implement the IGraphWithInitialization interface in the graph. You can use the properties in the implementation of the IGraphWithInitialization.Initialize() method.
using System;
using PX.Data;

namespace MyNameSpace
{
    //Dependency injection in a graph
    public class MyGraph : PXGraph<MyGraph>
    { 
        [InjectDependency]
        private IMyService MyService { get; set; }

        public PXAction<MyDAC> MyButton;
        [PXButton]
        [PXUIField(DisplayName ="My Action")]
        protected void myButton()
        {
            MyService.ProvideServiceFunctions();
        }

        //Other code of the graph
    }

    //Dependency injection in a graph extension
    public class MyGraphExtension : PXGraphExtension<MyGraph>
    {
        [InjectDependency]
        private IMyService MyService { get; set; }

        //Other code of the graph extension
    }

    //Dependency injection in a custom attribute
    public class CustomAttribute : PXEventSubscriberAttribute
    {
        [InjectDependency]
        public IMyService Service { get; set; }

        //Other code of the attribute
    }

    //Dependency injection in a custom action class
    public class CustomCancel<T> : PXCancel<T> 
        where T: class, IBqlTable, new()
    {
        [InjectDependency]
        public IMyService Service { get; set; }

        //Other code of the custom action class
    }

    //Dependency injection in a PXSelectBase-derived class
    public class MySelect<Table> : PXSelectBase<Table>
        where Table : class, IBqlTable, new()
    { 
        [InjectDependency]
        private IMyService MyService { get; set; }

        //Other code of the class
    }

    //Dependency injection in a PX.Tests.Unit.TestBase-derived class
    public class MyTestClass : PX.Tests.Unit.TestBase
    { 
        [InjectDependency]
        private IMyService MyService { get; set; }

        // Other code of the test class
    }
}

Injection of the Current Context into the Service

If you need to support the automatic injection of the current context (such as the current PXGraph instance) into the service, you add a constructor of the implementation class of the service with the following context as a parameter or as parameters:
  • If you are injecting dependency in a graph, PXGraph
  • If you are injecting dependency in a graph extension, PXCacheExtension, PXGraph, or both PXCacheExtension and PXGraph
  • If you are injecting dependency in an attribute, PXEventSubscriberAttribute, PXCache, or PXGraph (or any combination of these objects)
  • If you are injecting dependency in a custom action class, PXAction, PXGraph, or both PXAction and PXGraph
  • If you are injecting dependency in a PXSelectBase-derived class, a PXSelectBase-derived class
The following code shows examples of constructors in the implementation class.
public class MyService : IMyService
{
    //A constructor with PXEventSubscriberAttribute
    public MyService(PXEventSubscriberAttribute parent) 
    { 
        //Code of the constructor
    }

    //A constructor with PXCache
    public MyService(PXCache cache)
    { 
        //Code of the constructor
    }

    //A constructor with PXGraph
    public MyService(PXGraph graph)
    { 
        //Code of the constructor
    }

    //A constructor with PXEventSubscriberAttribute and PXGraph
    public MyService(PXEventSubscriberAttribute parent, PXGraph graph)
    { 
        //Code of the constructor
    }

    //Other code of the implementation class
}