【问题标题】:How do I combine the Decorator pattern with C# MEF?如何将装饰器模式与 C# MEF 结合使用?
【发布时间】:2013-07-28 04:48:53
【问题描述】:

我正在尝试增强我当前使用 C# MEF 建立的程序集。由于这些程序集已经在生产中使用,因此直接修改各个类目前不是可行的方法。我主要是在现有的行为中添加新的行为。例如我有:

public IExtension
{
     Object Execute(); 
}


public BaseExtension : IExtension
{
     // other methods and members

     public virtual Object Execute()
     {
         // do operations here. 
     }
}

[Export(typeof(IExtension)]
public AppRecordExtension : BaseExtension
{
     // .. other methods and members
     public override Object Execute()
     {
         base.Execute(); // shown just for example..
         this.someOperation(); 
     }
}

// other extensions made.

现在,当 MEF 容器在驱动程序的方法中调用扩展时,上述方法有效:

[ImportMany(typeof(IExtension)]
private IEnumerable<Lazy<IExtension>> operations;

public void ExecuteExtensions() 
{
     var catalog = new AggregateCatalog( new AssemblyCatalog(Assembly.GetExecutingAssembly()), new DirectoryCatalog("extensions", ".dll")); 
     CompositionContainer container = new CompositionContainer(catalog); 
     container.ComposeParts(this); 

     Dictionary<IExtension, object> result = new Dictionary<IExtension, object>(); 

     foreach(Lazy(IExtension> extension in operations) 
     {
         result.Add((extension.Value, extension.Value.Execute()); 

     }
}

但是,如果我想为 IExtension 或 BaseExtension 实现特定的装饰器,我不知道应该将它们放在容器中的哪个位置,或者我应该如何将属性放在装饰器上,以便所有原始 IExtension 具体类加载并执行附加行为。 IExtension 装饰器示例:

// do I put an attribute here? 
// if an attribute is put here, how does the MEF container call it?
public BatchableExtension : BaseExtension 
{
     private IExtension extension = null; 


     public BatchableExtension( IExtension extension) 
     {
        this.extension = extension; 
     }

     public override Object Execute() 
     {
        this.extension.Execute(); 
        doSomeBatchSpecificOperation(); 
     }
}

// do I put an attribute here? 
// if an attribute is put here, how does the MEF container call it?
public  MonitoringExtension : BaseExtension 
{
     private IExtension extension = null; 


     public MonitoringExtension( IExtension extension) 
     {
        this.extension = extension; 
     }

     public override Object Execute() 
     {
        this.extension.Execute(); 
        doSomeMonitoringSpecificOperation(); 
        doSomeMoreBehaviors(); 
     }

有人可以帮忙吗?我想确保当容器拾取扩展时,新行为也会被拾取,具体取决于传递的参数(例如,如果 isBatchable = true,添加 BatchableExtension 等)。如果它是非 MEF,则上面的内容类似于:

 public void Main(String[] args) 
 {
     IExtension ext = new AppRecordExtension(); 
     // this is the part where I want to simulate when I use MEF. 
     IExtension ext2 = new MonitoringExtension(new BatchableExtension(ext)); 
     ext2.Execute(); 
 }

【问题讨论】:

  • 您显示的是IExtension,但IBaseExtension 来自哪里?你的意思是什么:“现在,当 MEF 容器调用扩展时,上面的工作。”
  • IBaseExtension 是我的错字,现在更正了。我还更新了原始帖子以证明我的意思。基本上,当使用'vanilla' MEF 时,扩展的发现和执行工作。但是,当我添加一些装饰器模式实现(这是我在 Main() 部分中显示的)时,我被卡住了。
  • 结果字典如何被扩展填充?
  • 我主要按照这篇文章的例子:codeproject.com/Articles/432069/…

标签: c# design-patterns mef decorator


【解决方案1】:

MEF 不支持这种功能,所以你必须自己做。您可以使用 Export Metadata 公开用于构造装饰对象的数据 - 然后您将像这样导出您的扩展:

[ExtensionExport(IsBatch = true, IsMonitoring = false)]
public AppRecordExtension : BaseExtension
{
     // ...
}

在导入扩展的类中:

[ImportMany]
private IEnumerable<Lazy<IExtension, IExtensionMetadata>> operations;

public void ExecuteExtensions()
{
    // ...

    foreach(Lazy(IExtension, IExtensionMetadata> extension in operations) 
    {
        IExtension decoratedExtension = DecorateExtension(extension);
        result.Add(decoratedExtension, decoratedExtension.Execute()); 
    }
}

private IExtension DecorateExtension(Lazy<IExtension, IExtensionMetadata> exportedExtension)
{
    IExtension ext = exportedExtension.Value;
    if (exportedExtension.Metadata.IsBatch)
    {
        ext = new BatchableExtension(ext);
    }
    if (exportedExtension.Metadata.IsMonitoring)
    {
        ext = new MonitoringExtension(ext);
    }

    // Other decorating logic...

    return ext;
}

【讨论】:

    【解决方案2】:

    您可以轻松添加基本支持。你只需要一个自定义目录,它以你希望装饰发生的方式重写合同:

    public class DecoratorChainCatalog : ComposablePartCatalog
    {
        private List<Type> myDecoratorChain;
        private List<ComposablePartDefinition> myParts;
    
        private string myContractName;
    
        public DecoratorChainCatalog( Type contract )
            : this( AttributedModelServices.GetContractName( contract ) )
        {
        }
    
        public DecoratorChainCatalog( string contract )
        {
            Contract.RequiresNotNullNotEmpty( contract, "contract" );
    
            myContractName = contract;
    
            myDecoratorChain = new List<Type>();
            myParts = new List<ComposablePartDefinition>();
        }
    
        public void Add( Type type )
        {
            Contract.Invariant( !myParts.Any(), "Recomposition not supported" );
    
            myDecoratorChain.Add( type );
        }
    
        public override IQueryable<ComposablePartDefinition> Parts
        {
            get
            {
                ComposeDecoration();
                return myParts.AsQueryable();
            }
        }
    
        [SecuritySafeCritical]
        private void ComposeDecoration()
        {
            if ( myParts.Any() )
            {
                return;
            }
    
            Trace.WriteLine( "!! ComposeDecoration !!" );
    
            var contracts = new List<string>();
            foreach ( var type in myDecoratorChain )
            {
                var originalPart = AttributedModelServices.CreatePartDefinition( type, null );
    
                var importDefs = originalPart.ImportDefinitions.ToList();
    
                if ( type != myDecoratorChain.First() )
                {
                    RewriteContract( importDefs, contracts.Last() );
                }
    
                var exportDefs = originalPart.ExportDefinitions.ToList();
    
                if ( type != myDecoratorChain.Last() )
                {
                    contracts.Add( Guid.NewGuid().ToString() );
                    RewriteContract( exportDefs, type, contracts.Last() );
                }
    
                // as we pass it to lazy below we have to copy it to local variable - otherwise we create a closure with the loop iterator variable
                // and this will cause the actual part type to be changed
                var partType = type;
                var part = ReflectionModelServices.CreatePartDefinition(
                    new Lazy<Type>( () => partType ),
                    ReflectionModelServices.IsDisposalRequired( originalPart ),
                    new Lazy<IEnumerable<ImportDefinition>>( () => importDefs ),
                    new Lazy<IEnumerable<ExportDefinition>>( () => exportDefs ),
                    new Lazy<IDictionary<string, object>>( () => new Dictionary<string, object>() ),
                    null );
    
                myParts.Add( part );
            }
    
            // no add possible any longer
            myDecoratorChain = null;
        }
    
        [SecuritySafeCritical]
        private void RewriteContract( IList<ImportDefinition> importDefs, string newContract )
        {
            var importToDecorate = importDefs.Single( d => d.ContractName == myContractName );
            importDefs.Remove( importToDecorate );
    
            Contract.Invariant( importToDecorate.Cardinality == ImportCardinality.ExactlyOne, "Decoration of Cardinality " + importToDecorate.Cardinality + " not supported" );
            Contract.Invariant( ReflectionModelServices.IsImportingParameter( importToDecorate ), "Decoration of property injection not supported" );
    
            var param = ReflectionModelServices.GetImportingParameter( importToDecorate );
            var importDef = ReflectionModelServices.CreateImportDefinition(
                param,
                newContract,
                AttributedModelServices.GetTypeIdentity( param.Value.ParameterType ),
                Enumerable.Empty<KeyValuePair<string, Type>>(),
                importToDecorate.Cardinality,
                CreationPolicy.Any,
                null );
    
            importDefs.Add( importDef );
        }
    
        [SecuritySafeCritical]
        private void RewriteContract( IList<ExportDefinition> exportDefs, Type exportingType, string newContract )
        {
            var exportToDecorate = exportDefs.Single( d => d.ContractName == myContractName );
            exportDefs.Remove( exportToDecorate );
    
            var exportDef = ReflectionModelServices.CreateExportDefinition(
                new LazyMemberInfo( exportingType ),
                newContract,
                new Lazy<IDictionary<string, object>>( () => exportToDecorate.Metadata ),
                null );
    
            exportDefs.Add( exportDef );
        }
    }
    

    另请参阅:http://blade.codeplex.com/SourceControl/latest#src/Blade.Core/Composition/DecoratorChainCatalog.cs

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-06
      • 2018-04-14
      • 1970-01-01
      • 2016-03-07
      • 2012-12-17
      • 2012-02-04
      相关资源
      最近更新 更多