IoC,Dll参考和汇编扫描

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

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

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


“与其具体实现相关的独立程序集中的抽象类是实现这种分离的好方法。” – 框架设计指南第91页


例:

假设我有PersonBase.dllBob.dll

Bobinheritance自抽象类PersonBase 。 它们都在Person命名空间中。 但在不同的集会中

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

回到我的主要代码中,我需要一个人。 StructureMap可以扫描assembly体。 好的,我会问一下StructureMap!

现在,在我的主要代码中,我当然只是指PersonBase ,而不是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().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().Count == 1 //there he is! 

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

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

我是否遗漏了使用StructureMap的东西,或者作为一般原则,(流畅的)IoC配置是否需要明确引用所有程序集?

可能相关的问题: StructureMap和扫描组件

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

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

与组件

  • Core.exe
  • PersonBase.dll(由Core.exe引用编译时
  • Bob.dll(通过StructureMap Scan 加载运行时
  • 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() .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(); } )); ObjectFactory.GetAllInstances() .ToList() .ForEach(p => { Console.WriteLine(p.FirstName); } ); 

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

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

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

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

我们在当前项目中做了什么(使用AutoFac,而不是StructureMap,但我认为它应该没有区别):

我们有定义应用程序在核心程序App.Core使用的外部服务的接口,比如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 GetServices(); } 

…应用程序通过reflection找到此ServiceRegistry并枚举这些ServiceInfo实例并将它们注册到容器上。 对于我们来说,这个注册所有服务都存在于Web应用程序中,但是在单独的程序集中可以(并且在许多情况下更可取)。

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

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

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