在MEF中实现延迟加载部件(转)

在MEF的宿主中,当我们通过Import声明导入的对象时,组装(Compose)的时候会创建该对象。例如:

interface ILogger
    {
        void Log(string message);
    }

[Export(typeof(ILogger))]
    class ConsoleLogger : ILogger
    {
        public void Log(string message)
        {
            Console.WriteLine("logger 1" + message);
        }
    }

class Host
    {
        [Import]
        ILogger _logger = null;

public Host()
        {
            var catalog = new AssemblyCatalog(this.GetType().Assembly);
            var container = new CompositionContainer(catalog);

//这儿会创建ConsoleLogger对象
            container.ComposeParts(this);

_logger.Log("hello world");
        }
    }

有的时候,有些组件的创建开销比较大,但又不会立即使用。此时,我们希望通过延迟初始化的方式将其延迟到使用的时候创建,从而提高性能(常见的是提高启动速度)。MEF是支持这一模式的,我们只需要修改一下导入的声明形式即可。

[Import]
    Lazy<ILogger> _logger = null;

这样,Logger就会延迟到第一次使用的时候创建了。

元数据MetaData

有的时候,对于同一个服务有多个提供者,我们需要从中选择一个使用。MEF提供了ImportMany来解决这一需求。

[Export(typeof(ILogger))]
    class ConsoleLogger : ILogger
    {
        public void Log(string message)
        {
            Console.WriteLine(message);
        }
    }

[Export(typeof(ILogger))]
    class DbLogger : ILogger
    {
        public void Log(string message)
        {
            Console.WriteLine(message);
        }
    }

class Host
    {
        [ImportMany]
        ILogger[] _logger = null;

public Host()
        {
            var catalog = new AssemblyCatalog(this.GetType().Assembly);
            var container = new CompositionContainer(catalog);

container.ComposeParts(this);

_logger.FirstOrDefault(i => i is DbLogger).Log("hello world");
        }
    }

此时,如果我们想使用延迟导入的时候,就会变成如下形式:

class Host
    {
        [ImportMany]
        Lazy<ILogger>[] _loggerServices = null;

public Host()
        {
            var catalog = new AssemblyCatalog(this.GetType().Assembly);
            var container = new CompositionContainer(catalog);

//这儿会创建ConsoleLogger对象
            container.ComposeParts(this);

_loggerServices.FirstOrDefault(i => i.Value is DbLogger).Value.Log("hello world");
        }
    }

咋一看并没有什么问题,所有的Logger都是延迟创建的。但是仔细分析一下就会发现,要找到DbLogger的时候,必须遍历所有的_loggerServices,这个遍历会导致创建Logger。也就是说,使用第一个Logger的时候可能创建所有的Logger。

那么,如何实现我们只创建所需要的Logger呢? 这个时候就轮到元数据出场了,MEF中的元数据可以将一个数据附加到Export的服务对象中一并导出,从而可以通过元素据找到对应的服务。首先我们看看最终的效果吧:

public interface ILoggerData
    {
        string Name { get; }
    }

class Host
    {
        [ImportMany]
        Lazy<ILogger, ILoggerData>[] _logger = null;

public Host()
        {
            var catalog = new AssemblyCatalog(this.GetType().Assembly);
            var container = new CompositionContainer(catalog);

container.ComposeParts(this);

_logger.FirstOrDefault(i => i.Metadata.Name == "DB Logger").Value.Log("hello world");
        }
    }

这里首先声明了一个元数据类型的接口ILoggerData,然后,导入的对象变成了Lazy<ILogger, ILoggerData>,这个对象有一个属性为Metadata,它的类型就是刚才声明的ILoggerData。导出的ILogger对象是延迟创建的,而元数据不是延迟创建的。我们可以通过遍历ILoggerData来找到所需要的Logger对象,从而实现只创建所使用的Logger对象。

现在的问题是:如何在导出的时候声明相关的元数据。MEF提供了两种方式:

通过ExportMetadataAttribute标记声明

这种方式是在导出的服务的时候一并通过ExportMetaDataAttribute属性标记元素据:

[ExportMetadata("Name", "Console Logger")]
    [Export(typeof(ILogger))]
    class ConsoleLogger : ILogger
    {
        public void Log(string message)
        {
            Console.WriteLine(message);
        }
    }

[ExportMetadata("Name", "DB Logger")]
    [Export(typeof(ILogger))]
    class DbLogger : ILogger
    {
        public void Log(string message)
        {
            Console.WriteLine(message);
        }
    }

ExportMetaDataAttribute有两个参数,Name和Value,Name为属性名称,Value为属性值。Compse的时候,MEF会先创建一个实现了元数据对象,然后将根据将Value值赋值给Name所对应的属性名称。

这么做虽然比较简单,但是它有两个弊端:

1. 属性名称不是强类型

这里我们必须字符串来给标志属性名称,它们之间并没有语法级的一致性检查,在缺少nameof运算符的现在,一旦元数据属性名称更改的话是非常容易出错的。

2. 如果元数据有多个值的话赋值显得非常累赘。

假如我们增加了一个Priority类型的属性,

public interface ILoggerData
    {
        string Name { get; }
        int Priority { get; }
    }

此时,必须对所有的导出对象都增加一个ExportMetadata标记,写出如下形式:

[ExportMetadata("Name", "DB Logger")]
    [ExportMetadata("Priority", 3)]

当属性更多一点的话相信程序员们就会骂娘了。并且一旦某个Export对象漏写了,改对象就不会被导入。这个是一个运行时的错误,非常不容易排查的。

通过Attribute标记

这种方式可以通过一个Attribute来标记元数据:

[MetadataAttribute]
    [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
    class LoggerDataAttribute : Attribute, ILoggerData
    {
        public string Name { get; private set; }

public LoggerDataAttribute(string name)
        {
            this.Name = name;
        }
    }

[LoggerData("Console Logger")]
    [Export(typeof(ILogger))]
    class ConsoleLogger : ILogger, ILoggerData
    {
        public string Name { get; set; }

public void Log(string message)
        {
            Console.WriteLine(message);
        }
    }

[LoggerData("DB Logger")]
    [Export(typeof(ILogger))]
    class DbLogger : ILogger, ILoggerData
    {
        public string Name { get; set; }
        public void Log(string message)
        {
            Console.WriteLine(message);
        }
    }

首先,声明一个LoggerDataAttribute,这个Attribute必须被MetadataAttribute标记。然后,在Export的对象前加上该LoggerDataAttribute,这样MEF导入的时候就会根据该LoggerDataAttribute创建元数据了。

值得一提的是,这里的LoggerDataAttribute本身并不需要实现ILoggerData接口,它是一个DuckType的约定,只需要实现元数据的属性即可。我这里实现该接口主要是为了让编译器保障元数据属性都有被准确实现。

时间: 2024-11-16 07:47:19

在MEF中实现延迟加载部件(转)的相关文章

在MEF中实现延迟加载部件

在MEF的宿主中,当我们通过Import声明导入的对象时,组装(Compose)的时候会创建该对象.例如: interface ILogger    {        void Log(string message);    } [Export(typeof(ILogger))]    class ConsoleLogger : ILogger    {        public void Log(string message)        {            Console.Writ

django 中的延迟加载技术,python中的lazy技术

---恢复内容开始--- 说起lazy_object,首先想到的是django orm中的query_set.fn.Stream这两个类. query_set只在需要数据库中的数据的时候才 产生db hits.Stream对象只有在用到index时才会去一次次next. 例子: f = Stream() fib = f << [0, 1] << iters.map(add, f, iters.drop(1, f)) 1行生成了斐波那契数列. 说明: f是个lazy的对象,f首先放入

Hibernate中的延迟加载及fetch

Hibernate中的延迟加载 1.类级别的查询策略: lazy  :  true(默认值) false(立即加载) 2.多对一关联的查询策略: lazy:  proxy(默认值) no-proxy false 3.一对多  或多对多 lazy:true(默认值) false extra fetch:影响Hibernate针对底层SQL的生成 一.emp的映射文件的类级别设置非延迟加载 第二条会报空指针异常 二.emp的映射文件的类级别设置非延迟加载,其多对一属性也设置为非延迟加载 测试代码 三

EF中的延迟加载和贪婪加载

延迟加载: 优点: 仅在需要的时候加载数据,不需要预先计划,从而避免了各种复杂的外连接.索引.视图操作带来的低效率问题 使用方式: 第一: 在需要的延迟加载的属性前加上virtual,该属性的类型可以是任务的集合类型 ICOLOOCT<T>或者是0.1..1关联属性 如: public virtual List<Product> Products {get;set;} 第二: 在context构造器中开启延迟加载功能 ContextOptions.LazyLodingEnabled

android ViewPager中页面延迟加载的实现

对于viewpager的使用,如果说是请求的数据对内存的消耗不到的话,这是很理想的,但是如果说请求的数据对内存消耗的比较大的话这样就容易出现oom(比如说图片),在使用viewPager的时候使用setOffscreenPageLimit(int size)默认的是1,所以说我们怎么设置这个值,在加载数据的话会将当前页面和下个页面的数据同时的进行请求,这在某种情况下时非常的影响性能的,还有就是在当前页面和下个页面在viewPager中算作了同一个页面,因为你是无法通过生命周期来进行控制数据的延迟

Prism 5 + MEF中的ModuleCatalog.CreateFromXaml问题

protected override IModuleCatalog CreateModuleCatalog() { return Microsoft.Practices.Prism.Modularity.ModuleCatalog.CreateFromXaml(new Uri("ModuleCatalog.xaml", UriKind.Relative)); } .net的背景,就不说了.用这个代码作为开头吧.在Bootstrapper载入部件时需要用到. ModuleCatalog有

Swift中的延迟加载(懒加载)

Swift方式的延迟加载 而在Swift中,你只需一行代码即可实现此机制: lazy var players = String[]() 简单.简洁,直入主题. 但你得记住,你必须使用var关键字来定义延迟加载的属性,不能使用let关键字,因为常量必须在实例构建时赋值. 如果你想给延迟加载加上一些逻辑处理,Swift允许你在属性后面定义一个闭包调用(闭包的返回值会作为属性的默认值): lazy var players: String[] = { var temporaryPlayers = Str

Hibernate中的延迟加载(懒加载)

什么是懒加载? Hibernate提供了一种机制,即在设置了使用懒加载的情况下,查询某一条数据时不会立即访问数据库,因此不会返回指定对象,而是返回代理对象,该代理对象并不为null,它实际上是Hibernate自动实现的指定对象所属类的子类的对象,该对象具有默认值.当要使用真正对象的属性的时候才会访问数据库,这时代理对象会自动查询数据库中对应对象的数据并返回. 这样一来降低了程序对数据库访问次数和内存使用量.下面我们通过懒加载适用情况分析它究竟是如何做到的. 懒加载的适用情况? 1.获取某一个对

【Hibernate】Hibernate中使用延迟加载应该注意的事项

1,简介 在使用一些查询方法时,方法执行了,但是并没有立刻发送SQL语句查询数据库.而是在访问对象的getXxx方法时候才触发SQL执行加载对象数据.这种机制就称为延迟加载. 2,优点 延迟加载主要是为后续关联映射提供,避免查找无用的关联数据.可以降低数据库操作的并发率,提升内存资源使用率. 3,使用 在struts2中,session.load()和query.iterator()都使用的这种机制. 下面笔者使用session.load()方法举例: hibernate.cfg.xml 文件