Autofac子依赖链注册

时间:2014-09-30 17:43:05

标签: c# dependency-injection autofac

问题

如何构建AutoFac ContainerBuilder,以便正确解析我的子依赖关系(假设接口的多个具体实现)?默认注册/解析将不起作用,因为我有多个子依赖项的具体实现,并且子依赖项解析依赖于主对象的分辨率。在所有情况下,我都希望使用构造函数注入。

方案

例如,假设我需要打印收据,因此我创建了一个名为IReceipt的接口,其中包含一个名为PrintReceipt()的方法。

但我需要打印3种收据

  1. 标准收据
  2. 礼品收据
  3. 电子邮件收据
  4. 所有收据类型都有不同的格式,电子邮件收据根本不打印,而是通过电子邮件发送。所以我希望我的IReceipt能够依赖格式化器和处理器。假设我使用Process()方法创建IProcessor,它可以是打印机处理器,也可以是电子邮件处理器(打印机处理器负责打印机通信,电子邮件处理器负责与SMTP服务器通信) 。此外,我创建了一个IFormatter,它可以将输入提供给处理器对象,格式化为标准收据,或礼品收据,甚至是电子邮件收据的HTML。因此,IReciept的任何具体实现的构造函数现在需要两个依赖项 - 一个IFormatter和一个IProcessor。

    现在,根据我需要决定的根构成,我是否正在解决用于标准收据,礼品收据或电子邮件收据的IReceipt。我想调用容器Resolve()方法传递必要的参数,以便它解析正确的IReceipt。此外,我希望注册ContainerBuilder知道,如果我尝试解决IReceipt标准收据的具体实现,它需要使用正确的标准收据IFormatter解决子依赖关系,并更正标准收据IProcessor。对于礼品收据方案和电子邮件收据方案也是如此。

    小结

    所以 - 在所有这些 - 我的问题是 - 如何构造ContainerBuilder,以便在设计时定义子依赖,并且对Resolve()的单个调用将正确识别所需的具体实现?我不是在寻求与打印机对话或发布HTML的解决方案。此问题严格适用于Autofac注册和解决方法。在不同的客户端,我使用CastleWindsor使用了这个确切的策略,但我当前的客户端使用的是Autofac。

1 个答案:

答案 0 :(得分:11)

好的,所以我想出了一种执行子依赖链接的方法。同样,希望每个根组合对象调用一次解析,并使所有子依赖关系满足整个链。不打算就最佳实践进行宗教辩论 - 无论是否应实施工厂方法,服务定位器等。我故意遗漏了IoC范围,因为它不是我原来问题的主题。

这个人为的例子没有实现电子邮件或打印机功能,但它现在被删除了。重点是展示如何预定义整个依赖关系链。现在,自动化单元测试将更容易实现。

代码

主程序

using System;
using System.Collections.Generic;
using Autofac;

namespace MyAutoFacTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // CREATE THE IOC ENGINE AND CONTAINER
            var builder = new Autofac.ContainerBuilder();
            Autofac.IContainer container;

            // CREATE THE DEPENDENCY CHAIN REGISTRATION
            builder.RegisterType<Printer>()
                .Named<IPrinter>("My Default Printer");

            builder.RegisterType<PrinterProcessor>()
                .Named<IProcessor>("PrinterProcessor")
                .WithParameter(Autofac.Core.ResolvedParameter.ForNamed<IPrinter>("My Default Printer"));

            builder.RegisterType<EmailProcessor>()
                .Named<IProcessor>("EmailProcessor");

            builder.RegisterType<StandardReceiptFormatter>()
                .Named<IFormatter>("StandardReceiptFormatter");

            builder.RegisterType<GiftReceiptFormatter>()
                .Named<IFormatter>("GiftReceiptFormatter");

            builder.RegisterType<EmailReceiptFormatter>()
                .Named<IFormatter>("EmailReceiptFormatter");

            builder.RegisterType<Receipt>()
                .Named<IReceipt>("StandardReceipt")
                .WithParameter(Autofac.Core.ResolvedParameter.ForNamed<IProcessor>("PrinterProcessor"))
                .WithParameter(Autofac.Core.ResolvedParameter.ForNamed<IFormatter>("StandardReceiptFormatter"));

            builder.RegisterType<Receipt>()
                .Named<IReceipt>("GiftReceipt")
                .WithParameter(Autofac.Core.ResolvedParameter.ForNamed<IProcessor>("PrinterProcessor"))
                .WithParameter(Autofac.Core.ResolvedParameter.ForNamed<IFormatter>("GiftReceiptFormatter"));

            builder.RegisterType<Receipt>()
                .Named<IReceipt>("EmailReceipt")
                .WithParameter(Autofac.Core.ResolvedParameter.ForNamed<IProcessor>("EmailProcessor"))
                .WithParameter(Autofac.Core.ResolvedParameter.ForNamed<IFormatter>("EmailReceiptFormatter"));

            // COMPILE THE AUTOFAC REGISTRATION
            container = builder.Build();

            // SETUP INITIALIZATION STUFF - THINGS THAT WOULD ORDINARILY HAPPEN AS PART OF EXTERNAL SYSTEMS INTEGRATION
            int someBogusDatabaseIdentifier = 1;

            var standardReceipt = container.ResolveNamed<IReceipt>("StandardReceipt");
            standardReceipt.PrintReceipt(someBogusDatabaseIdentifier);

            var giftReceipt = container.ResolveNamed<IReceipt>("GiftReceipt");
            giftReceipt.PrintReceipt(someBogusDatabaseIdentifier);

            var emailReceipt = container.ResolveNamed<IReceipt>("EmailReceipt");
            emailReceipt.PrintReceipt(someBogusDatabaseIdentifier);

            Console.ReadLine();
        }
    }
}

接口

<强> IPrinter

namespace MyAutoFacTest
{
    public interface IPrinter
    {
        void Print();
    }
}

<强> IReceipt

namespace MyAutoFacTest
{
    public interface IReceipt
    {
        void PrintReceipt(int id);
    }
}

<强> IProcessor

namespace MyAutoFacTest
{
    public interface IProcessor
    {
        void Process(string formattedString);
    }
}

<强> IFormatter

namespace MyAutoFacTest
{
    public interface IFormatter
    {
        string GetFormattedString(int id);
    }
}

具体实施

首先显示叶依赖...

<强>打印机

using System;

namespace MyAutoFacTest
{
    public class Printer : IPrinter
    {
        public void Print()
        {
            Console.WriteLine("Printer is printing");
        }
    }
}

打印机处理器

using System;

namespace MyAutoFacTest
{
    public class PrinterProcessor : IProcessor
    {
        private IPrinter _printer;

        public PrinterProcessor(IPrinter printer)
        {
            this._printer = printer;
        }

        public void Process(string formattedString)
        {
            Console.WriteLine("Printer processor sending receipt to printer.");
            this._printer.Print();
        }
    }
}

电子邮件处理器

using System;

namespace MyAutoFacTest
{
    public class EmailProcessor : IProcessor
    {
        public void Process(string formattedString)
        {
            Console.WriteLine("Email Processor sending out an email receipt");
        }
    }
}

标准收据格式化程序

namespace MyAutoFacTest
{
    public class StandardReceiptFormatter : IFormatter
    {
        public string GetFormattedString(int id)
        {
            return "StandardReceiptFormatter formatted string";
        }
    }
}

礼品收据格式化程序

namespace MyAutoFacTest
{
    public class GiftReceiptFormatter : IFormatter
    {
        public string GetFormattedString(int id)
        {
            return "GiftReceiptFormatter formatted string";
        }
    }
}

电子邮件收据格式化程序

namespace MyAutoFacTest
{
    public class EmailReceiptFormatter : IFormatter
    {
        public string GetFormattedString(int id)
        {
            return "EmailReceiptFormatter formatted string";
        }
    }
}

<强>收据

using System;

namespace MyAutoFacTest
{
    public class Receipt : IReceipt
    {
        private IFormatter _formatter;
        private IProcessor _processor;

        public Receipt(IFormatter formatter, IProcessor processor)
        {
            this._formatter = formatter;
            this._processor = processor;
        }

        public Receipt(IFormatter formatter)
        {
            this._formatter = formatter;
        }

        public Receipt(IProcessor processor)
        {
            this._processor = processor;
        }

        public void PrintReceipt(int id)
        {
            var formattedString = this._formatter.GetFormattedString(id);
            Console.WriteLine(formattedString);

            this._processor.Process(formattedString);
        }
    }
}

<强>结论

与需要创建的对象相关联的大多数噪声被捆绑到一个位置。在实践中,我可能会将注册移动到自己的代码块(也许是静态类)。将噪声排除在函数类之外会产生漂亮的干净代码,并且关注功能意图。所有的接线都是在这个过程的早期,现在已经不在了。

特别注意注册,我设计的例子有4层依赖..

  1. Main
  2. 收据对象
  3. 处理器和格式化程序
  4. 打印机(仅用于打印机处理器)(可能表达了对电子邮件服务器的依赖性,但我认为这个示例相当清晰)。
  5. 通过使用Autofac.Core.ResolvedParameter,我们可以引用其他注册对象(按名称)。这将保持注册时间长,但持平。表达任何层次结构(浅层 - 如果这是一个单词)仅作为父子,并且非常可重用。 Resolve仅在根组合对象上调用 - 3个收据引擎(标准,礼品和电子邮件)。对于每个根组合对象,整个依赖关系链现在都是可解析的。