Wiki

Clone wiki

ServiceResolver / Home

Quick Start

Overview

ServiceResolver is an abstraction about a standard way for register and resolution services, using a custom implementation, or encapsulating an underlying DI engine.

Actually, there's two implementations for this framework:

Architecture overview

Service providers

ServiceResolver_01.PNG

Service resolvers, conditional, module & helpers

ServiceResolver_02.PNG

Exceptions

ServiceResolver_03.PNG

Framework features

This framework offer standard characteristics on registration and resolution ways, but introduce some complex strategies like "Modules registration", "Resolvers" ecc.

This solution is compatible with net45, net46, and NetStandard2.0.

Getting Started

The easiest way to get started is by installing the available NuGet packages.

Quick examples

Registration with generics

this is the most common way to register services.

#!c#
// suppose GetProvider method gives us the main reference component
IServiceRegisterProvider serviceRegisterProvider = GetProvider();
serviceRegisterProvider.Register<IMyInterface, InterfaceImplementor>();

Registration with scopes

#!c#
// suppose GetProvider method gives us the main reference component
IServiceRegisterProvider serviceRegisterProvider = GetProvider();
serviceRegisterProvider.Register<ICustomCalculator, CustomCalculator>(LifetimeScope.Singleton);

Registration with types

#!c#
// suppose GetProvider method gives us the main reference component
IServiceRegisterProvider serviceRegisterProvider = GetProvider();
serviceRegisterProvider.Register(typeof(IMyInterface), typeof(InterfaceImplementor));
// using a lifetime scope
serviceRegisterProvider.Register(typeof(IMyInterface2), typeof(InterfaceImplementor2), LifetimeScope.Singleton);

Registration with types for open generics

#!c#
// suppose GetProvider method gives us the main reference component
IServiceRegisterProvider serviceRegisterProvider = GetProvider();
serviceRegisterProvider.Register(typeof(IMyGenericInterface<>), typeof(MyGenericImpl<>));
// using a lifetime scope
serviceRegisterProvider.Register(typeof(IMyGenericInterface2<>), typeof(MyGenericImpl2<>), LifetimeScope.Singleton);

Registration with factory resolution

#!c#
// suppose GetProvider method gives us the main reference component
IServiceRegisterProvider serviceRegisterProvider = GetProvider();
serviceRegisterProvider.Register(typeof(IMyInterface), () => new InterfaceImplementor());

// you can use generic way registration
serviceRegisterProvider.Register<IMyInterface>(() => new InterfaceImplementor());

// you can use the overload for retrieving another services from service provider
serviceRegisterProvider.Register(typeof(IMyInterface2), serviceProvider => new InterfaceImplementor2(serviceProvider.GetService<IOtherDependency>()));

Registration with conditional resolution

in some cases, It could be very useful using a custom criteria for service resolution.

#!c#
// suppose GetProvider method gives us the main reference component
IServiceRegisterProvider serviceProvider = GetProvider();
serviceProvider.RegisterConditional<ICustomLogger>(register => register
    .Register<NoSqlLogger>(service => service.ConsumerType == typeof(LogManager))
    .Register<DatabaseLogger>(service => service.ConsumerType == typeof(LogManagerTrusted)));

serviceProvider.Register<LogManager>()
    .Register<LogManagerTrusted>();

var serv1 = serviceProvider.GetService<LogManager>();
var serv2 = serviceProvider.GetService<LogManagerTrusted>();

// asserts
Assert.Equal(typeof(NoSqlLogger), serv1.Logger.GetType());
Assert.Equal(typeof(DatabaseLogger), serv2.Logger.GetType());

Registration with Resolvers

this is a complex situation when you need to isolate registrations in order to use them as dependencies for a particular service resolution.

#!c#
// suppose GetProvider method gives us the main reference component
var serviceRegisterProvider = GetProvider();

serviceRegisterProvider.Register<IMyInterface, InterfaceImplementor>(LifetimeScope.Singleton);

// a custom resolver
serviceRegisterProvider.RegisterResolver<CustomDerivedBusinessLogic>()
    .Register<ICustomLogger>(() => new ConsoleLogger("external"));

// another resolver
serviceRegisterProvider.RegisterResolver<CustomDerivedBusinessLogicV2>()
    .Register<ICustomLogger, DatabaseLogger>();

// arranges
// each services is resolved by serviceRegisterProvider, but one dependency (ICustomLogger) is resolved in a resolver context.
var bl1 = serviceRegisterProvider.GetService<CustomDerivedBusinessLogic>();
var bl2 = serviceRegisterProvider.GetService<CustomDerivedBusinessLogicV2>();

// asserts
Assert.Equal(typeof(ConsoleLogger), bl1.Logger.GetType());
Assert.Equal(typeof(DatabaseLogger), bl2.Logger.GetType());

Assert.Equal(bl1.Interface, bl2.Interface);
Assert.Same(bl1.Interface, bl2.Interface);
When you create a Resolver, you create a new reference of IRegisterResolver, but this one will contain all dependecies registration for resolution of Service resolver, a little example like:

#!c#

serviceProvider.Register<IMyInterface, InterfaceImplementor>();

var registerResolver = serviceProvider.RegisterResolver<IOtherService>()
    .Register<IDependency1, Dependency1>()
    .Register<IDependency2, Dependency2>()
    .Register<IDependency3, Dependency3>();
In the example you register 3 isolated dependencies, which can be resolved only by registerResolver reference, but they cannot be resolved by serviceProvider reference, because they're isolated into another context.

If you try to do the following example, it will throw an exception.

#!c#
serviceProvider.GetService<IDependency1>();
Obviously serviceProvider reference can resolve IOtherService, but the other 3 registrations for resolver not.

Naturally you can create more and more complex scenarios for inner resolutions:

#!c#
var registerResolver = serviceProvider.RegisterResolver<IOtherService>()
    .Register<IDependency1, Dependency1>()
    .Register<IDependency2, Dependency2>()
    .RegisterResolver<IDependency3, Dependency3>()
        .Register<IDependency003, Dependency003>()
        .Register<IDependency004, Dependency004>()
        .RegisterResolver<IDependency4>()
            .Register<IDependency004, Dependency004>()
            .Register<IDependency005, Dependency005>()
            .Register<IDependency006, Dependency006>();

There's no limit to isolate registrations with resolvers, only your imagination !

Registration with Many resolutions

Associate a list of registrations for a single resolution is done with RegisterMany<TService>(..)

#!c#
var serviceRegisterProvider = GetProvider();
// retrieves all implementation for IMyInterface from assembly (see other overloads for details).
serviceRegisterProvider.RegisterMany<IMyInterface>(GetType().Assembly);

// gets all services
IEnumerable<IMyInterface> services = serviceRegisterProvider.GetServices<IMyInterface>();

Register Initializers

Initializers could be useful whenever you need to initialize a service after resolution.

#!c#
var serviceRegisterProvider = GetProvider();

serviceRegisterProvider.Register(() => new ConsoleLogger("prova"));
serviceRegisterProvider.Register<ICustomLogger, CustomLogger>()
.Register<IMyInterface, InterfaceImplementor>()
.RegisterInitializer<IMyInterface>((container, service) =>
    {
        service.Description = container.GetService<ConsoleLogger>().Owner;
    });

var logInterface = serviceRegisterProvider.GetService<IMyInterface>();
var custLog = serviceRegisterProvider.GetService<ConsoleLogger>();

// asserts
Assert.True(logInterface.Description== custLog.Owner);
serviceRegisterProvider = GetProvider();
serviceRegisterProvider.Register<ICustomLogger, CustomLogger>()
    .Register<IMyInterface, InterfaceImplementor>()
    .RegisterInitializer<IMyInterface>(service =>
    {
        service.Description = "INIT DESC";
    });

Assert.True(serviceRegisterProvider.GetService<IMyInterface>().Description== "INIT DESC");

Batch Registration for Open generics

#!c#
var serviceRegisterProvider = GetProvider();

serviceRegisterProvider.RegisterGeneric(typeof(IMyGenericInterface<>), new[] {GetType().Assembly});

// suppose in the current example there are 2 implementation for the given interfaces
Assert.NotNull(serviceRegisterProvider.GetService<IMyGenericInterface<int>>());
Assert.NotNull(serviceRegisterProvider.GetService<IMyGenericInterface<string>>());

// and for this case there's no implementation.. so exception is thrown.
Assert.Throws<ServiceUnavailableException>(() =>
    serviceRegisterProvider.GetService<IMyGenericInterface<long>>());

Module Registration

Modules is a cool way to organize your registrations, so you can define your module for a logical group of registrations, It could be easy to understand too many registrations in a better way.

So, in order to use a module, you need to implement the IServiceModule interface, and implement the unique method Initialize(), in order to register all registrations you want.

#!c#
// an example of module Implementation.
public class CustomServiceModule
    : IServiceModule
{
    public void Initialize(IServiceRegister serviceRegister)
    {
        serviceRegister.RegisterResolver<IComposedBusinessLogic, ComposedBusinessLogic>()
            .Register<ICustomCalculator, CustomCalculator>(LifetimeScope.Singleton)
            .Register<ICustomLogger>(() => new ConsoleLogger("resi-bl"), LifetimeScope.Singleton)
            ;
    }
}

Once implemented, you need to register into service provider.

#!c#
serviceRegisterProvider.Register(new CustomServiceModule());

Batch Modules registration

another interesting feature is module batch registrations, in this way applications are able to automate registration of all modules present in the execution application, and the others modules referenced in the other assemblies linked into execution project.

#!c#
var serviceRegisterProvider = GetProvider();
// you can exclude obsoleted modules for registration indicating in the second argument.
serviceRegisterProvider.RegisterModulesFrom(Assembly.GetExecutingAssembly(), true);
// asserts
Assert.NotNull(serviceRegisterProvider.GetService<IComposedBusinessLogic>());

Build child register provider

you can create another service register provider inheriting all registration from another one.

#!c#
var serviceRegisterProvider = GetProvider();
serviceRegisterProvider.Register<ICustomLogger>(() => new ConsoleLogger("external"))
    .RegisterResolver(typeof (IComposedBusinessLogic), typeof (ComposedBusinessLogic))
    .RegisterResolver(typeof(ICustomCalculator), typeof(CustomCalculatorLogger));
serviceRegisterProvider.Verify();
var serviceRegisterProvider2 = serviceRegisterProvider.BuildChildServiceProvider();

serviceRegisterProvider2.Register<IAggregatorBl, AggregatorBl>();
serviceRegisterProvider2.GetService<IAggregatorBl>();

Verify registrations

in order to verify all registrations, you can use Verify() method, whenever there's a missing dependency registration for a service It throws an exception explaining the reasons.

#!c#
var serviceRegisterProvider = GetProvider();
serviceRegisterProvider.Register<ICustomLogger>(() => new ConsoleLogger("external"));
serviceRegisterProvider.RegisterResolver<IAggregatorBl, AggregatorBl>()
    .RegisterResolver<IComposedBusinessLogic, ComposedBusinessLogic>()
    .RegisterResolver<ICustomCalculator, CustomCalculatorLogger>();
// asserts the verify invocation doesn't throw an exception.
serviceRegisterProvider.Verify();

Working with scopes

In some situations, Scopes aren't managed directly by applications, instead there are customization for injecting Scopes initialization inside integration middlewares.

If you're working with ASP.NET, those integrations already exists, but in other context like no "Web based" you need manually open Scopes in order to working with Scope resolution lifetime.

#!c#
// in this example It shows us how can Open a new Scope directly by IServiceRegisterProvider reference.

var serviceRegisterProvider = GetProvider();

using (var scope = serviceRegisterProvider.BeginScope())
{
   // all your bussines logic here, with your services resolutions using the Scope indicated on registration.
}

Some considerations

  • It's a good practice to implement IDisposable on IServiceRegisterProvider interface in order to call it when applications is shutdown.
  • Normally, once you get resolved a service by service provider, this one is blocked for further registrations, otherwise an exception is thrown.

Updated