上一片介绍了Primitive层,Attribute Model可以认为是对Primitive的上层实现。主要包括如下内容:
1. 一系列的Attribute来定义Import和Export
常用的有如下Attribute:Export,InheritedExport,Import,ImportConstructor,ImportMany,ExportMetadata
Export:如果需要将某个class定义为可供其他组件Import的组件,只需将Export attribute应用于该class即可。这相当于申明了一个ExportDefinition.
例如:
[Export]
class Person
{
public string Name { get; set; } public string Age { get; set; }
}
这里将Person类导出,依赖Person的组件可以通过Import Attribute来定义依赖。
ExportAttribute有两个属性,ContractName和Type,其实都是用来标识Export的,用来在Import的时候指定依赖目标。在没有显示设置情况下,默认是用Type来标识。
InheritedExport是将Export定义在基类上,所有派生类自动应用基类的Export
Import: 其他组件如果需要依赖声明为Export的组件,则使用Import Attribute来指定。
例如:
class Company
{
[Import]
private Person _chairMan
}
这里Company类定义了自身对Person组件的依赖。ImportAttribute也有ContractName和Type属性,用来定义所依赖组件的标识符。
ImportConstructor:通过该Attributes来定义容器创建组件是使用的构造函数,函数的参数自动定义为Import,会通过容器来注入。
ImportMany:导入所有满足条件的组件实例。参考如下代码:
public interface ILog
{ } [Export(typeof(ILog))]
public class FileLog : ILog
{ } [Export(typeof(ILog))]
public class DBLog : ILog
{ } public class TaskExecutor
{
[ImportMany(typeof(ILog))]//声明注入所有提供ILog接口的组件实例
public IEnumerable<ILog> _loggers;
} class Program
{ static void Main(string[] args)
{ var container = new CompositionContainer(new ApplicationCatalog()); var executor = new TaskExecutor();
container.ComposeParts(executor);// FileLog 和 DBLog都会注入_logger数组 Console.ReadKey();
}
}
ExportMetadata:有时候ImportMany的时候我们不希望对满足导出接口的所有组件实例进行进一步筛选.这个时候可以通过ExportMetadata来定义Export的组件具有哪些特性,然后在依赖组件中对导入的组件根据Metadata进行筛选。依然用上面的例子,可以这样来进行筛选。
[Export(typeof(ILog))]
[ExportMetadata("Type", "db")]
public class DBLog : ILog
{ }
//需要定义一个接口来声明Export所支持的metadata
public interface ILogMetadata
{
string Type { get; }
} public class TaskExecutor
{
[ImportMany(typeof(ILog))]
public IEnumerable<Lazy<ILog, ILogMetadata>> _loggers;//这里将变量类型声明为Lazy<ILog,ILogMetadata>,这样在依赖注入的时候不会创建组件的实例 public IEnumerable<ILog> AvailableLogger
{
get
{
var result = new List<ILog>();
foreach (var logger in _loggers)
{
//对metadata进行筛选
if (logger.Metadata.Type == "db")
{
result.Add(logger.Value);
}
} return result;
}
}
}
CreationPolicy:
在将组件通过ExportAttribute导出的时候,可以通过PartCreationPolicyAttributes来指定该组件的实例创建法则.有如下几种选项:
Any //将选择权交给Container来决策,该选项是默认选项,而Container层一般是选择Shared
NonShared //每次填充依赖的时候都创建一个新的组件实例来满足Import
Shared //以单例的形式创建组件实例
同时在定义Import的时候也能指定所需求的组件实例的形式。可以通过设置Import的RequiredCreationPolicy属性来指定。这样就会出现Import的CreationPolicy和Export的CreationPolicy不一致的问题。两者组合最终得到的结果如下:
|| Part.Any || Part.Shared || Part.NonShared ||
| Import.Any | Shared | Shared | Non Shared |
| Import.Shared | Shared | Shared | No Match |
| Import.NonShared | Non Shared | No Match | Non Shared |
2. 一系列发现和生成ComposablePartDefinition的ComposablePartCatalog
PartCatalog是按一定规则来发现Composable Part,并生成对应的ComposablePartDefinition。MEF提供了如下几种Catalog:
System.ComponentModel.Composition.Primitives.ComposablePartCatalog
System.ComponentModel.Composition.Hosting.AggregateCatalog //可以组合几种Catalog
System.ComponentModel.Composition.Hosting.ApplicationCatalog //在应用程序目录中的Dll和Exe中发现组件
System.ComponentModel.Composition.Hosting.AssemblyCatalog //在指定的Assembly中发现组件
System.ComponentModel.Composition.Hosting.CompositionScopeDefinition //这个还没用过
System.ComponentModel.Composition.Hosting.DirectoryCatalog //在指定目录中的dll和exe中发现组件
System.ComponentModel.Composition.Hosting.FilteredCatalog //基于指定的Catalog来应用一个Filter,产生新的Catalog
System.ComponentModel.Composition.Hosting.TypeCatalog //基于一组类型来发现组件
当将Catalog注册到Container中之后,Catalog会自动去发现Composable Part,并车安生对应的Definition.
3. AttributedModelService
除了定义上述的规则,然后让Container去自动发现组件,生成PartDefinition,和创建Part外。AttributedModelService类提供了若干方法可以手动的去创建PartDefinition,创建Part,添加Part,注入依赖。
[Export]
public class Person
{
public string Name { get; set; } public string Age { get; set; }
} [Export]
public class Company
{
[Import]
public Person ChairMan { get; set; }
} class Program
{ static void Main(string[] args)
{
var container = new CompositionContainer(new ApplicationCatalog()); var company = new Company();
var person = new Person(); var personPart = AttributedModelServices.CreatePart(person);
var companyPart = AttributedModelServices.CreatePart(company); var batch = new CompositionBatch();
batch.AddPart(personPart);
batch.AddPart(companyPart); container.Compose(batch); Console.ReadKey();
}
上述代码就是通过手动创建Part,然后注册到Container,然后通过调用Compose方法并注入依赖。
AttributedModelService也提供了扩展方法来直接面向组件实例编程,而不用了解到Primitive层。
通过调用扩展方法ComposeParts(CompositionContainer, Object[]),可以直接传入通过Attribute定义好Import/Export的组件实例对象。组件的依赖会自动注入,并将Export注册到Container.