سؤال

Given the following code that resolves instances of IProcessor

How do I get autofac to resolve and create these for my service consumer?
I need autofac to use this function, or something similar to this function to create the relevant strategy for a corresponding item. These strategies need to be created in such a way that their dependencies are correctly resolved.

Ideally this needs to happen in the COMPOSITION ROOT of the application. The code below does not correctly use the container to build instances. StrategyA and StrategyB in real code would have dependencies of their own. They would also need to be disposed when the consumer is disposed.

Container
I have tried this, and get an error saying that the resolve operation has already happened.

var builder = new Autofac.ContainerBuilder();
builder.RegisterType<StrategyA>().As<IProcessor>().Keyed<IProcessor>(typeof(ItemA).Name).InstancePerDependency();
builder.RegisterType<StrategyB>().As<IProcessor>().Keyed<IProcessor>(typeof(ItemB).Name).InstancePerDependency();
builder.Register<Func<string, IProcessor>>(c => (s) => c.ResolveKeyed<IProcessor>(s));
builder.RegisterType<MyServiceConsumer>().As<IConsumer>();

var container = builder.Build();
var consumer = container.Resolve<IConsumer>().DoStuff(new ItemA()).Dump();

The service consumer.

public class MyServiceConsumer : IConsumer {
    Func<string, IProcessor> processor;
    public MyServiceConsumer(Func<string, IProcessor> processor) {
        //processor.Dump("px");
        this.processor = processor;
    }
    public string DoStuff(IItem item) {
        return processor(item.GetType().Name).ProcessItem(item);
    }
}

Here are the interfaces.

public interface IConsumer { string DoStuff(IItem item); }
public interface IProcessor { string ProcessItem(IItem item); }
public interface IItem { string Name { get; } }
public interface IItemStrategy<in T> : IProcessor where T : IItem { string ProcessItem(T item); }

Here are the concrete classes.

public class ItemA : IItem { public string Name { get { return "A"; } } public string UniqueA { get { return "+ UA"; } } }
public class ItemB : IItem { public string Name { get { return "B"; } } public string UniqueB { get { return "+ UB"; } } }

The strategy implementations. I hope I am applying the pattern correctly, I do want strongly typed strategies ideally?

public class StrategyA : IItemStrategy<ItemA> { 
    string IProcessor.ProcessItem(IItem item) { Debug.Assert(item is ItemA); return this.ProcessItem((ItemA)item); }
    public string ProcessItem(ItemA item) { return "PA " + item.Name + item.UniqueA; } 
}
public class StrategyB : IItemStrategy<ItemB> { 
    string IProcessor.ProcessItem(IItem item) { Debug.Assert(item is ItemB); return this.ProcessItem((ItemB)item); }
    public string ProcessItem(ItemB item) { return "PB " + item.Name + item.UniqueB; } 
}
هل كانت مفيدة؟

المحلول

I have figured it out.
The hint was here autofac registration issue in release v2.4.5.724

var builder = new Autofac.ContainerBuilder();
builder.RegisterType<StrategyA>().As<IProcessor>().Keyed<IProcessor>(typeof(ItemA).Name).InstancePerDependency();
builder.RegisterType<StrategyB>().As<IProcessor>().Keyed<IProcessor>(typeof(ItemB).Name).InstancePerDependency();
builder.Register<Func<string, IProcessor>>(c => {
    var ctx = c.Resolve<IComponentContext>();
    return (s) => ctx.ResolveKeyed<IProcessor>(s);
});
builder.RegisterType<MyServiceConsumer>().As<IConsumer>();

var container = builder.Build();

var consumer = container.Resolve<IConsumer>().DoStuff(new ItemB()).Dump();

نصائح أخرى

I think you're trying too hard to avoid DI inside your code itself. There's nothing wrong with calling .Resolve off your container.

I would add the following method declaration to IItem:

IProcessor GetStrategy(IContainer container);

Add the following implementation into ItemA:

public IProcessor GetStrategy(IContainer container)
{
    return container.Resolve<StrategyA>();
}

and the following implementation into ItemB:

public IProcessor GetStrategy(IContainer container)
{
    return container.Resolve<StrategyB>();
}

Finally, you change the constructor of MyServiceConsumer to:

public MyServiceConsumer(IItem item, IContainer container)
{
    this.item = item;
    this.processor = item.GetStrategy(container);
}

Change your caller to pass in the container, and away you go. If you'd prefer, make the container static to avoid passing it around, although this could impact testability.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top