T4 Supplement to Add Service Reference

I’m a self-admitted control-freak.  And when I add a service reference to a project pointing to a WCF service that exposes metadata, I would like more control over the code-generation process.  Not long ago I had a specific need to to this when creating a client-side T4 template for my Trackable DTO’s project with Entity Framework 4.0.  Instead of relying solely on “Add Service Reference” in Visual Studio to generate client-side POCO classes, I wanted to generate the classes myself, hooking into a change-tracking mechanism and injecting data binding code.

The way that Self-Tracking Entities in EF4 accomplishes this is to add a class library project with a T4 template the inspects an entity data model (edmx file).  The problem is that this class library assembly must then be referenced both by the service and the client, violating a tenet of service-oriented applications to share contract and schema but not class or assembly.  That way, the client could be completely ignorant of the persistence stack used (in this case Entity Framework), and there could be a cleaner separation of concerns.  The WCF service could use a data access layer (DAL) to abstract away persistence concerns, and the client could make use of a change-tracker that the service would have no interest in referencing.

And so I needed to generate client-side entities with data contracts using a T4 template that could read service metadata exposed as WSDL.  The way to do this would be to create a WsdlImporter referencing data contracts from a service.  The result would be a CodeCompileUnit, which I could reflect over using the CodeDom to generate class and member definitions.

private CodeCompileUnit GetMetadataCodeUnit(string mexAddress,
    long maxReceivedMessageSize, Type collectionType)
{
    Binding mexBinding = GetMetadataBinding(mexAddress,
        maxReceivedMessageSize);
    var mexClient = new MetadataExchangeClient(mexBinding);
    mexClient.ResolveMetadataReferences = true;

    var metadata = mexClient.GetMetadata
        (new EndpointAddress(mexAddress));
    var sections = metadata.MetadataSections
        .Where(s => s.Identifier.Contains("datacontract.org"));
    var metaDocs = new MetadataSet(sections);
    var wsdlImporter = new WsdlImporter(metaDocs);

    var schemaImporter = new XsdDataContractImporter();
    schemaImporter.Options = new ImportOptions();
    schemaImporter.Options.ReferencedCollectionTypes
        .Add(collectionType);
    schemaImporter.Import(wsdlImporter.XmlSchemas);
    return schemaImporter.CodeCompileUnit;
}

The GetMetadataBinding method creates a custom binding with a transport element that has been configured with the specified max received message size.

private Binding GetMetadataBinding(string mexAddress,
    long maxReceivedMessageSize)
{
    Uri uri = new Uri(mexAddress);
    BindingElement element = null;
    switch (uri.Scheme)
    {
        case "net.pipe":
            element = new NamedPipeTransportBindingElement();
            ((NamedPipeTransportBindingElement)element)
              .MaxReceivedMessageSize = maxReceivedMessageSize;
            break;
        case "net.tcp":
            element = new TcpTransportBindingElement();
            ((TcpTransportBindingElement)element)
              .MaxReceivedMessageSize = maxReceivedMessageSize;
            break;
        case "http":
            element = new HttpTransportBindingElement();
            ((HttpTransportBindingElement)element)
              .MaxReceivedMessageSize = maxReceivedMessageSize;
            break;
        case "https":
            element = new HttpsTransportBindingElement();
            ((HttpsTransportBindingElement)element)
              .MaxReceivedMessageSize = maxReceivedMessageSize;
            break;
        default:
            break;
    }
    if (element != null)
    {
        Binding binding = new CustomBinding(element);
        return binding;
    }
    else
    {
        return null;
    }
}

Here is a link to a simple Console app that tests the WsdlImporter code.  And here is a link to a project that implements it as a T4 template.

Enjoy.

About Tony Sneed

Married with three children.
This entry was posted in Technical. Bookmark the permalink.

One Response to T4 Supplement to Add Service Reference

  1. Please let me know if you’re looking for a article author for your site. You have some really great articles and I think I would be a good asset. If you ever want to take some of the load off, I’d absolutely love to write some articles for your blog in exchange for a link back to mine.
    Please blast me an email if interested. Thanks!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s