Productivity: Auto generate WCF Service Contracts using T4 template.

The purpose of this T4 template is to auto generate WCF Service Contracts with appropriate ServiceContract, OperationContract (with Name for overloads) and FaultContract attributes from an existing WCF Service implementation class.

The template adds Name property of the OperationContract if the method is overloaded and, for simplicity, a number is appended to the name of the method. You should change this Name property afterwards specially for 3rd party consumption.

The template will check for out/ref parameters and generate the contract accordingly.

USAGE:
1. Drop this file into your VS project (VS 2008+).
2. Rename this file to match this pattern: {Your_Service_Implementation_File}.tt
3. The WCF Service Contract gets generated as {Your_Service_Implementation_File}.cs in the same project when you save the file.
4. In the world of SOA, you must move this Service Contract to a common assembly outside of the implementation project. So, be sure to move the file (and rename the interface as needed) and change the namespace to match the namespace in the common assembly.

<# 
/*
   The purpose of this T4 template is to auto generate WCF Service Contracts
   with appropriate ServiceContract, OperationContract (with Name for overloads)
   and FaultContract<Type> attributes from an existing WCF Service implementation class.

   The template adds Name property of the OperationContract if the method is overloaded and,
   for simplicity, a number is appended to the name of the method. You should change this Name
   property afterwards specially for 3rd party consumption.

   The template will check of out/ref parameters and generate the contract accordingly.

   USAGE:
   1. Drop this file into your VS project (VS 2008+).
   2. Rename this file to match this pattern: {Your_Service_Implementation_File}.tt
   3. The WCF Service Contract gets generated as {Your_Service_Implementation_File}.cs in the same project when you save the file.
   4. In the world of SOA, you must move this Service Contract to a common assembly outside of the implementation project.
      So, be sure to move the file (and rename the interface as needed) and change the namespace to match the namespace in the
	  common assembly.
*/
#>
<#@ template debug="true" hostspecific="true" #>
<#@ output extension="cs" #>
<#@ assembly name="EnvDTE" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="System.Diagnostics" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ assembly name="EnvDTE80" #> 
<#@ import namespace="EnvDTE80" #> 
<#    
    //Get reference to VS Development environment.
	EnvDTE.DTE dte = GetEnvDTE();

	//Extract source file name from this template (.tt) file.
	string sourceFileName = dte.Solution.FindProjectItem(Host.TemplateFile).Name;
	
	//sourceFileName is your Your_Service_Implementation_File
	sourceFileName =sourceFileName.Replace(".tt", ".cs");  
   
    ProjectItem enumProjectItem = dte.Solution.FindProjectItem(sourceFileName);
    FileCodeModel codeModel = enumProjectItem.FileCodeModel;

    CodeNamespace codeNamespace = FindNamespace(codeModel.CodeElements);
    CodeClass codeClass = FindClass(codeModel.CodeElements);
    List<CodeFunction> codeFunctions = GetPublicMethodList(codeClass.Children);
        
    
#>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Configuration;
using System.Reflection;
using System.ServiceModel.Configuration;
using System.Runtime.Serialization;

namespace <#= codeNamespace.Name #>
{
    [ServiceContract(Namespace="http://www.yourdomain.com" +"/<#= codeNamespace.Name #>")]
    public interface I<#= codeClass.Name #>
    {                    
        <#
            PushIndent("        ");
            
            int methodCount = 0;
            
			foreach (CodeFunction method in codeFunctions)
            {
                if(methodCount > 0)
                {
                    WriteLine(String.Empty);
                    WriteLine(String.Empty);
                }
                
				if(codeClass.Name==method.Name)
				{
					continue; //skip the constructor(s)
				}
				WriteSyncOperationContract(method, methodCount);
                WriteLine(string.Empty);
                
                methodCount ++;
            }
            
            ClearIndent();
        #>
    }
}

<#+
    private void WriteSyncOperationContract(CodeFunction method, int methodNumber)
    {
        
		if(method.IsOverloaded==true)
		{
			WriteLine("&#91;OperationContract(Name=" +"\"" +method.Name +methodNumber.ToString()  +"\")&#93;");
		}
		else
		{
			WriteLine("&#91;OperationContract&#93;");
		}
		
		WriteLine("//&#91;FaultContract(typeof(your_custom_serializable_type))&#93;");
		Write(method.Type.AsString);
		Write(" ");
		Write(method.Name);
		Write("("); 

		int count = 1; 
		
		foreach(CodeElement element in method.Parameters)
        {
            CodeParameter parameter = element as CodeParameter;
			
            if (parameter != null)
            {
                CodeParameter2 parameter2=(CodeParameter2) parameter;
				
				if(parameter2.ParameterKind ==vsCMParameterKind.vsCMParameterKindRef)
				{
					Write("ref");
					Write(" "); 
				}
				else if(parameter2.ParameterKind ==vsCMParameterKind.vsCMParameterKindOut)
				{
					Write("out");
					Write(" ");
				}


				
				Write(parameter.Type.AsString + " ");
				Write(parameter.Name);

				if(count < method.Parameters.Count)
				{
					Write(", ");
				}
				
                count++;
            }
        }

		Write(");");   
    }

	private CodeNamespace FindNamespace(CodeElements elements)
    {
        foreach (CodeElement element in elements)
        {
            CodeNamespace ns = element as CodeNamespace;
        
            if (ns != null)
                return ns;
        }
    
        return null;
    }
    
    private CodeClass FindClass(CodeElements elements)
    {
        foreach (CodeElement element in elements)
        {
            CodeClass codeClass = element as CodeClass;
        
            if (codeClass != null)
                return codeClass;
    
            codeClass = FindClass(element.Children);
    
            if (codeClass != null)
                return codeClass;
        }
    
        return null;
    }
    
    private List<CodeFunction> GetPublicMethodList(CodeElements elements)
    {
        List<CodeFunction> methods = new List<CodeFunction>();
        
        foreach (CodeElement element in elements)
        {
            CodeFunction method = element as CodeFunction;
        
            if (method != null &&method.Access==vsCMAccess.vsCMAccessPublic)
			{
                methods.Add(method);
			}            
        }
    
        return methods;
    }
    
    private EnvDTE.DTE GetEnvDTE()
    {
        IServiceProvider hostServiceProvider = (IServiceProvider)Host;
        
        if (hostServiceProvider == null)
               throw new Exception("Host property returned unexpected value (null)");
        
        EnvDTE.DTE dte = (EnvDTE.DTE)hostServiceProvider.GetService(typeof(EnvDTE.DTE));
        
        if (dte == null)
               throw new Exception("Unable to retrieve EnvDTE.DTE");
    
        return dte;
    }
#>

Acknowledgement:
http://weblogs.thinktecture.com/cweyer/2009/06/generating-async-wcf-operationcontract-signatures-with-a-t4-template.html

Leave a Reply