4

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(); 
 }
4

2 に答える 2

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

于 2014-06-03T20:45:42.360 に答える
1

MEF はこの種の機能をサポートしていないため、自分で行う必要があります。メタデータのエクスポートを使用して、装飾されたオブジェクトを構築するためのデータを公開できます。次に、拡張機能を次のようにエクスポートします。

[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;
}
于 2013-07-28T20:27:28.417 に答える