Autofac配置策略工厂模式(已经发生解析)

3
给定以下解决IProcessor实例的代码
我该如何让autofac解析并为我的服务消费者创建这些实例?
我需要autofac使用此函数或类似于此函数的函数来创建相应项的相关策略。这些策略需要以正确的方式创建,以便正确解析其依赖项。
理想情况下,这需要在应用程序的组合根中发生。 下面的代码没有正确地使用容器来构建实例。在真实代码中,StrategyAStrategyB将具有自己的依赖项。当消费者被处理时,它们还需要被处理。 容器
我已经尝试过这样做,并收到错误消息,说明解析操作已经发生。
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();
服务消费者。
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);
    }
}

以下是接口。
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); }

这里是具体类。

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"; } } }

策略实现。 我希望我正确地应用了这种模式,最好是强类型的策略?

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; } 
}
2个回答

3

我已经解决了这个问题。
提示在这里: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();

每次我注册一个 Func,这都会让我感到困扰 : )。幸运的是,异常消息非常描述详细。 - Cristian Diaconescu

0

我认为你过于努力地避免在代码本身中使用 DI。从容器中调用 .Resolve 没有任何问题。

我会在 IItem 中添加以下方法声明:

IProcessor GetStrategy(IContainer container);

将以下实现添加到ItemA中:
public IProcessor GetStrategy(IContainer container)
{
    return container.Resolve<StrategyA>();
}

并将以下实现应用于ItemB:

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

最后,您将MyServiceConsumer的构造函数更改为:

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

将调用者更改为传递容器,然后就可以开始了。如果您愿意,可以将容器设置为静态以避免传递它,但这可能会影响可测试性。


这不是一个坏建议,我正在尝试遵循使用组合根的模式。也许我用同样的方法打得太多了。(http://blog.ploeh.dk/2011/07/28/CompositionRoot/) 如果容器能够解决依赖关系,那就太好了。 - Jim
1
我猜问题在于我的解决方案有点演变成了服务定位器模式,而不是 DI 基础设施构建该对象。 - Sean Hederman
我想是的,服务定位器现在已经成为一种反模式。参考资料可以看这篇文章:http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/ - Jim
在构造函数中不应使用IContainer。相反,您应该使用ILifetimeScope - 这将自动注入,并对应于环境范围。IContainer不能保证相同。公开相同的解析方法。第一选择是使用内置的Func<MyStrategy>选项来解决策略。但从配置角度来看,这更加棘手。 - Jim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接