Caliburn.Micro Bootstrapper及IOC容器配置

Caliburn.Micro Bootstrapper及IOC容器配置如果想深入学习Caliburn.Micro,Bootstrapper和IOC容器配置是重中之重,一定要弄清楚,否则很难理解CM的工作方式。配置Bootstrapper的意义如果在Boostrapper中不进行任何配置的话,Bootstrapper会首先把Bootstrapper所在程序集加载到AssemblySource.Instance中。而我们在Bootstrapper中只在Displa…

大家好,又见面了,我是你们的朋友全栈君。

如果想深入学习Caliburn.Micro,Bootstrapper和IOC容器配置是重中之重,一定要弄清楚,否则很难理解CM的工作方式。

配置Bootstrapper的意义

如果在Boostrapper中不进行任何配置的话,Bootstrapper会首先把Bootstrapper所在程序集加载到 AssemblySource.Instance中。而我们在Bootstrapper中只在DisplayRootViewFor()中给定了一个主ViewModel的类型,那么CM是如何找到找到ViewModel和View并创建实例的?

CM获得ViewModel实例的方式

CM查找ViewModel的方式只有一个,就是从IOC中提取。默认提取方法是IOC.GetInstance,这个方法是直接用ViewModel的类型创建ViewModel的实例,即Activator.CreateInstance()。默认的IOC.GetInstance方法,多次调用就相当于是多次创建新实例,实际上我们只需要第一次是创建新实例,再次调用,只需要返回已经有的实例就ok了。默认的方式是一个很简单的让CM能正常工作的方式,但不是很好,建议用户还是使用自定义的IOC容器。

并且,默认的方式有如下缺点:

  1. Bootstrapper需要依赖ViewModel所在的程序集,否则IOC无法创建ViewModel实例。
  2. 每次从IOC提取实例都是一个新建的实例,无法找到之前创建的实例。

这些问题都可以通过配置MEF等作为IOC容器后解决。

CM获得View实例的方式

在配置IOC容器之前,我们先看看,CM获取实例的方式。清楚的知道CM在内部是如何使用IOC的,才能更好的配置IOC。

CM在创建ViewModel实例后,会先根据ViewModel类型全名获取View的类型名(根据设定的名称映射规则),然后根据View的类型名查找View类型并创建实例。CM会在3个地方查找View,如下:

  1. ViewAware:仅仅只有ViewModel继承了ViewAware,并且View实例已经被创建之后才能用。如果一个ViewModel继承自ViewAware,那么在创建ViewModel对应的View时,会调用ViewAware的AttachView方法把View关联在ViewModel上,以后就可以通过ViewAware的GetView方法获得关联的View。也就是说从ViewAware只能获取已有的View实例,并不能创建View实例。ViewModel可以通过继承Screen的方式间接继承ViewAware(Screen继承了ViewAware),这样会有很多方便,比如在ViewModel中用GetView获得View进行某些操作。
  2. AssemblySource:用FindTypeByNames方法可以从AssemblySource.Instance中根据类型名称获取类型,然后CM用得到的类型创建View实例。AssemblySource.Instance中的类型都是Bootstrapper的SelectAssemblies方法提供的,在Bootstrapper中可以重载SelectAssemblies方法。
  3. IOC:默认情况下没有配置IOC容器,只是在IOC.GetInstance方法中提供了一个简单的创建实例的方法。

所以如果没有配置IOC容器的话,View所在程序集就必须满足以下之一:

  • 用SelectAssemblies方法加载到AssemblySource中。这样CM就可以从AssemblySource中获取View类型
  • View和Bootstrapper在同一个程序集。这样CM就可以用默认IOC.GetInstance静态方法创建一个View实例。

Bootstrapper配置内容

Bootstrapper中有2个必需用的方法即:

  • Initialize:初始化Bootstrapper所有设置,包括EventAggregator事件、AssemblySource、IOC、Application事件。
  • DisplayRootViewFor: 显示主界面。

Bootstrapper中可以通过重载来配置CM的方法主要有:

  • SelectAssemblies() :设置加载到AssemblySource中的程序集列表

  • PrepareApplication():从名字就可以看出是application的事件处理,事实上里边代码是这样的

      Application.Startup += OnStartup;
      Application.DispatcherUnhandledException += OnUnhandledException;
      Application.Exit += OnExit;
    

    所以,OnStartup(object sender, StartupEventArgs e)、OnExit(object sender, EventArgs e)、OnUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) 这3个可以重载的方法意义就很清楚了

  • Configure(): 用于配置IOC容器

  • GetInstance(Type service, string key):IOC容器获取实例的方法

  • GetAllInstances(Type service):IOC容器获取实例的方法

  • BuildUp(object instance) :IOC容器注入实例的方法

Bootstrapper配置实例

MEF是一个.net的插件框架,也可以作为一个依赖注入容器(IOC)使用。我通常就用MEF作为CM的IOC容器。在MEF中所有export部件都会被作为插件导入到container中,通过container也可以访问每个export对象。我们在把MEF作为IOC容器的时候,通常只需要把类标记为export导入到container就可以了,当然不标记为export的类是无法导入到container的。也就是说我们把MEF作为IOC容器的时候,主要使用export部件相关的功能。不了解MEF的话,请了解一下MEF再看以下内容会比较容易理解。

AssemblySource配置

AssemblySource在CM中只有在查找View的时候会用到,(当然ViewModel-First的时候查找ViewModel也会用到)。所以如果你把View和ViewModel都注入到IOC容器中,应该是可以不需要AssemblySource的。事实上我们在用MEF作为IOC容器时一般只把ViewModel导入容器,View不作处理的。所以,一定要记得把View所在的程序集加入到AssemblySource。

代码示例如下:

protected override IEnumerable<Assembly> SelectAssemblies()
{ 
   
    return new[] { 
   
        Assembly.GetExecutingAssembly()
    };//返回目前正在執行程序集,当View在目前正在执行的程序集中时,可以这样写。
}

我个人习惯吧View单独放在一个文件夹的不同程序集中,所以我通常是这样做的:

        protected override IEnumerable<Assembly> SelectAssemblies()
        { 
   
            List<Assembly> lst = new List<Assembly>();
            lst.AddRange(base.SelectAssemblies());
            lst.AddRange(
                Directory.GetFiles(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + @"\views")
                    .Where(file => file.EndsWith("dll", true, CultureInfo.CurrentCulture) || file.EndsWith("exe", true, CultureInfo.CurrentCulture))
                    .Select(Assembly.LoadFrom));
            //lst.AddRange(from file in Directory.GetFiles(Environment.CurrentDirectory + @"\views") where file.EndsWith("dll") || file.EndsWith("exe") select Assembly.LoadFrom(file));
            return lst;
        }

虽然并不需要把ViewModel部分也加载到AssemblySource中,但是建议还是放进去比较好,这样在使用的时候会比较方便,并且IOC容器也可以直接用AssemblySource里的内容填充。

如果动态加载了模块,也建议能够同时在AssemblySource和IOC中注册模块。

IOC配置

在这里我们用MEF作为IOC容器,所以需要先引用两个命名空间:

using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;

如果所有需要注入IOC的类都已经导入到了AssemblySource,就可以这样设置IOC。注:ViewModel-First时,ViewModel是必需要注入到IOC的。

    protected override void Configure()
    { 
   
        container = CompositionContainer(
            new AggregateCatalog(   AssemblySource.Instance.Select(x => new AssemblyCatalog(x))  )
            );

        var batch = new CompositionBatch();
        batch.AddExportedValue<IWindowManager>(new WindowManager());//新建一个窗口管理器添加到IOC中
        batch.AddExportedValue<IEventAggregator>(new EventAggregator());//如果要使用弱事件就需要添加这个了
        batch.AddExportedValue(container);//只是个习惯,这样就可以在任何地方通过IOC使用container了
        container.Compose(batch);
    }

从IOC容器中获取对象的方法代码如下:

    protected override object GetInstance(Type serviceType, string key)
    { 
   
        string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;
        var exports = container.GetExportedValues<object>(contract);

        var exportList = exports.ToList();//避免直接用exports时 调用2次IEnumerable操作
        if (exportList.Any())
            return exportList.First();
        throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
    }

从容器获取所有同类型对象和注入容器的方法可以用如下代码:

    protected override IEnumerable<object> GetAllInstances(Type serviceType)
    { 
   
        return container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
    }

    protected override void BuildUp(object instance)
    { 
   
        container.SatisfyImportsOnce(instance);
    }

BuildUp用到的不多,可以不设置。

Application相关配置

PrepareApplication()中,我们可以添加新的事件处理程序,比如Activated等。OnStartup可以添加程序启动前需要处理的事情,比如命令行参数处理等,当然还有DisplayRootViewFor方法。OnUnhandledException中添加程序中未处理的异常的处理方法。OnExit处理程序退出事件。

另外,在其他平台(非PC)及winform中应用略有不同,请查看官方帮助。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/162197.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • go语言的type func()用法

    go语言的type func()用法在 go 语言中 type 可以定义任何自定义的类型比如熟悉的 typedogstruc typemyIntint 等等所以 func 也是可以作为类型自定义的 typemyFuncfu int int 意思是自定义了一个叫 myFunc 的函数类型 这个函数的签名必须符合输入为 int 输出为 int 已知 相同底层类型的变量之间是可以相互转换的 例如从一个取值范围小的 int16 转为取值范围大的 int32 所以 自定义的 myInt 和 int 之间也是可以转换的 typemyIn

    2025年6月8日
    2
  • Dynamips模拟3660桥接PC后与实际网络通讯问题

    Dynamips模拟3660桥接PC后与实际网络通讯问题

    2021年7月26日
    76
  • Python基础知识点梳理

    Python基础知识点梳理python常见知识点梳理摘要:本文主要介绍一些平时经常会用到的python基础知识点,用于加深印象。python的详细语法介绍可以查看官方编程手册,也有一些在线网站对python语法进行了比较全面的介绍,比如菜鸟教程:python3教程|菜鸟教程本文主要是介绍基础语法,操作实例在另一篇博客中单独介绍:python语言介绍python是一门解释型语言,python的设计目标:一门…

    2022年6月24日
    26
  • 第四章 软件项目进度管理

    第四章 软件项目进度管理本章内容提要第一节软件项目进度管理概述l进l进度是对执行的活动和里程碑所制定的工作计划日期表。l项目进度管理也被称作项目时间管理、工期管理,是指在项目实施过程中,对各阶段的工作进展程度和项目最终完成的期限所进行的管理,是为了确保项目按期完成所需要的管理过程。l项目进度管理是保证项目如期完成及合理安排资源供应,节约工程成本的重要措施之一。度是对执行的活动和里程碑所制定的工作计划日期表。l项目进度管…

    2022年5月20日
    35
  • 服务器中”系统平均负载 Load average“含义学习

    服务器中”系统平均负载 Load average“含义学习文章目录一、什么是系统平均负载二、衡量系统性能三、行车过桥(引用)四、自我总结一、什么是系统平均负载  uptime、w、top等命令都会有系统负载loadaverage的输出,系统平均负载被定义为在特定时间间隔内运行队列中的平均进程数,包括可运行状态和不可中断状态的平均进程数,也就是活跃进程数。它和cpu使用率没有直接的关系二、衡量系统性能  如果系统平均负载的数值除以CPU的数目高于…

    2025年11月7日
    4
  • JAVA中运算符的详讲

    JAVA中运算符的详讲

    2021年9月29日
    33

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号