用AutoFac替换出厂

mo.*_*mo. 17 c# autofac factory-pattern

我习惯于创建我自己的工厂(如图所示):

public class ElementFactory
{
    public IElement Create(IHtml dom)
    {
        switch (dom.ElementType)
        {
            case "table":
                return new TableElement(dom);
            case "div":
                return new DivElement(dom);
            case "span":
                return new SpanElement(dom);
        }
        return new PassthroughElement(dom);
    }
}
Run Code Online (Sandbox Code Playgroud)

我终于开始在我当前的项目中使用IoC容器(AutoFac)了,我想知道是否有一些使用AutoFac优雅地实现同样功能的神奇方法?

Kei*_*thS 25

简短回答:是的.

更长的答案:首先,在Foo类被注册为IFoo实现的简单情况下,构造函数参数或类型属性Func<IFoo>将由Autofac自动解决,无需额外布线.Autofac将注入一个基本上container.Resolve<IFoo>()在调用时执行的委托.

在像你这样的更复杂的情况下,返回的确切结果是基于输入参数,你可以做两件事之一.首先,您可以将工厂方法注册为其返回值,以提供参数化分辨率:

builder.Register<IElement>((c, p) => {
    var dom= p.Named<IHtml>("dom");
    switch (dom.ElementType)
    {
        case "table":
            return new TableElement(dom);
        case "div":
            return new DivElement(dom);
        case "span":
            return new SpanElement(dom);
    }
    return new PassthroughElement(dom);
  });

//usage
container.Resolve<IElement>(new NamedParameter("dom", domInstance))
Run Code Online (Sandbox Code Playgroud)

这不是类型安全的(domInstance不会被编译器检查以确保它是一个IHtml),也不是很干净.相反,另一种解决方案是将工厂方法实际注册为Func:

builder.Register<Func<IHtml, IElement>>(dom =>
{
    switch (dom.ElementType)
    {
        case "table":
            return new TableElement(dom);
        case "div":
            return new DivElement(dom);
        case "span":
            return new SpanElement(dom);
    }
    return new PassthroughElement(dom);
});


public class NeedsAnElementFactory //also registered in AutoFac
{
    protected Func<IHtml,IElement> CreateElement {get; private set;}

    //AutoFac will constructor-inject the Func you registered
    //whenever this class is resolved.
    public NeedsAnElementFactory(Func<IHtml,IElement> elementFactory)
    {
        CreateElement = elementFactory;
    }  

    public void MethodUsingElementFactory()
    {
        IHtml domInstance = GetTheDOM();

        var element = CreateElement(domInstance);

        //the next line won't compile;
        //the factory method is strongly typed to IHtml
        var element2 = CreateElement("foo");
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您想将代码保存在ElementFactory中而不是将其放在Autofac模块中,您可以将工厂方法设置为静态并注册(这在您的情况下尤其有效,因为您的工厂方法通常是静态的):

public class ElementFactory
{
    public static IElement Create(IHtml dom)
    {
        switch (dom.ElementType)
        {
            case "table":
                return new TableElement(dom);
            case "div":
                return new DivElement(dom);
            case "span":
                return new SpanElement(dom);
        }
        return new PassthroughElement(dom);
    }
}

...

builder.Register<Func<IHtml, IElement>>(ElementFactory.Create);
Run Code Online (Sandbox Code Playgroud)

  • 好吧,也可以使用“键控注册”,将每个元素类型名称指定为对应对象类型的解析方法的键。这种情况下的复杂之处在于并非所有可能的返回类型都与键相关联(您有一个默认情况),并且由于其他原因需要元素名称的 IHtml 父级。因此,获取您的工作代码并进行设置以便 AutoFac 可以使用它似乎是最简单的。 (3认同)
  • 注册不编译:`builder.Register <Func <IHtml,IElement >>(ElementFactory.Create);`我希望它看起来像这样:`builder.Register <Func <IHtml,IElement >>((context ,parameters)=>(html => ElementFactory.Create(html)));` (2认同)
  • 甚至更简单:context =>`builder.Register <Func <IHtml,IElement >>(context => ElementFactory.Create)`; (2认同)