如何将Decorator模式与C#MEF相结合?

时间:2013-07-28 04:48:54

标签: c# design-patterns mef decorator

我正在尝试增强目前使用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(); 
 }

2 个答案:

答案 0 :(得分: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;
}

答案 1 :(得分:1)

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

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