Autofac sub-dependencies chain registration

和自甴很熟 提交于 2019-12-04 08:39:34

OK, so I figured out a way to perform the sub-dependency chaining. Again, the desire is to call resolve once per root composition object and have all sub-dependencies satisfied the entire chain down. Not looking to get into a religious debate about best practices - whether or not factory methods, service locators, etc. should be implemented. I intentionally left out IoC scoping as it is not the subject of my original question.

This contrived example does not implement email or printer functionality, but it is stubbed out now. The whole point is to show how to pre-define the entire dependency chain. Now automated unit tests will be easier to implement.

Code

Main Program

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();
        }
    }
}

Interfaces

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

Concrete Implementations

Will show the leaf dependencies first...

Printer

using System;

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

Printer Processor

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();
        }
    }
}

Email Processor

using System;

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

Standard Receipt Formatter

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

Gift Receipt Formatter

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

Email Receipt Formatter

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

Receipt

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

Conclusion

Most of the noise associated with objects that need to be created is bundled into one location. In practice I would likely move the registration to its own chunk of code (Perhaps a static class). Keeping the noise out of the functional classes yields nice clean code with a focus on functional intent. All the wire-up is early in the process, and now out of the way.

Looking at the registration in particular, my contrived example has 4 layers of dependencies..

  1. Main
  2. Receipt object
  3. Processors and Formatters
  4. Printer (Only used on Printer Processor)(Could probably express an email server dependency, but I think this example is fairly clear as-is).

By using the Autofac.Core.ResolvedParameter we can reference other registered objects (by name). This will keep the registration long, but flat. Any hierarchy is expressed (shallowly - if that's a word) as parent-child only, and very re-usable. Resolve is only called on the root composition objects - the 3 receipt engines (Standard, Gift, and Email). For each root composition object the entire chain of dependencies is now resolvable.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!