10

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

在代码中使用 IoC 容器连接组件(而不是通过xml配置)时,您通常需要对所有程序集的显式项目/构建引用吗?

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


“抽象类位于与其具体实现不同的程序集中,是实现这种分离的好方法。” -框架设计指南 p.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

没运气。什么是明确的,我想要鲍勃:

//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 和扫描程序集

4

4 回答 4

15

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

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

与组件

  • 核心程序
  • PersonBase.dll(Core.exe 引用的编译时间
  • Bob.dll(通过 StructureMap 扫描 加载运行时)
  • Betty.dll(通过 StructureMap Scan 加载运行时)

为了使用 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); } );
于 2009-05-03T05:04:14.280 回答
1

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

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

于 2009-04-30T21:12:09.877 回答
0

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

ObjectFactory.Initialize(initialization => 
   initialization.ForRequestedType<PersonBase>()
    .TheDefault.Is.OfConcreteType<Bob>());
于 2009-04-30T21:25:12.060 回答
0

我们在我当前的项目上做了什么(它使用 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 应用程序中,但是可以(并且在许多情况下更可取)将它放在一个单独的程序集中。

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

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

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

于 2009-05-02T20:02:23.753 回答