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 instanceAdapter<>
.
In other words, IFoo
both IBar
can be in namespace1 and IAbc
in namespace2 and I want to register:
-
Adapter<IFoo>
asIAdapter<IFoo>
-
Adapter<IBar>
asIAdapter<IBar>
-
Adapter2<IAbc>
orAdapter<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.
source to share
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>()
source to share