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 theAutofac
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 theAutofac
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:
- Implement a registration class derived from the Autofac.Module class.
- 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
}