简体   繁体   中英

Autofac. Naming resolving and child dependencies

Does the named/keyed resolving also resolve child dependencies using a name? Or named resolving applies Name/Key only for resolved type without its child dependencies?

Error occurs:

None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'ConsoleApplication1.Program+MainClassOne' can be invoked with the available services and parameters: Cannot resolve parameter 'INamed named' of constructor 'Void.ctor(INamed)'.

class Program
{
    static void Main(string[] args)
    {
        var container = new ContainerBuilder();
        container.RegisterType<NamedB>().Named<INamed>("B");
        container.RegisterType<NamedA>().Named<INamed>("A");
        container.RegisterType<MainClassOne>().Named<MainClassOne>("A");
        container.RegisterType<MainClassTwo>().Named<MainClassTwo>("B");

        var di = container.Build();
        var a = di.ResolveNamed<MainClassTwo>("B");
        var a2 = di.ResolveNamed<MainClassOne>("A");

    }

    public class MainClassOne
    {
        public MainClassOne(INamed named)
        {
            Console.WriteLine("MainClassOne= " + named.Name);
        }
    }

    public class MainClassTwo
    {
        public MainClassTwo(INamed named)
        {
            Console.WriteLine("MainClassTwo= " + named.Name);
        }
    }

    public interface INamed
    {
       string Name { get; set; }
    }

    public class NamedA : INamed
    {
        public string Name
        {
            get { return "A"; }
            set { }
        }
    }

    public class NamedB : INamed
    {
        public string Name
        {
            get { return "B"; }
            set { }
        }
    }
}

The key is only used when resolving the requested service. If the key was passed to subsequent resolve operations it would require all services in the dependency graph to also be registered with the same key.

What you are trying to achieve can be done using lambda based registrations that make the dependencies for your main classes explicit.

container.RegisterType<NamedA>().Named<INamed>("A");    
container.RegisterType<NamedB>().Named<INamed>("B");
container.Register(c => new MainClassOne(c.ResolveNamed<INamed>("A"))).Named<MainClassOne>("A");
container.Register(c => new MainClassTwo(c.ResolveNamed<INamed>("B"))).Named<MainClassTwo>("B");

This will allow you to resolve the main classes using their name while ensuring that they also receive the correct named dependency.

Update your registration to look like this:

        container.RegisterType<NamedB>().Named<INamed>("B");
        container.RegisterType<NamedA>().Named<INamed>("A");

        container.RegisterType<MainClassOne>()
            .Named<MainClassOne>("A")
            .WithParameter(
                new ResolvedParameter(
                    (pi, ctx) => pi.Name == "named",
                    (pi, ctx) => ctx.ResolveNamed<INamed>("A")));

        container.RegisterType<MainClassTwo>()
            .Named<MainClassTwo>("B")
            .WithParameter(
                new ResolvedParameter(
                    (pi, ctx) => pi.Name == "named",
                    (pi, ctx) => ctx.ResolveNamed<INamed>("B")));

There are probably ways to make this look neater (extension methods etc). Also, consider using Keyed rather than Named parameters.

The "ResolvedParameter" class takes two delegates - the first is a selector, and allows you to determine whether or not the referenced constructor parameter is relevant, and if it is, the second delegate is executed to provide a value.

Take a look at keyed services and select by context for further information.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM