IoC,Dll参考和汇编扫描

时间:2009-04-30 21:00:20

标签: c# inversion-of-control structuremap

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

  

使用IoC连接组件时   容器代码(相反   要通过 xml 进行配置吗?   通常需要明确的项目/构建   对所有集合的引用?

为什么单独的组件?这是因为:


  

“居住在的一个抽象类   将混凝土与其混凝土分开   实现是一个很好的方法   实现这种分离。“ - 框架   设计指南p.91


示例:

假设我有 PersonBase.dll Bob.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

4 个答案:

答案 0 :(得分:14)

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

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

答案 1 :(得分:1)

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

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

答案 2 :(得分:0)

自动扫描选项仅在保留命名,汇编和命名空间约定时有效。您可以使用流畅的界面手动配置structuremap。示例:

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

答案 3 :(得分: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实例并将它们注册到容器上。对我们来说,这个注册所有服务都存在于Web应用程序中,但是在单独的程序集中可以(并且在许多情况下更可取)。

通过这种方式,我们可以将域逻辑与基础架构代码隔离开来,并根据对基础架构代码的直接引用,防止应用程序结束的“只需一次”解决方法。我们还避免在每个Services实现中都有对容器的引用。

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

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