C# 用于小型插件系统的简单IoC容器
我正在为.NET3.5应用程序(WinForms)设计一个简单的插件框架 我们当前的应用程序需要开始支持动态加载和“挂接”不同的“插件”/“扩展”,这些在编译时应用程序是未知的 这些扩展将被“钩住”到应用程序的不同区域,例如aded作为特定类的事件处理程序 例如(简化):C# 用于小型插件系统的简单IoC容器,c#,.net,dependency-injection,inversion-of-control,ioc-container,C#,.net,Dependency Injection,Inversion Of Control,Ioc Container,我正在为.NET3.5应用程序(WinForms)设计一个简单的插件框架 我们当前的应用程序需要开始支持动态加载和“挂接”不同的“插件”/“扩展”,这些在编译时应用程序是未知的 这些扩展将被“钩住”到应用程序的不同区域,例如aded作为特定类的事件处理程序 例如(简化): 公共类系统 { 公共活动行动完成; 公共事件行动失败; 公众活动停止; } 我想要的一个用例是,开发人员能够在插件程序集中定义此类事件的处理程序,而不让应用程序知道它们 据我所知,IoC容器允许在运行时动态发现对象并在容器中
公共类系统
{
公共活动行动完成;
公共事件行动失败;
公众活动停止;
}
我想要的一个用例是,开发人员能够在插件程序集中定义此类事件的处理程序,而不让应用程序知道它们
据我所知,IoC容器允许在运行时动态发现对象并在容器中注册它们
IoC容器是否也可以为我连接到各种事件?或者,没有这样的框架,这项任务更容易完成吗
如何着手设计如何为这样的任务集成一个IoC容器?(假设有多个扩展点,例如可用于在上注册的不同事件)
我发现自己在问一些问题:
你可能想看看。它允许你询问的所有事情。它使用的术语(组件、导出等)最初令人困惑,但使用起来非常简单 插件本身提供了一个注册方法来执行该操作,这是常见的吗 注册 MEF使应用程序完成查找和注册插件的工作。该插件只需要实现一个接口,说明“我是一个插件,可以做X” 国际奥委会应该登记吗?(通常是怎么做的?) 使用MEF插件的应用程序能够指定如何加载插件。这可以通过搜索DLL目录、读取配置文件中的程序集名称列表、检查GAC来实现——任何操作都可以。它是完全可扩展的(因为您可以编写自己的搜索类) 在使用IoC容器时,如何轻松定义扩展点 ?
MEF使用接口定义应用程序和插件之间的契约。此答案将特定于我的容器 我们当前的应用程序需要开始支持动态加载和“挂接”不同的“插件”/“扩展”,这些在编译时应用程序是未知的 要做到这一点,您必须定义一些扩展接口,这些接口放在类库中,在应用程序和所有插件之间共享 例如,如果希望应用程序能够向应用程序菜单添加内容,可以创建以下界面:
class ApplicationMenu
{
// The "File" menu
IMenuItem File { get; }
}
interface IMenuRegistrar
{
void Register(ApplicationMenu menu);
}
[Component]
public class ReplyEmailNotification : ISubscriberOf<ReplyPosted>
{
ISmtpClient _client;
IUserQueries _userQueries;
public ReplyEmailNotification(ISmtpClient client, IUserQueries userQueries)
{
_client = client;
_userQueries = userQueries;
}
public void Invoke(ReplyPosted e)
{
var user = _userQueries.Get(e.PosterId);
_client.Send(new MailMessage(user.Email, "bla bla"));
}
}
这意味着您的插件可以创建以下类:
[Component]
public class CoolPluginMenuRegistrar : IMenuRegistrar
{
public void Register(ApplicationMenu menu)
{
menu.File.Add("mnuMyPluginMenuName", "Load jokes");
}
}
我的容器使用[Component]
属性,以便它可以为您发现并自动注册类
要注册所有扩展点(如上所述),您只需执行以下操作:
public class Program
{
public static void Main(string[] args)
{
var registrar = new ContainerRegistrar();
registrar.RegisterComponents(Lifetime.Transient, Environment.CurrentDirectory, "MyApp.Plugin.*.dll");
var container = registrar.Build();
// all extension points have been loaded. To load all menu extensions simply do something like:
var menu = GetMainMenu();
foreach (var registrar in container.ResolveAll<IMenuRegistrar>())
{
registrar.Register(menu);
}
}
}
事件可以由任何插件处理,只要它们:
[组件]
或手动注册)ISubscriberOf
您可以在这里更详细地了解它:对于MEF,每个“部分”都是某个接口的实现。这意味着对于每个扩展点,都应该创建一个新接口?例如,我有3个事件:OnA、OnB、OnC。如何使用MEF将插件连接到这些事件上?(没有定义3个不同的接口)我真的不理解这个问题-如果OnA、OnB和OnC在逻辑上是以这样一种方式关联的,它们可以合理地成为单个接口的一部分,那么就让它们成为单个接口。如果它们没有关联,则将它们作为单独接口的一部分。或者我误解了你的问题?我的意思是,如果我有许多不同的事件要动态注册,它们中的每一个都必须显式定义它们正在导入某个接口。这意味着我必须定义3个不同的接口(每个事件类型一个)。假设我有1个接口和3个事件导入此接口实现。所有3个事件都将导入所有实现,这不是我想要的。
[Component]
public class ReplyEmailNotification : ISubscriberOf<ReplyPosted>
{
ISmtpClient _client;
IUserQueries _userQueries;
public ReplyEmailNotification(ISmtpClient client, IUserQueries userQueries)
{
_client = client;
_userQueries = userQueries;
}
public void Invoke(ReplyPosted e)
{
var user = _userQueries.Get(e.PosterId);
_client.Send(new MailMessage(user.Email, "bla bla"));
}
}
DomainEvent.Publish(new ReplyPosted(user.Id, "This is a subject"));