This project has moved and is read-only. For the latest updates, please go here.

Getting composition to occur on object exported by a custom export provider

Jan 10, 2011 at 2:35 PM


I have written a custom export provider, the purpose of which is to host types marked with a custom "ServiceExportAttribute" attribute as WCF services and supply MEF with a proxy to the WCF service.

It has been written in F#, here is the code:

namespace HuwmanCode.ComponentModel.Composition.Hosting

open System
open System.ComponentModel.Composition
open System.ComponentModel.Composition.Hosting
open System.ComponentModel.Composition.Primitives
open System.Reflection
open System.ServiceModel
open System.ServiceModel.Channels
open System.ServiceModel.Dispatcher
open System.ServiceModel.Description

open HuwmanCode
open HuwmanCode.Reflection
open HuwmanCode.ComponentModel.Composition

type ServiceExportProvider (assemblies:Assembly[]) =
    inherit ExportProvider ()

    let getServiceType (definition:ImportDefinition) = 
        if definition.ContractName |> String.IsNullOrWhiteSpace then None
        elif definition.Cardinality <> ImportCardinality.ZeroOrOne && definition.Cardinality <> ImportCardinality.ExactlyOne then None
        else assemblies
             |> (fun asm -> asm.GetTypes ()) |> Array.concat
             |> (fun typ -> typ,typ.GetCustomAttribute<ExportServiceAttribute> (false))
             |> Array.choose (fun (typ,attr) -> attr |> (fun attr -> typ,attr.Contract))
             |> Array.filter (fun (sType,cType) -> cType.FullName = definition.ContractName && sType.IsImplementationOf (cType))
             |> Array.filter (fun (_,cType) -> cType.GetCustomAttribute<ServiceContractAttribute> (false) |> Option.isSome)
             |> Array.nthOrNone 0

    let createServiceChannel (serviceType:Type) (contractType:Type) =
        let address = new Uri (sprintf "net.pipe://localhost/%O" (Guid.NewGuid()))
        let binding = new NetNamedPipeBinding(TransactionFlow=true)
        let host = new ServiceHost(serviceType, address)
        host.AddServiceEndpoint (contractType.FullName, binding, "") |> ignore
        host.Open ()

        let channelFactoryType' = typeof<ChannelFactory<_>>
        let channelFactoryType = channelFactoryType'.GetGenericTypeDefinition().MakeGenericType([|contractType|])
        let channelFactory = Activator.CreateInstance (channelFactoryType, binding, address.ToString()) :?> IDisposable
        let flags = BindingFlags.Instance ||| BindingFlags.InvokeMethod ||| BindingFlags.Public
        let channel = channelFactoryType.InvokeMember("CreateChannel", flags, null, channelFactory, null)

    override this.GetExportsCore (definition, atomicComposition) =
        let contractName = definition.ContractName
        match getServiceType definition with 
        | None -> Seq.empty
        | Some (serviceType,contractType) -> 
            let export = new Export (definition.ContractName, fun () -> createServiceChannel serviceType contractType)
            seq { yield export }

While there are probably many areas for improvement, the particular issue I am experiencing is that due to the fact that a proxy is exported composition is never performed on the actual instance. How can this be achieved since I don't have access to the CompositionContainer instance from within the export provider? Can MEF cater for this scenario?

Jan 10, 2011 at 9:47 PM

Yes it can. Check the other providers implementation. Some of them have SourceProvider property to have access to the outer scope.