Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resolve type without creating object

Tags:

c#

autofac

Here's my problem: I have a container where I register concrete types as interfaces.

builder.RegisterType<DeleteOrganization>().As<IDeleteOrganization>();

I'm implementing a SerializationBinder for a serialization project I'm doing and the BindToType method that I need to implement wants me to return a Type object. The BindToType method gives me an assemblyName and typeName (both strings) to help me create a type object. What I want to do is if the typeName is an interface, I want to ask Autofac what the concrete implementation Type is for that interface Type without actually having it create the object. Is that possible?

like image 910
chrisdrobison Avatar asked Feb 28 '12 18:02

chrisdrobison


2 Answers

If you are using the RegisterType to register your services this is possible. I wrote a quick test that should help you extract the data you need.


private interface IDeleteOrganization
{

}

private class DeleteOrganization : IDeleteOrganization
{

}


[TestMethod]
public void CanResolveConcreteType()
{
    var builder = new ContainerBuilder();

    builder.RegisterType()
        .As();

    using(var container = builder.Build())
    {
        var registration = container.ComponentRegistry
            .RegistrationsFor(new TypedService(typeof (IDeleteOrganization)))
            .SingleOrDefault();

        if (registration != null)
        {
            var activator = registration.Activator as ReflectionActivator;
            if (activator != null)
            {
                //we can get the type
                var type = activator.LimitType;
                Assert.AreEqual(type, typeof (DeleteOrganization));
            }
        }
    }
}
like image 181
Danielg Avatar answered Nov 20 '22 01:11

Danielg


You can very nicely encapsulate @Danielg's method so that you can let Autofac inject the type-list into a construtor. It requires you to implement the IRegistrationSource.

In my case I wanted to get all registered types derived from IConsoleCommand like that:

public Help(TypeList<IConsoleCommand> commands)
{
    _commands = commands;
}

I used a simple DTO-List to carry the types and the T that I wanted to resolve them for:

public class TypeList<T> : List<Type>
{
    public TypeList(IEnumerable<Type> types) : base(types)
    {
    }
}

The actual registration source is implemented as following where the <T> from the TypeList<T> is used to match the type of interface that is registered and that we want to retrieve.

internal class TypeListSource<T> : IRegistrationSource
{
    public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
    {
        if (service is IServiceWithType swt && typeof(TypeList<T>).IsAssignableFrom(swt.ServiceType))
        {
            var registration =
                new ComponentRegistration(
                    id: Guid.NewGuid(),
                    activator: new DelegateActivator(swt.ServiceType, (context, p) =>
                    {
                        var types =
                            context
                                .ComponentRegistry
                                .RegistrationsFor(new TypedService(typeof(T)))
                                .Select(r => r.Activator)
                                .OfType<ReflectionActivator>()
                                .Select(activator => activator.LimitType);
                        return new TypeList<T>(types);
                    }),
                    services: new[] {service},
                    lifetime: new CurrentScopeLifetime(),
                    sharing: InstanceSharing.None,
                    ownership: InstanceOwnership.OwnedByLifetimeScope,
                    metadata: new Dictionary<string, object>()
                );
            return new IComponentRegistration[] {registration};
        }
        // It's not a request for the base handler type, so skip it.
        else
        {
            return Enumerable.Empty<IComponentRegistration>();
        }
    }

    public bool IsAdapterForIndividualComponents => false;
}

Finally you have to add it to the builder with:

builder.RegisterSource(new TypeListSource<IConsoleCommand>());

Now Autofac can resolve the types with dependency injection.

like image 3
t3chb0t Avatar answered Nov 20 '22 01:11

t3chb0t