我想实现涉及不同.NET程序集(即模块)的架构。其中一些模块应该提供其他模块用作.NET接口的服务。 应该在运行时动态加载和注册模块,我不希望它们之间存在“硬编码”依赖关系。
示例:
Module1.dll: Defines a class implementing interface IService1 Module2.dll: Uses the class provided by Module1 through the interface IService1
问题在于放置IService1
的定义:两个模块都需要这个定义。但由于Module2
也应该在没有Module1
的情况下工作(在运行时检查服务的可用性),我不希望Module2.dll
直接引用Module1.dll
。
一种可能性是将每个模块拆分为两个程序集(接口和定义),但这意味着我将我不想要的DLL数量加倍。
我还想到使用一个单独的“接口DLL”,即一个包含所有接口定义的单个程序集,但话又说回来,如果我改变一个单独的接口或者如果我添加新的模块(带有新的接口),我需要更新这个中央DLL,因此所有其他模块(因为它们都依赖于它......)
我想要的是将界面定义链接到Module1
和Module2
,但我不知道是否resp。这是怎么可能的。
我很感激任何想法
修改
也许这个例子有点过于简单:可能存在Module1a.dll
,Module1b.dll
等提供IService
和Module2a.dll
,Module2b.dll
等实现的情况。正在使用它们......
答案 0 :(得分:2)
我认为你应该研究designing by contract。这样,所有组件将在编译时彼此完全隔离。但这会将工作转移到维护手动编写的合同而不是编译时检查的代码接口。
基本上,你在导线的两侧都有相同的接口声明,然后由代理自动连接,这是一个自定义的Castle DynamicProxy实现,加上一些Reflection东西或像{{3的IoC容器它支持服务定位器。
基本上,你在Module1.dll和Module2.dll
上写下“合约”public interface IFooProvider {
void Foo GetFoo();
}
然后你需要一个服务定位器实现,它是一个代码的核心部分,它将帮助模块注册自己,消费者找到注册的模块。这是我提到的DI或IoC容器的正常工作。
它会像这样或那样:
public interface IServiceLocator {
object LocateProvider<ContractType>();
void RegisterProvider<ContractType>(object implementation);
}
基本上,Module1在加载时应该与ServiceLocator一起注册自己提供的合同,然后在加载时,Module2会调用“LocateProvider”方法提供它想要的合同并获得Module1实现。
一些事情:
public class Module1Implementation : IProviderContract {
void Foo GetFoo() { return new Foo(); }
}
public class Main {
public void Main() {
var locator = ServiceLocator.GetLocator();
locator.RegisterProvider<IFooProvider>(new Module1Implementation());
}
}
在Module2.dll中:
public class Consumer {
public IFooProvider FooProvider { get; set; }
public Consumer() {
var locator = ServiceLocator.GetLocator();
FooProvider = locator.LocateProvider<IFooProvider>();
// if Module1.dll is loaded, the locator should supply
// Module1Implementation for you
}
}
然后所有“提供者”模块都应该引用服务定位器DLL,它应该是1个额外的DLL,或者它可能已经内置到核心应用程序本身。在加载时,每个模块都应该使用ServiceLocator以及它们所代表的“合同”进行注册。
然后,拼图的最后一部分,就是你如何使合约看起来像是在单独的DLL中定义出来以显示为相同,这就是为什么我提到了Castle DynamicProxy和Reflection。如何做到这一点已经超出了这个已经很久的答案,所以我将把它留给谷歌。 : - )
这将如何帮助? 所有 DLL之间的所有依赖关系,但删除了服务定位器实现。所以你的部署问题归结为:
呼!这有点长,流言蜚语,但我希望它能回答你的问题。
答案 1 :(得分:1)
这不是关于模块和dll,这是关于OO设计。
IService1是否应该由许多其他模块实现,或者它是否是Module1独有的?如果您需要许多不同的模块来实现IServer1而不依赖于彼此,那么您将需要一个dll来保存此接口及其任何支持文件。
这不会使你拥有的dll数量增加一倍,它只会增加1个。
因此,要么在Module1.dll中定义您的接口,要么在第三个dll中定义接口(合同)。
每当你有许多模块需要处理时,你都会有一个Common模块,其中包含所有内容使用的核心文件(interfaces,api等等)。该模块通常不会引用其他模块。
答案 2 :(得分:0)
Module2.dll
应声明IService
,Module1.dll
应引用Module2.dll
。然后,Module2.dll
应该使用反射(可能使用配置设置)来创建Module1.dll
中声明的类型并将其强制转换为IService
:
// in module2.dll
var serviceInstance = (IService)Activator.CreateInstance(typeToLoad);
答案 3 :(得分:0)
将接口放入Module2.dll有什么问题?
IService1
是Module2.dll定义的接口(或契约),必须实现Module2.dll要使用的每个类。
响应您的编辑:在这种情况下,我可能会将接口放入单独的程序集中。如果(接口的)实现是由第三方完成的,并且您不希望给它们超过必要的(即只有接口的定义),这也很有用。
答案 4 :(得分:0)
一种常见的方法是为“契约”接口设置一个单独的程序集,一个不包含任何实现细节,只包含接口定义。
Module1引用CoreInterfaces.dll(或者你称之为的任何东西) Module2引用了CoreInterfaces.dll