StructureMap和nTier应用程序

时间:2015-02-14 16:31:26

标签: c# asp.net-mvc inversion-of-control structuremap n-tier-architecture

想知道是否有人可以按我的方式提出一些指导。我的标准应用程序设置一直是nTier应用程序(演示文稿,业务,数据和通常的常见)。我已经避免设置和IoC容器(在其他人的应用程序中使用它们很长时间,只是没有设置它们),只要我能,但最终不得不冒险。

我对IoC的理解允许依赖注入,这反过来又可以使单元测试成为可能(更容易),所以在我的脑海中,我想至少在业务层上执行单元测试....但每个例子像StructureMap一样设置IoC使得表示层上的IoC成为可能。那么......我要问的是什么是最佳实践'适用于具有IoC的nTier App。

感谢。

1 个答案:

答案 0 :(得分:2)

DI的主要好处不是单元测试(尽管这肯定是一个好处)。主要好处是松散耦合。 “可测试”的应用程序不一定是松散耦合的。

然而,松散耦合带来的不仅仅是可测试性。

疏松耦合的好处

  1. 延迟绑定(服务可以与其他服务交换,通常而不更改现有代码
  2. 可扩展性(代码可以扩展,通常而不会更改现有代码
  3. 并行开发(定义了多个开发人员可以遵守的抽象合同)
  4. 可维护性(责任明确定义的类更容易维护)
  5. 可测试性(类更容易测试)。
  6. 恕我直言,当DI与软件模式相结合时,可扩展性绝对是主要的好处。请考虑以下类型:

    public interface IWriter
    {
        void WriteSomething();
    }
    
    public interface ISomeService
    {
        void Write();
    }
    

    您可以使用Decorator Pattern扩展服务:

    public class NullWriter : IWriter
    {
        public void WriteSomething()
        {
            // Do nothing - this is a "null object pattern".
        }
    }
    
    public class HelloWriter : IWriter
    {
        public readonly IWriter innerWriter;
    
        public HelloWriter(IWriter innerWriter)
        {
            if (innerWriter == null)
                throw new ArgumentNullException("innerWriter");
            this.innerWriter = innerWriter;
        }
    
        public void WriteSomething()
        {
            this.innerWriter.WriteSomething();
    
            Console.WriteLine("Hello.");
        }
    }
    
    public class GoodbyeWriter : IWriter
    {
        public readonly IWriter innerWriter;
    
        public GoodbyeWriter(IWriter innerWriter)
        {
            if (innerWriter == null)
                throw new ArgumentNullException("innerWriter");
            this.innerWriter = innerWriter;
        }
    
        public void WriteSomething()
        {
            this.innerWriter.WriteSomething();
    
            Console.WriteLine("Goodbye.");
        }
    }
    
    public class SomeService : ISomeService
    {
        private readonly IWriter writer;
    
        public SomeService(IWriter writer)
        {
            if (writer == null)
                throw new ArgumentNullException("writer");
        }
    
        public void Write()
        {
            this.writer.WriteSomething();
        }
    }
    

    上面的内容就像是:

    // Composition Root
    var nullWriter = new NullWriter();
    var goodbyeWriter = new GoodbyeWriter(nullWriter);
    var helloWriter = new HelloWriter(goodbyeWriter);
    var service = new SomeService(helloWriter);
    // End Composition Root
    
    // Execute
    service.Write();
    
    //Writes:
    
    //Hello.
    //Goodbye.
    

    现在,已设置方案,您可以扩展SomeService所做的而不更改任何现有类型。需要更改的应用程序的唯一部分是composition root

    public class HowAreYouWriter : IWriter
    {
        public readonly IWriter innerWriter;
    
        public HowAreYouWriter(IWriter innerWriter)
        {
            if (innerWriter == null)
                throw new ArgumentNullException("innerWriter");
            this.innerWriter = innerWriter;
        }
    
        public void WriteSomething()
        {
            this.innerWriter.WriteSomething();
    
            Console.WriteLine("How are you?");
        }
    }
    
    // Composition Root
    var nullWriter = new NullWriter();
    var goodbyeWriter = new GoodbyeWriter(nullWriter);
    var howAreYouWriter = new HowAreYouWriter(goodbyeWriter);
    var helloWriter = new HelloWriter(howAreYouWriter);
    var service = new SomeService(helloWriter);
    // End Composition Root
    
    // Execute
    service.Write();
    
    //Writes:
    
    //Hello.
    //How are you?
    //Goodbye.
    

    约定优于配置

    DI的另一个(经常被忽视的)好处是Convention over Configuration。将构造函数注入与DI容器组合时,其中许多容器可以自动将ISomeService映射到SomeService。一些容器(如StructureMap)也可以构建自己的约定。

    好处并不明显,因为在您使用约定注册数十种类型之前,它确实无法获得回报。但是,如果您使用它们,可以大大减少编写应用程序所需的代码量。

    N-Tier App

    对于单个应用程序,通常只有一个composition root尽可能靠近应用程序的入口点。在MVC中,这将在HttpApplication.Start方法中。

    但是,这可能会有所不同,具体取决于您是将应用程序设计的图层视为DI Friendly Libraries还是DI Friendly Frameworks,以及您是否认为应用程序的某个部分是“插件”在构建之后添加(基本上,创建一个可以加载动态依赖关系的组合根)。

    解决此问题通常有3种方法:

    1. 将所有类型公开,并在包含表示层的同一项目中组合它们。
    2. 将组合根放入每个图层,并使每个图层的公共API在内部使用DI。然后使用主项目组成图层的公共API。通常,您还需要在从主Compose方法调用的每个图层上制作公共Compose方法。
    3. 制作一个单独的合成根项目,将所有部分组合在一起。
    4. 我甚至看到有些人建议将所有“层”放入一个项目中,如果你不打算单独使用这些部分,那就不会有太大的缺点了。 NuGet Gallery是一个以这种方式构建的项目。

      恕我直言,将所有内容公开并将组合根目录放在主应用程序中通常是单个多层应用程序的最佳选择,其中的部分不打算与其他应用程序一起使用。

      最后的词

      如果您认真学习DI,请选择Mark Seemann撰写的书Dependency Injection in .NET。您可能不会认为DI本身就是一个足够大的开发领域,但本书确实提供了许多优势,而不仅仅是DI,例如SOLID原则。