C# 用于小型插件系统的简单IoC容器

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容器允许在运行时动态发现对象并在容器中

我正在为.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
  • 插件本身提供一个注册方法来进行注册,这是常见的吗

    是的。通过在共享程序集中定义为扩展点的不同接口

    国际奥委会应该登记吗?(通常是怎么做的?)

    对。如果容器提供了它

    在使用IoC容器时,如何轻松定义扩展点


    您可以在这里更详细地了解它:

    对于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"));