Registration adapter without common interface and scan assemblies

I am trying to achieve the following:

  • Scan a specific assembly ( RegisterAssemblyTypes(assembly)

    ).
  • A filter for a specific namespace ( .IsInNamespace("...")

    ) that will have standalone interfaces without a common base interface.
  • Register the generic adapter as a generic interface. (.RegisterGeneric(typeof(Adapter<>)).As(typeof(IAdapter<>))

  • I will need some of these registrations and each set will use IAdapter<>

    with a different instance Adapter<>

    .

In other words, IFoo

both IBar

can be in namespace1 and IAbc

in namespace2 and I want to register:

  • Adapter<IFoo>

    as IAdapter<IFoo>

  • Adapter<IBar>

    as IAdapter<IBar>

  • Adapter2<IAbc>

    or Adapter<IAbc>(customParams)

    howIAdapter<IAbc>

To repeat a bit, if I do my own assembly scan to get the interfaces, then I believe I can do something similar to this (untested):

Type interfaces[] = ...; // Scan assembly and filter to namespace1

foreach (Type interfaceType in interfaces)
{
    builder
        .Register(context => typeof(Adapter<>).MakeGenericType(interfaceType))
        .As(type => typeof(IAdapter<>).MakeGenericType(interfaceType));
}

interfaces = ...; // Interfaces in namespace2

foreach (Type interfaceType in interfaces)
{
    builder
        .Register(context => typeof(Adapter2<>).MakeGenericType(interfaceType))
        .As(type => typeof(IAdapter<>).MakeGenericType(interfaceType));
}

      

Is there a way to do this out of the box using Autofac without my own scanning / looping? I've looked at the Autofac adapter support, but it only seems to work with the generic interface. RegisterGeneric also helps, but with only one instance type.

+3


source to share


1 answer


Autofac does not have built-in support for solving a generic service based on a generic parameter type. By the way, you can use dynamic registration using IRegistrationSource

to do what you want.

Let's say you have these types:

namespace A
{
    interface IFoo { }
}
namespace B
{
    interface IBar { }
}

interface IAdapter<T> { }
class Adapter1<T> : IAdapter<T> { }
class Adapter2<T> : IAdapter<T> { }
class Foo : A.IFoo { }
class Bar : B.IBar { }

      

You want something like this:

ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Foo>().As<A.IFoo>();
builder.RegisterType<Bar>().As<B.IBar>();

builder.RegisterGeneric(typeof(Adapter1<>)).As(typeof(IAdapter<>));
builder.RegisterGeneric(typeof(Adapter2<>)).As(typeof(IAdapter<>));

IContainer container = builder.Build();

var fooAdapter = container.Resolve<IAdapter<A.IFoo>>(); // should return Adapter1<Foo>()
var barAdapter = container.Resolve<IAdapter<B.IBar>>(); // should return Adapter2<Bar>()

      

When Autofac resolves IAdapter<T>

, it cannot find a specific implementation based on the namespace T

.



A IRegistrationSource

will allow you to dynamically register the type when Autofac needs it.

class TestRegistrationSource : IRegistrationSource
{
    public Boolean IsAdapterForIndividualComponents
    {
        get { return false; }
    }

    public IEnumerable<IComponentRegistration> RegistrationsFor(
        Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
    {
        IServiceWithType typedService = service as IServiceWithType;
        if (typedService == null)
        {
            yield break;
        }
        if (!(typedService.ServiceType.IsGenericType 
              && typedService.ServiceType.GetGenericTypeDefinition() == typeof(IAdapter<>)))
        {
            yield break;
        }

        Type t = typedService.ServiceType.GetGenericArguments()[0];

        IComponentRegistration registration = 
            RegistrationBuilder.ForDelegate((c, p) => c.ResolveNamed(t.Namespace, typedService.ServiceType, p))
                               .As(service)
                               .CreateRegistration();

        yield return registration;
    }
}

      

This one IRegistrationSource

will register a new concrete implementation when Autofac needs it. When Autofac needs it IAdapter<A.Foo>

, it will go in TestRegistrationSource

, which will register a new one IAdapter<A.Foo>

as a delegate, which will allow named generic registrationIAdapter<T>

To make it work TestRegistrationSource

, you need to change the registration Adapter1<T>

and Adapter2<T>

to use the generic named registration.

ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Foo>().As<A.IFoo>();
builder.RegisterType<Bar>().As<B.IBar>();

builder.RegisterSource(new TestRegistrationSource());

builder.RegisterGeneric(typeof(Adapter1<>)).Named("Namespace.A", typeof(IAdapter<>));
builder.RegisterGeneric(typeof(Adapter2<>)).Named("Namespace.B", typeof(IAdapter<>));

IContainer container = builder.Build();

var fooAdapter = container.Resolve<IAdapter<A.IFoo>>(); // will return Adapter1<Foo>()
var barAdapter = container.Resolve<IAdapter<B.IBar>>(); // will return Adapter2<Bar>()

      

+1


source







All Articles