初学Prism、MEF应用陷阱例一
转自 http://www.189works.com/article-42722-1.html
最近熟悉了一下Prism框架,将自己理解的主要知识点分为如下:
1、BootStrapper-启动引导
2、Region-区域
3、Module-模块
4、Aggregate-聚合事件
5、MVVM
根据Prism的QuikStart 以及网上诸多的示例,应用,我们很快便能理解其中的一些知识,并且非常跃跃欲试!
经过一翻学习、运行示例之后…………… 开始了自己的第一个Prism应用;
按照学习到的知识:
首先、我们新建项目、创建一个Shell 并且标记[Export]
[Export] public partial class Shell : Window
接着、添加BootStrapper类,重写CreateShell()以及InitializeShell方法
public class BootStrapper : MefBootstrapper
{
protected override DependencyObject CreateShell()
{
//注意:Prism的Mef.Container.Catalog的配置来自AggregateCatalog
return this.Container.GetExportedValue<Shell>();
}
protected override void InitializeShell()
{
base.InitializeShell();
Application.Current.MainWindow = (Shell)this.Shell;
Application.Current.MainWindow.Show();
}
}
最后、修改App文件通过BootStrapper.Run()来启动应用
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
new BootStrapper()
.Run();
}
}
121、三步走就完成了,原来这么简单哦!!
满怀欣喜的您如果真的这样做了,并且运行、你就会跟我一样杯具的发现在
return this.Container.GetExportedValue<Shell>();
这里报错哦~~~ oh my god! 所报错误为MEF无法匹配Export元数据!
为什吗,为什吗呢? 猴急的你也许会跟我一样,重新打开官方示例仔细查看,甚至是copy code过来,也许你还会像我一样往官方示例添加NewShell 试试,或者新建项目添加Shell 并使用官方项目来getExport它!!!!
但是猴急一圈下来,有可能您发现还是错的,如果您运气好,完全copy了官方示例来测试没有出错,那也只能说你不会理解到本文下面将要降到的陷阱!
到这里之后,说实话,我怀疑过我的系统环境,怀疑我新建的项目问题,怀疑过.g.cs的隐藏文件,我甚至拿文本打开项目文件去对比到底有啥差异!
唯一没做的就是去查看prism源码,在google了一翻、苦思冥想了一翻,群里扯了一翻之后,问题还是没有解决的情况下,我开始打开prism的源码一探究竟!
(如果您也经过了这些磨难,甚至认为Prism太操蛋的时候,那赶紧继续往下看吧)
我们需要查看的就是MefBootstrapper类,作为一个菜鸟 ,我们一定要准确快速的定位问题:直接飞至MefBootstrapper类Run方法
this.Logger.Log(Resources.CreatingMefContainer, Category.Debug, Priority.Low);
this.Container = this.CreateContainer(); //方法内部Catalog来自AggregateCatalog
if (this.Container == null)
{
throw new InvalidOperationException(Resources.NullCompositionContainerException);
}
this.Logger.Log(Resources.ConfiguringMefContainer, Category.Debug, Priority.Low);
this.ConfigureContainer(); //内部调用RegisterBootstrapperProvidedTypes注册下列Export
/*
this.Container.ComposeExportedValue<ILoggerFacade>(this.Logger);
this.Container.ComposeExportedValue<IModuleCatalog>(this.ModuleCatalog);
this.Container.ComposeExportedValue<IServiceLocator>(new MefServiceLocatorAdapter(this.Container));
this.Container.ComposeExportedValue<AggregateCatalog>(this.AggregateCatalog);
*/
看看我们重写的方法实实在在的在Run方法里做了一些初始化了,
抓住
this.Container = this.CreateContainer(); //方法内部Catalog来自AggregateCatalog
protected virtual CompositionContainer CreateContainer()
{
CompositionContainer container = new CompositionContainer(this.AggregateCatalog);
return container;
}
看一下,结果见注释!(//方法内部Catalog来自AggregateCatalog)
这一看,如果您使用过MEF或者对MEF有所了解的话,就能很快想到问题原因了,从一开始我们的报错信息来看,是Export没有匹配到,那就是说MEF.Container中没有包含我们Export出去的东西
为什么没有包含,一:MEF的Catalog有问题;二:Export标记没写(#这废话),看到这里我们就知道了真是Catalog的问题,因为Prism默认的Container首先会从AggregateCatalog添加
那就需要继续往下看看AggregateCatalog到底干了些神马:
老老实实Create
protected virtual AggregateCatalog CreateAggregateCatalog()
{
return new AggregateCatalog();
}
老老实实Config
protected virtual void ConfigureAggregateCatalog() { }
这就奇怪了,既然与AggregateCatalog直接相关的部分没问题,那就要继续往下看:
protected virtual void ConfigureContainer() { this.RegisterBootstrapperProvidedTypes(); }
Hi,有东东哦,go go go继续跟进;
protected virtual void RegisterBootstrapperProvidedTypes()
{
this.Container.ComposeExportedValue<ILoggerFacade>(this.Logger);
this.Container.ComposeExportedValue<IModuleCatalog>(this.ModuleCatalog);
this.Container.ComposeExportedValue<IServiceLocator>(new MefServiceLocatorAdapter(this.Container));
this.Container.ComposeExportedValue<AggregateCatalog>(this.AggregateCatalog);
}
Okay,至此我们的跟踪已经完成。同时我们也学习到了Prism博大精神的依赖注入功能;
闲话一句,我们发现Prism框架有个依赖组件ServiceLocator ,这是一个非常好的设计哦,建议还不了解同学都去codeplex上看下其源码,和几个扩展实现!
再Okay下,通过上面的简单跟踪知道了Container的Catalog内容之后,我们的问题也清楚了,那就是因为我们Export的程序集未被加载到Container的Catalog中;
在MEF中,Export出的东西叫做部件,存放部件的地方叫做Catalog ,这个Catalog可以是目录,程序集,也可以是代码; 当然MEF的知识还需要单独去了解哦!
同时这里也可以联想到为什么官方示例能够正常运行, 因为大部分示例官方非官方的 他们对Prism框架的演示都包含了Module\Region\Aggregate的应用,
自然他们也就在BootStrapper对Module和Aggregate进行了配置,配置的同时将包含Export的所有程序集都添加到Container中了,
所以在Container获取部件的时候就没有出错,这样想来的话,如果大家一步一步一个功能一个功能实现去学习的话就会遇到陷阱了!
好了, 问题分析清楚了,解决办法就是将自身程序集Catalog注册到Container中去就可以了,
示例:
protected override void ConfigureAggregateCatalog()
{
base.ConfigureAggregateCatalog();
//加载自身
this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(this.GetType().Assembly));
//加载ViewModel所在Assembly
this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(ShellViewModel).Assembly));
//加载Modules目录
if (Directory.Exists("./Modules"))
{
this.AggregateCatalog.Catalogs.Add(new DirectoryCatalog("./Modules"));
}
}
先分享下自己的应用选型, 选择Prism框架的Module\Region\Aggregate功能,当然包含了MEF; 选择SimapleMVVM的MVVM功能;
然后是后话,Prism非常不错的框架,我们现在不管高手、菜鸟 开发的时候都在应用好的设计模式,好的开发方法 和 好的架构;
但是技术更新迅速的同时我们大都是草草了解其使用并直接应用,有时候可能没有完全理解其精粹;拿prism来说,作为企业开发框架,主打功能就是解耦、解耦的方法就是依赖注入,再一个就是设计模式;
我们使用prism的完全可以将项目拆分为很多小的工作模块去开发, 目前我的项目中就这样,UI就是纯UI ViewModel单独项目且Export 并且将程序集添加到Prism的Container供UI层去Import
然后ViewModel在通过 IService从Core模块中获取服务,
然后利用ServiceLocator 或者 prism 在任意地方去Import 就可以做很多工作了, 之前做WPF都是将ViewModel在View.xaml中去绑定或者写到资源里,
现在我更喜欢这样:
[Import]
public ShellViewModel ViewModel
{
get
{
return (ShellViewModel)this.DataContext;
}
set
{
this.DataContext = value;
}
}
结尾:对Prism的学习和应用才刚开始,有新的理解和问题我会继续和大家分享 ;另外我现在的项目为C/S分布式应用,服务端EF+WCF+WindowsService, 客户端WPF+WCF ,设计过程和解决问题的过程中也有很多有意思的东西,有时间的话我也会分享出来,供朋友们学习交流!