学习Prism一定要掌握依赖注入的应用,只有了解了Prism的依赖注入才能更好的使用Prism提升应用开发的架构。
首先说明Prism依赖注入有两种方式及MEF和Unity ,在Prism中是两个没有关联的dll。我倾向于使用MEF,下面学习下MEF在Silverlight中的具体实现。先看MEF实现图示
1、Catalog(目录):为了发现可用于组合容器的部件,组合容器将使用“Catalog”。目录是一个对象,通过它发现可用部件,MEF 提供了用于从提供的类型、程序集或磁盘路径创建Catalog
2、Compose(组合):在MEF中,容器将导入与导出匹配的这一过程我们称之为组合,部件由 MEF 组合,MEF 将部件实例化,然后使导出程序与导入程序相匹配。
3、Part(部件):通过 MEF,应用程序可以通过部件的元数据来发现并检查部件,而不用实例化部件,或者甚至不用加载部件的程序集。在部件中可以指定1个或多个Export和Import。
4、Export(导出):在MEF中通过在类或属性中添加Export属性标签表明该对象能够被其他部件引入。
5、Import(导入):是通过向Container申请导入满足条件的对象实例。
在Import时需要遵循Export契约,否则导入将会失败。
按照MEF的约定,任何一个类或者是接口的实现都可以通过[System.ComponentModel.Composition.Export] 属性将其他定义组合部件(Composable Parts),在任何需要导入组合部件的地方都可以通过在特定的组合部件对象属性上使用 [System.ComponentModel.Composition.Import ]实现部件的组合,两者之间通过契约(Contracts)进行 通信。
在MEF中所有组合都需要匹配契约,契约可以是一个字符串或和类型,每一个Export都需要声明一个契约,同样每一个Import都可以定义相同的契约进行匹配。缺省情况下会按照type进行匹配。如果在Export中指定名称则按名称进行匹配。强烈推荐使用契约名称进行匹配,契约名称可加入命名空间,这样匹配更加准确方便。
下面看实例,来自http://mef.codeplex.com/
1、对象注入
[Export] public class SomeComposablePart { ...}
2、属性注入
public class Configuration { [Export("Timeout")] public int Timeout { get { return int.Parse(ConfigurationManager.AppSettings["Timeout"]); } } } [Export] public class UsesTimeout { [Import("Timeout")] public int Timeout { get; set; } }
3、方法注入 【名字区分】
public class MessageSender { [Export(typeof(Action<string>))] public void Send(string message) { Console.WriteLine(message); } } [Export] public class Processor { [Import(typeof(Action<string>))] public Action<string> MessageSender { get; set; } public void Send() { MessageSender("Processed"); } }
4、契约可采用字符串进行标注
public class MessageSender { [Export("MessageSender")] public void Send(string message) { Console.WriteLine(message); } } [Export] public class Processor { [Import("MessageSender")] public Action<string> MessageSender { get; set; } public void Send() { MessageSender("Processed"); } }
5、继承注入,即在基类或借口中定义契约,其子类自动应用其契约
[InheritedExport] public interface ILogger { void Log(string message); } public class Logger : ILogger { public void Log(string message); }
6、构造注入
class Program { [ImportingConstructor] public Program(IMessageSender messageSender) { ... } }
7、选项注入
[Export] public class OrderController { private ILogger _logger; [ImportingConstructor] public OrderController([Import(AllowDefault=true)] ILogger logger) { if(logger == null) logger = new DefaultLogger(); _logger = logger; } }
8、集合注入
public class Notifier { [ImportMany(AllowRecomposition=true)] public IEnumerable<IMessageSender> Senders {get; set;} public void Notify(string message) { foreach(IMessageSender sender in Senders) { sender.Send(message); } } }
9、为了保障一个对象的某些属性能够被及时实例化,可使用IPartImportsSatisfiedNotification
public class Program : IPartImportsSatisfiedNotification { [ImportMany] public IEnumerable<IMessageSender> Senders {get; set;} public void OnImportsSatisfied() { // when this is called, all imports that could be satisfied have been satisfied. } }
10、MEF注入共享模式分为NoShared,Shared,Any三种,简单讲就是NoShared意味着每个调用者就将创建一个新的实例,而Shared将会为每一个调用者提供一个唯一的静态实例(也就是说单态)
[Export("UserManageViewModel")] [PartCreationPolicy(CreationPolicy.Shared)] public class UserManageViewModel:MyViewModelBase { .... }
通过以上介绍基本说明了MEF的注入模式,在实际应用中最重要的也常被忽视的可能就是契约的定义上,如果在类中标示了[Export(typeof(...))]而在引用时使用了[Import["..."]可能会导致无法匹配,另外说明的是所有的部件一定是要被加载到Container中的,否则将会出现无法匹配的异常。