【问题标题】:IoC, Dll References, and Assembly ScanningIoC、Dll 引用和程序集扫描
【发布时间】:2009-04-30 21:00:20
【问题描述】:

虽然这个问题与 StructureMap 有关,但我的一般问题是:

使用 IoC 连接组件时 容器在代码中(相反 通过 xml 进行配置)你呢 通常需要明确的项目/构建 引用所有程序集?

为什么是单独的程序集?因为:


"抽象类驻留在 从混凝土中分离组装 实现是一个很好的方法 实现这样的分离。” -框架 设计指南第 91 页


例子:

假设我有 PersonBase.dllBob.dll

Bob 继承自抽象类 PersonBase。它们都在 Person 命名空间中。 但在不同的程序集中

我正在为 PersonBase 编程,而不是 Bob

回到我的主要代码,我需要一个人。 StructureMap 可以扫描程序集。太好了,我会向 StructureMap 要一个!

现在,在我的主要代码中,我当然只指PersonBase,而不是Bob。我实际上不希望我的代码知道关于Bob任何事情。没有项目参考,没有 nuthin。这就是重点。

所以我想说:

//Reference: PersonBase.dll (only)
using Person;  
...

//this is as much as we'll ever be specific about Bob:
Scan( x=> { x.Assembly("Bob.dll"); }

//Ok, I should now have something that's a PersonBase (Bob). But no ?
ObjectFactory.GetAllInstances<PersonBase>().Count == 0

没有运气。明确我想要 Bob 的工作是什么:

//Reference: PersonBase.dll and Bob.dll
using Person; 
...
Scan( x => {x.Assembly("Bob.dll"); }

//If I'm explicit, it works. But Bob's just a PersonBase, what gives?
ObjectFactory.GetAllInstances<Bob>().Count == 1 //there he is!

但现在我不得不在我的项目中引用 Bob.dll,这正是我不想要的。

我可以使用 Spring + Xml 配置来避免这种情况。但后来我又回到了 Spring + Xml 配置......!

我是否在使用时遗漏了什么 StructureMap,或作为一般 原则,做(流利的)IoC 配置需要显式引用 到所有程序集?

可能相关问题:StructureMap and scanning assemblies

【问题讨论】:

    标签: c# inversion-of-control structuremap


    【解决方案1】:

    我终于解决了这个问题。它看起来像这样:

    IoC Uml http://img396.imageshack.us/img396/1343/iocuml.jpg

    与组件

    • Core.exe
    • PersonBase.dll(Core.exe 引用了编译时间
    • Bob.dll(通过 StructureMap 扫描加载运行时
    • Betty.dll(通过 StructureMap 扫描加载运行时间

    要使用 StructureMap 获得它,我需要一个自定义的“ITypeScanner”来支持扫描程序集:

    public class MyScanner : ITypeScanner {
      public void Process(Type type, PluginGraph graph) {
    
        if(type.BaseType == null) return;
    
        if(type.BaseType.Equals(typeof(PersonBase))) {
          graph.Configure(x => 
            x.ForRequestedType<PersonBase>()
            .TheDefault.Is.OfConcreteType(type));
        }
      }
    } 
    

    所以我的主要代码如下:

    ObjectFactory.Configure(x => x.Scan (
      scan =>
      {
        scan.AssembliesFromPath(Environment.CurrentDirectory 
        /*, filter=>filter.You.Could.Filter.Here*/);
    
        //scan.WithDefaultConventions(); //doesn't do it
    
        scan.With<MyScanner>();
      }
    ));
    
    ObjectFactory.GetAllInstances<PersonBase>()
     .ToList()
      .ForEach(p => 
      { Console.WriteLine(p.FirstName); } );
    

    【讨论】:

    【解决方案2】:

    您也可以使用 StructureMap 进行 xml 配置。如果你愿意,你甚至可以混合它们。

    您还可以将 StructureMap 属性放入 Bob 类中,以告诉 StructureMap 如何加载程序集。 DefaultConstructor 是我不时使用的一个。

    【讨论】:

    • 感谢克里斯的回复 :) 如果我必须回到 Xml 接线,我会坚持使用 Spring。我认为我上面的内容在结构/概念上是合理的。我的问题是,Ioc-configured-in-code 似乎始终需要我特别想避免的 dll 引用。
    • 没问题。 spring.net 也没有任何问题。但请注意不要过度设计自己。您向解决方案引入的项目越多,构建所需的时间就越长。您制作的东西越可配置,您创建的测试表面就越多。
    【解决方案3】:

    仅当您保持命名、程序集和命名空间约定时,自动扫描选项才有效。您可以使用流畅的界面手动配置结构图。示例:

    ObjectFactory.Initialize(initialization => 
       initialization.ForRequestedType<PersonBase>()
        .TheDefault.Is.OfConcreteType<Bob>());
    

    【讨论】:

    • 在这种情况下——这基本上是我的第二种情况,但涉及到 dll 引用——我需要对 Bob.dll 的 dll 引用。这是我想要避免的。
    • [...] .Is.OfConcreteType() //
    • 我明白你的意思,但是仍然不可能建立一个不依赖于你要配置的类型的 IoC 容器。
    • 解决了这个问题 - 请参阅下面的答案
    【解决方案4】:

    我们在我当前的项目上做了什么(它使用 AutoFac,而不是 StructureMap,但我认为它不应该有所作为):

    我们有定义应用程序在核心程序集中使用的外部服务的接口,比如App.Core(比如您的 PersonBase)。

    然后我们在Services.Real 中实现了这些接口(如 Bob.dll)。

    在我们的例子中,我们还有Service.Fake,用于促进依赖其他企业服务和数据库等的 UI 测试。

    前端“客户端”应用程序本身(在我们的例子中是 ASP.NET MVC 应用程序)引用 App.Core

    当应用启动时,我们使用Assembly.Load 根据配置设置加载适当的“服务”实现 DLL。

    这些 DLL 中的每一个都有一个 IServiceRegistry 实现,它返回它实现的服务列表:

    public enum LifestyleType { Singleton, Transient, PerRequest}
    
    public class ServiceInfo {
        public Type InterfaceType {get;set;}
        public Type ImplementationType {get;set;}
        // this might or might not be useful for your app, 
        // depending on the types of services, etc.
        public LifestyleType Lifestyle {get;set;} 
    }
    
    public interface IServiceRegistry {
        IEnumerable<ServiceInfo> GetServices();
    }
    

    ...应用程序通过反射找到这个ServiceRegistry,并枚举这些ServiceInfo实例并将它们注册到容器上。对我们来说,这个 register-all-services 存在于 Web 应用程序中,但可以(并且在许多情况下更可取)将它放在一个单独的程序集中。

    通过这种方式,我们可以将域逻辑与基础架构代码隔离开来,并防止应用程序最终依赖于对基础架构代码的直接引用而出现“仅此一次”的变通方法。我们还避免了在每个服务实现中都必须引用容器。

    如果你这样做,一件非常重要的事情:确保你有测试来验证你可以创建每个“顶级”类型(在我们的例子中,ASP.NET MVC 控制器) IOC 容器的每个潜在配置。

    否则,很容易忘记实现一个接口并破坏应用程序的大部分部分。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-08-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-06
      • 1970-01-01
      相关资源
      最近更新 更多