Autofac子依赖项链注册

本文关键字:注册 项链 依赖 Autofac | 更新日期: 2023-09-27 18:25:04

问题

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

场景

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

但是我需要打印3种收据

  1. 标准收据
  2. 礼品收据
  3. 电子邮件回执

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

现在,在我需要决定的根本组成部分,我是在解决用于标准收据、礼品收据还是电子邮件收据的IReceipt。我想调用传递必要参数的容器Resolve()方法,以便它解析正确的IReceipt。此外,我希望注册ContainerBuilder知道,如果我试图解决IReceipt标准收据的具体实现,它需要使用正确的标准收据I格式器和正确的标准回执I处理器来解决子依赖项。礼品收据场景和电子邮件收据场景也是如此。

回顾

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

Autofac子依赖项链注册

好的,所以我找到了一种执行子依赖关系链接的方法。同样,我们希望每个根组合对象调用resolve一次,并使所有子依赖项都满足整个链。不想卷入关于最佳实践的宗教辩论——是否应该实施工厂方法、服务定位器等。我故意省略了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();
        }
    }
}

接口

IP打印机

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

IReceipt

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

I处理器

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

I格式化程序

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. Receipt对象
  3. 处理器和格式化程序
  4. 打印机(仅在打印机处理器上使用)(可能表示对电子邮件服务器的依赖,但我认为这个例子非常清楚)

通过使用Autofac.Core.ResolvedParameter,我们可以引用其他注册的对象(按名称)。这将使注册时间长,但保持不变。任何层次结构都只表示为父子关系,并且非常可重用。Resolve只在根组合对象上调用,即3个收据引擎(Standard、Gift和Email)。对于每个根组合对象,整个依赖关系链现在都是可解析的。