IoC、Dll 引用和程序集扫描
-
03-07-2019 - |
题
虽然这个问题与 StructureMap 有关,但我的 一般的 问题是:
使用 IoC 连接组件时 容器 在代码中 (相反 通过配置 XML) 你 通常需要显式项目/构建 对所有程序集的引用?
为什么要使用单独的组件?因为:
“抽象类驻留在 将组件与混凝土分开 实现是 实现这种分离。框架设计指南第91页
例子:
假设我有 PersonBase.dll 和 鲍勃.dll
鲍勃 继承自抽象类 人员库. 。他们都在 人 命名空间。 但在不同的集会.
我正在编程 人员库, , 不是 鲍勃.
回到我的主代码,我需要一个人。StructureMap 可以扫描程序集。太棒了,我会向 StructureMap 索取一份!
现在,在我的主代码中,我当然仅指 人员库, ,不 鲍勃. 。我实际上不想让我的代码知道 任何事物 关于 鲍勃. 。没有项目参考,什么也没有。这就是重点。
所以我想说:
//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!
但现在我不得不参考 鲍勃.dll 在我的项目中,这正是我不想要的。
我可以使用 Spring + Xml 配置来避免这种情况。但后来我又回到 Spring + Xml 配置......!
我是否在使用 StructureMap,或作为一般 原理,do(流利)IoC 配置需要显式引用 到所有组件?
可能相关的问题: StructureMap 和扫描组件
解决方案
我终于解决了这个问题。它看起来像这样:
IoC Uml http://img396.imageshack.us/img396/1343/iocuml.jpg
与组件
- 核心程序
- PersonBase.dll(引用 编译时间 通过核心.exe)
- 鲍勃.dll(加载运行时间 通过结构图扫描)
- 贝蒂.dll(加载运行时间 通过结构图扫描)
为了使用 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); } );
其他提示
您也可以使用 StructureMap 进行 xml 配置。如果需要,您甚至可以混合它们。
您还可以将 StructureMap 属性放入 Bob 类中,以告诉 StructureMap 如何加载程序集。DefaultConstructor 是我最终不时使用的一个。
仅当您保留命名、程序集和命名空间约定时,自动扫描选项才有效。您可以使用流畅的界面手动配置结构图。例子:
ObjectFactory.Initialize(initialization =>
initialization.ForRequestedType<PersonBase>()
.TheDefault.Is.OfConcreteType<Bob>());
我们在当前项目中所做的事情(使用 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 应用程序中,但可以(并且在许多情况下更可取)将其放在单独的程序集中。
通过这种方式,我们可以将域逻辑与基础设施代码隔离,并防止应用程序最终依赖于对基础设施代码的直接引用的“仅此一次”解决方法。我们还避免在每个服务实现中必须引用容器。
如果您这样做,一件非常重要的事情:制作 当然 您有测试来验证您是否可以使用 IOC 容器的每个潜在配置创建每个“顶级”类型(在我们的示例中为 ASP.NET MVC 控制器)。
否则,很容易忘记实现一个接口并破坏应用程序的大部分内容。