Dependency Injection
If you are new to the concepts of inversion of control and dependency injection, check out this article by Martin Fowler.
VertiGIS Studio Mobile relies heavily on inversion of control patterns to inject dependencies into its components and services. Under the hood, VertiGIS Studio Mobile uses Autofac to register and inject components, services and other classes. To inject a dependency into a class in VertiGIS Studio Mobile, two things must happen.
- The class to be injected must register itself with the autofac container.
- The class that requires the dependency must accept a concrete class or abstract interface in its constructor as a placeholder for the injected class.
A good example of this pattern is the AlertOperations
class in the commands and operation tutorial .
using App1.Services.AlertOperations;
using VertiGIS.Mobile.Composition;
using VertiGIS.Mobile.Composition.Messaging;
using VertiGIS.Mobile.Infrastructure.Messaging;
[assembly: Export(typeof(AlertOperations), SingleInstance = true)]
namespace App1.Services.AlertOperations
{
class AlertOperations : OperationsBase
{
public AlertOperations(IOperationRegistry operationRegistry)
: base(operationRegistry)
{
}
}
}
- This class both registers itself as a dependency, and has dependencies injected itself.
- The assembly attribute
Export
is part of the VertiGIS Studio Mobile SDK, and registers the class with Autofac as a singleton. - The constructor of the class takes an object of the type
IOperationRegistry
. This dependency will be resolved by Autofac at runtime whenAlertOperations
is injected by another class. Chaining dependency injection in this manner is both a common and best practice pattern in VertiGIS Studio Mobile.
This is an example of a class that injects AlertOperations
, which will in turn inject an IOperationRegistry
into itself.
[assembly: Service(typeof(CustomService), PropertiesAutowired = true)]
namespace App1.Services
{
class CustomService : ServiceBase
{
public CustomService(AlertOperations AlertOperations)
:base()
{
...
}
}
...
}
Autofac has a number of ways to inject dependencies, including factory functions which can dynamically spin up instances of a class or lazy wrappers for dependencies. For details, check out the Autofac documentation.
Components and Services
Components and services in VertiGIS Studio Mobile are almost always initialized by Autofac. This means that they can inject any class that has been registered with Autofac in their constructor, allowing classes used for behavior like commands and operations to be easily shared and loosely coupled to many components and services. Root level components (components that are instantiated from a layout definition) must always register with Autofac to be correctly initialized, and it is best practice to use dependency injection to initialize instances of any other custom classes, services or components that the root level component depends on.
List of Registration Assembly Attributes
There's a number of different VertiGIS Studio Mobile specific assembly attributes which allow classes to register themselves with VertiGIS Studio Mobile's Autofac container.
Export
Used for registering a generic class with Autofac.
[assembly: Export(typeof(MyCustomClass))]
Component
Used for registering a component that does not consume configuration. For a more detailed example, see the tutorial on implementing a custom component.
[assembly: Component(typeof(MyCustomComponent), "my-custom-component")]
AppItemComponent
Used for registering a component that does consume configuration. For a more detailed example, see the tutorial on implementing a custom component with configuration.
[assembly: AppItemComponent(typeof(MyCustomComponent), "my-custom-component", MyCustomComponentConfiguration.ConfigItemtype)]
Service
Used for registering a service. For a more detailed example, see create a service.
[assembly: Service(typeof(CustomService))]
AppItem
Used for registering the definition for an app item with VertiGIS Studio Mobile. For a more detailed example, see the tutorial on implementing a custom component with app configuration.
[assembly: AppItem(CustomAppItem.AppItemType, typeof(CustomAppItem))]
View
Used for registering a view with VertiGIS Studio Mobile. For a more detailed example see the tutorial on implementing a custom component with UI.
[assembly: View(typeof(CustomView))]
ViewModel
Used for registering a view model with VertiGIS Studio Mobile. For a more detailed example see the tutorial on implementing a custom component with UI.
[assembly: ViewModel(typeof(CustomViewModel))]
Managing Sub-Dependencies
Sometimes, you may may want to split code associated with a component or service into multiple classes for individual views, utility functions, etc. Each of these classes may have dependencies on global classes registered with Autofac, such as operations, but only the registered component is instantiated by dependency injection, so only the component class can inject these global classes. One solution is to have the component class inject all the dependencies of its associated classes and pass them to the class constructors, but this pattern introduces unnecessary boilerplate and coupling. Instead, if the component class creates instances of associated classes through dependency injection, then Autofac will automatically resolve any dependencies the associated class has.
In the example below, the RelatedClass
has a global dependency, MapOperations
. In order to resolve this dependency, the CustomComponent
creates an instance of the RelatedClass
through dependency injection.
Autofac allows for the injection of factory functions for classes, partially resolved class constructors, and more.
- Related Class
- Component
using VertiGIS.Mobile.Composition;
using VertiGIS.Mobile.Infrastructure.Messaging;
using VertiGIS.Mobile.Samples.Samples.Custom.Component;
[assembly: Export(typeof(RelatedClass))]
namespace VertiGIS.Mobile.Samples.Samples.Custom.Component
{
internal class RelatedClass
{
private MapOperations _mapOperations;
public RelatedClass(MapOperations mapOperations)
{
_mapOperations = mapOperations;
}
}
}
using VertiGIS.Mobile.Samples;
using VertiGIS.Mobile.Samples.Samples.Custom.Component;
using VertiGIS.Mobile.Composition.Layout;
using System.Xml.Linq;
using Xamarin.Forms;
[assembly: Component(typeof(CustomComponent), "component", XmlNamespace = XmlNamespaces.SamplesNamespace)]
namespace VertiGIS.Mobile.Samples.Samples.Custom.Component
{
internal class CustomComponent : ComponentBase
{
private RelatedClass _relatedClass;
public CustomComponent(RelatedClass relatedClass)
{
_relatedClass = relatedClass;
}
protected override VisualElement Create(XNode node)
{
return new Label()
{
Text = "Hello, World!",
HorizontalTextAlignment = TextAlignment.Center
};
}
}
}
Relevant SDK Sample
Check out the VertiGIS Studio Mobile SDK Sample for dependency injection.