The recording of the first ever ReactiveUI virtual conference is available on our YouTube Channel.

Custom Dependency Inversion

Override Default Depenedency Inversion Container

We understand that some developers would prefer to use their favorite dependency inversion container. ReactiveUI allows for this by implementing IMutableDependencyResolver. Once this class has been implemented you simply need to assign it to Locator via Locator.SetLocator().

Use a Dependency Resolver Package

ReactiveUI uses Splat for service location, and Splat can be thought of as a ReactiveUI utility extracted into a separate package. Splat includes ready to use packages containing dependency resolver implementations for Autofac, DryIoc, Microsoft.Extensions.DependencyInjection, Ninject, and SimpleInjector, see the documentation page for more info.

Now let's consider a simple example. Assume we wish to use Splat.Autofac dependency resolver package. We are going to replace the default container used by ReactiveUI with Autofac. To get started, we install the Splat.Autofac package via NuGet package manager or via .NET Core CLI:

dotnet add package Splat.Autofac

Next, we build a new Autofac container and register all the required dependencies into it. Next, we add a call to UseAutofacDependencyResolver which is an extension method inside the Splat.Autofac namespace. This method becomes available once we install the Splat.Autofac package into our project. We write the following code:

// Create a new Autofac container builder.
var builder = new ContainerBuilder();
builder.RegisterType<MainPage>().As<IViewFor<MainViewModel>>();
builder.RegisterType<MainViewModel>();
// etc.

// Register the Adapter to Splat.
// Creates and sets the Autofac resolver as the Locator.
var autofacResolver = builder.UseAutofacDependencyResolver();

// Register the resolver in Autofac so it can be later resolved.
builder.RegisterInstance(autofacResolver);

// Initialize ReactiveUI components.
autofacResolver.InitializeReactiveUI();

// If you need to override any service (such as the ViewLocator), register it after InitializeReactiveUI.
// https://autofaccn.readthedocs.io/en/latest/register/registration.html#default-registrations
// builder.RegisterType<MyCustomViewLocator>().As<IViewLocator>().SingleInstance();

Note Call Locator.CurrentMutable.InitializeReactiveUI() only if you are going to override the default Splat locator. Otherwise you don't need to explicitly call the method, as ReactiveUI calls the method implicitly for you.

Set Autofac Locator's lifetime after the ContainerBuilder has been built:

var autofacResolver = container.Resolve<AutofacDependencyResolver>();

// Set a lifetime scope (either the root or any of the child ones) to Autofac resolver.
// This is needed because Autofac became immutable since version 5+.
// https://github.com/autofac/Autofac/issues/811
autofacResolver.SetLifetimeScope(container);`

Implement a Custom IMutableDependencyResolver

If you are going to write a custom dependency resolver for Splat, then implement the IMutableDependencyResolver interface and add a call to Locator.SetLocator. This will replace the default locator implementation with your own.

IMutableDependencyResolver implementation against Autofac (example)
public class AutofacDependencyResolver : IMutableDependencyResolver
{
    private readonly IContainer _container;

    public AutofacDependencyResolver(IContainer container)
    {
        _container = container;
    }

    public object GetService(Type serviceType, string contract = null)
    {
        try
        {
            return string.IsNullOrEmpty(contract)
                ? _container.Resolve(serviceType)
                : _container.ResolveNamed(contract, serviceType);
        }
        catch (DependencyResolutionException)
        {
            return null;
        }
    }

    public IEnumerable<object> GetServices(Type serviceType, string contract = null)
    {
        try
        {
            var enumerableType = typeof(IEnumerable<>).MakeGenericType(serviceType);
            object instance = string.IsNullOrEmpty(contract)
                ? _container.Resolve(enumerableType)
                : _container.ResolveNamed(contract, enumerableType);
            return ((IEnumerable)instance).Cast<object>();
        }
        catch (DependencyResolutionException)
        {
            return null;
        }
    }

    public void Register(Func<object> factory, Type serviceType, string contract = null)
    {
        var builder = new ContainerBuilder();
        if (string.IsNullOrEmpty(contract))
        {
            builder.Register(x => factory()).As(serviceType).AsImplementedInterfaces();
        }
        else
        {
            builder.Register(x => factory()).Named(contract, serviceType).AsImplementedInterfaces();
        }

        builder.Update(_container);
    }

    public IDisposable ServiceRegistrationCallback(Type serviceType, string contract, Action<IDisposable> callback)
    {
        // this method is not used by RxUI
        throw new NotImplementedException();
    }

    public void Dispose()
    {
        _container.Dispose();
    }
}
// Build a new Autofac container.
var containerBuilder = new ContainerBuilder();
container.RegisterType<MainPage>().As<IViewFor<MainViewModel>>();

// Use your own IMutableDependencyResolver implementation.
Locator.SetLocator(new AutofacDependencyResolver(containerBuilder.Build()));

// These InitializeX() methods will add ReactiveUI platform 
// registrations to your container. They MUST be present if
// you override the default Locator.
Locator.CurrentMutable.InitializeSplat();
Locator.CurrentMutable.InitializeReactiveUI();

From this point on calls Locator.Current will go against your custom implementation!

Note Some DI engines get locked away after they're being built and become immutable. For such services, the Use<MatchingService>DependencyResolver extension method (UseAutofacDependencyResolver in Autofac) must be called again on the locked container. See AutoFac docs.

Note In Microsoft DI, the built container is a completely different type. You should recall UseMicrosoftDependencyResolver on the IServiceProvider once it's ready, in case you are using an external framework to manage DI (i.e. Generic Host), see the dedicated MS DI docs.