<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:copyright="http://blogs.law.harvard.edu/tech/rss" xmlns:image="http://purl.org/rss/1.0/modules/image/">
    <channel>
        <title>WCF</title>
        <link>http://geekswithblogs.net/jlavin/category/6744.aspx</link>
        <description>Ramblings about Windows Communication Foundation and how to use it in development.</description>
        <language>en-US</language>
        <copyright>Jim Lavin</copyright>
        <managingEditor>jlavin@jimlavin.net</managingEditor>
        <generator>Subtext Version 0.0.0.0</generator>
        <item>
            <title>Extending the Service Factory v3</title>
            <link>http://geekswithblogs.net/jlavin/archive/2007/09/15/115376.aspx</link>
            <description>&lt;p&gt;I've been needing a tailored version of the Web Service Software Factory to simplify the design and creation of web services for my development team. Not that the Service Factory isn't easy to use, but my team has to design and build services that use industry standard schemas as well as invoke workflows. So, I figured extending the the service factory would allow me to tailor it to my team's needs and by using some of the new features in the v3b117 Alpha I could keep the modifications simple. Granted, things will change between now and the final release, but half of the fun with being on the bleeding edge is taking things apart to get a good understanding of how they work.&lt;/p&gt; &lt;h2&gt;Technology Extensions&lt;/h2&gt; &lt;p&gt;What I needed was a way for the person designing the service to indicate if an operation should invoke a workflow or not. To do this I needed a new property on the operation contract model element in the service designer. Normally, you would have to modify the DSL model to add the new property, recompile and distribute the new version of the DSL. This is not always the best way to do things because you end up having various versions of the DSL laying about taking up space. Not anymore! By using the extensions provided in the service factory you can extend the model specifically for the purposes of the factory without having to modify the DSL. This is really nice! The new extensions are a really big step towards the reuse of DSLs for factory building.&lt;/p&gt; &lt;p&gt;In order to target the models for a specific implementation technology the team has added a Technology Extensions library which allows you to dynamically extend the model at runtime. If you look at the service designer model properties you'll see a property for implementation technology and you have a choice of either ASMX or WCF. &lt;/p&gt; &lt;p&gt;&lt;a href="http://geekswithblogs.net/images/geekswithblogs_net/jlavin/WindowsLiveWriter/ExtendingtheServiceFactoryv3_AF/ServiceModelPropertiesCutout.png" atomicselection="true"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="193" alt="ServiceModelPropertiesCutout" src="http://geekswithblogs.net/images/geekswithblogs_net/jlavin/WindowsLiveWriter/ExtendingtheServiceFactoryv3_AF/ServiceModelPropertiesCutout_thumb.png" width="323" border="0" /&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;This property dynamically loads up a set of model extensions that provides additional properties and actions that are specific for the selected technology. Once the extension is loaded then the properties for the model elements show the additional properties for the specific technology extension.&lt;/p&gt; &lt;p&gt;&lt;a href="http://geekswithblogs.net/images/geekswithblogs_net/jlavin/WindowsLiveWriter/ExtendingtheServiceFactoryv3_AF/ModelElementPropertiesCutout.png" atomicselection="true"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="309" alt="ModelElementPropertiesCutout" src="http://geekswithblogs.net/images/geekswithblogs_net/jlavin/WindowsLiveWriter/ExtendingtheServiceFactoryv3_AF/ModelElementPropertiesCutout_thumb.png" width="322" border="0" /&gt;&lt;/a&gt; &lt;/p&gt; &lt;h2&gt;Tailoring the Factory to Invoke a Workflow&lt;/h2&gt; &lt;p&gt;It just so happens that just like there are two ways to create web services within .Net there are two ways to host workflows within a web service. You can use the native ASMX hosting method provided with Windows Workflow or you can instantiate the workflow runtime in a WCF web service and invoke the workflow using the ManualWorkflowScheduler. This allows us to use the existing Technology Extensions to tailor the hosting method based on the selected implementation technology in the service model.&lt;/p&gt; &lt;h2&gt;Extending the Object Model&lt;/h2&gt; &lt;p&gt;First we need to extend the OperationContract ObjectExtender with a new property called InvokesWorkflow that will be presented to the DSL user once they have selected an implementation technology.&lt;/p&gt; &lt;p&gt;Below is a sample of the code I added to the WCFOperationContract:&lt;/p&gt;&lt;pre&gt;        private bool invokesWorkflow = false;

        [Category(ServiceContractWCFExtensionProvider.ExtensionProviderPropertyCategory),
         Description("Specifies the if the operation should invoke a workflow to handle the request."),
         ReadOnly(false),
         BrowsableAttribute(true)]
        [XmlElement("InvokesWorkflow")]
        public bool InvokesWorkflow
        {
            get { return invokesWorkflow; }
            set { invokesWorkflow = value; }
        }
&lt;/pre&gt;
&lt;p&gt;That's all we have to do to add our new property to the DSL. Now as our designers are working on a service's design they can indicate if the operation should invoke a workflow or not. This is so much simpler than having to modify the DSL and republish it.&lt;/p&gt;
&lt;h2&gt;Modifying the Implementation Specific Text Templates&lt;/h2&gt;
&lt;p&gt;Next we need to modify the code generation templates to add the technology specific implementations to the code. You'll find the templates under the ServiceContractDSL project in a folder called TextTemplates. They have created one for each technology extension, I am assuming the Service Factory Team did this to keep things simple. If I find out differently, I'll let you know in a later post.&lt;/p&gt;
&lt;p&gt;I modified the WCF specific service implementation template to do the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add using statements for the various workflow assemblies. 
&lt;/li&gt;&lt;li&gt;Add a new method called GetWCFImplementation which inserts the hosting code to the generated classes methods. 
&lt;/li&gt;&lt;li&gt;Parse out the parts of the request and add them to a Dictionary that is passed to the workflow when it is invoked. 
&lt;/li&gt;&lt;li&gt;Instantiate the response message class. 
&lt;/li&gt;&lt;li&gt;Parse out the parts of the response and assigned their return values from the Dictionary after the workflow has completed. 
&lt;/li&gt;&lt;li&gt;Return the response message class.&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;Below is the modified template:&lt;/p&gt;&lt;pre&gt;&amp;lt;#@ Template Language="C#" Inherits="Microsoft.Practices.Modeling.CodeGeneration.Strategies.TextTemplating.ModelingTextTransformation" #&amp;gt;
&amp;lt;#@ Import Namespace="System.Text" #&amp;gt;
&amp;lt;#@ Import Namespace="System.Globalization" #&amp;gt;
&amp;lt;#@ Import Namespace="System.Collections.Generic" #&amp;gt;
&amp;lt;#@ Import Namespace="System.ServiceModel" #&amp;gt;
&amp;lt;#@ Import Namespace="Microsoft.Practices.ServiceFactory.ServiceContracts" #&amp;gt;
&amp;lt;#@ Import Namespace="Microsoft.Practices.ServiceFactory.DataContracts" #&amp;gt;
&amp;lt;#@ Import Namespace="Microsoft.Practices.ServiceFactory.Extenders.DataContract.Wcf" #&amp;gt;
&amp;lt;#@ Import Namespace="Microsoft.VisualStudio.Modeling" #&amp;gt;
&amp;lt;#@ Assembly Name="Microsoft.Practices.ServiceFactory.DataContracts.Dsl, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5d6a471ce3d28564" #&amp;gt;
&amp;lt;#@ Assembly Name="Microsoft.Practices.ServiceFactory.Extenders.DataContract.Wcf, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5d6a471ce3d28564" #&amp;gt;
&amp;lt;#@ Assembly Name="Microsoft.Practices.ServiceFactory.ServiceContracts.Dsl, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5d6a471ce3d28564" #&amp;gt;
&amp;lt;#@ Assembly Name="System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" #&amp;gt;
&amp;lt;#@ ModelInjector processor="ModelInjectorDirectiveProcessor" #&amp;gt;
&amp;lt;#@ include file="TextTemplates\WCF\CS\CommonTextTransformation.tt" #&amp;gt;
//------------------------------------------------------------------------------
// &lt;auto-generated&gt;
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// &lt;/auto-generated&gt;
//------------------------------------------------------------------------------

using System;
using System.Workflow;
using System.Workflow.ComponentModel;
using System.Workflow.Runtime;
using System.Workflow.Runtime.Hosting;
using System.Workflow.Activities;
using WCF = global::System.ServiceModel;

namespace &amp;lt;#=CurrentExtender.ArtifactLink.Namespace#&amp;gt;
{	
	/// &lt;summary&gt;
	/// Service Class - &amp;lt;#= CurrentElement.Name #&amp;gt;
	/// &lt;/summary&gt;
	[WCF::ServiceBehavior(Name = "&amp;lt;#= CurrentElement.Name #&amp;gt;", 
		Namespace = "&amp;lt;#= CurrentExtender.Namespace #&amp;gt;", 
		InstanceContextMode = WCF::InstanceContextMode.&amp;lt;#= CurrentExtender.InstanceContextMode.ToString() #&amp;gt;, 
		ConcurrencyMode = WCF::ConcurrencyMode.&amp;lt;#= CurrentExtender.ConcurrencyMode.ToString() #&amp;gt; )]
	public abstract class &amp;lt;#= CurrentElement.Name #&amp;gt;Base &amp;lt;#= BuildInheritedServiceContract(CurrentElement.ServiceContract) #&amp;gt;
	{
&amp;lt;#
		if (CurrentElement.ServiceContract != null) {
#&amp;gt;
		#region &amp;lt;#= CurrentElement.ServiceContract.Name #&amp;gt; Members
&amp;lt;#
		foreach(Operation operation in CurrentElement.ServiceContract.Operations)
		{
			WCFOperationContract wcfOperation = GetObjectExtender&lt;wcfoperationcontract&gt;(operation);
            if(wcfOperation.GenerateCode)
            {
			    if (!wcfOperation.AsyncPattern)
			    {

#&amp;gt;

		public virtual &amp;lt;#= GetResponseElement(operation.Response) #&amp;gt; &amp;lt;#= operation.Name #&amp;gt;(&amp;lt;#= GetRequestElement(operation.Request) #&amp;gt;)
		{
			&amp;lt;#= GetImplementation(operation.Request) #&amp;gt;
		}

&amp;lt;#
			    }
			    else
			    {

#&amp;gt;

		public virtual IAsyncResult Begin&amp;lt;#=operation.Name#&amp;gt;(&amp;lt;#= GetRequestElement(operation.Request,UseComma.Yes) #&amp;gt;AsyncCallback callback, object state)
		{
			return null;
		}

		public virtual &amp;lt;#= GetResponseElement(operation.Response) #&amp;gt; End&amp;lt;#=operation.Name#&amp;gt;(IAsyncResult result)
		{
			&amp;lt;#= GetImplementation(operation.Request) #&amp;gt;
		}

&amp;lt;#			    } // close if AsyncPattern
            } // close if Generatecode

		} // close Operations foreach
#&amp;gt;
		#endregion		
&amp;lt;#
	} // end if
#&amp;gt;		
	}
	
	public partial class &amp;lt;#= CurrentElement.Name #&amp;gt; : &amp;lt;#= CurrentElement.Name #&amp;gt;Base
	{
&amp;lt;#
		if (CurrentElement.ServiceContract != null) {
#&amp;gt;
		#region &amp;lt;#= CurrentElement.ServiceContract.Name #&amp;gt; Members
&amp;lt;#
		foreach(Operation operation in CurrentElement.ServiceContract.Operations)
		{
			WCFOperationContract wcfOperation = GetObjectExtender&lt;wcfoperationcontract&gt;(operation);
            if(wcfOperation.GenerateCode)
            {
			    if (!wcfOperation.AsyncPattern)
			    {

#&amp;gt;

		public override &amp;lt;#= GetResponseElement(operation.Response) #&amp;gt; &amp;lt;#= operation.Name #&amp;gt;(&amp;lt;#= GetRequestElement(operation.Request) #&amp;gt;)
		{
			&amp;lt;#= GetWCFImplementation(operation) #&amp;gt;
		}

&amp;lt;#
			    }
			    else
			    {

#&amp;gt;

		public override &amp;lt;#= GetResponseElement(operation.Response) #&amp;gt; End&amp;lt;#=operation.Name#&amp;gt;(IAsyncResult result)
		{
			&amp;lt;#= GetWCFImplementation(operation) #&amp;gt;
		}

&amp;lt;#			    } // close if AsyncPattern
            } // close if Generatecode

		} // close Operations foreach
#&amp;gt;
		#endregion		
&amp;lt;#
	} // end if
#&amp;gt;		
	}
	
}

&amp;lt;#+
	private string BuildInheritedServiceContract(ServiceContract serviceContract)
	{
		if (serviceContract == null)
		{
			return string.Empty;
		}
		else
		{
			return ": " + ResolveServiceContractNameAndAddProjectRef(serviceContract);
		}
	}
	
	private string ResolveServiceContractNameAndAddProjectRef(ServiceContract contract)
	{
		WCFServiceContract wfcSc = GetObjectExtender&lt;wcfservicecontract&gt;(contract);
		if(wfcSc == null)
		{
			return "I" + contract.Name;
		}
		AddProjectReference(wfcSc.ArtifactLink);
		return wfcSc.ArtifactLink.Namespace + ".I" + contract.Name;
	}

	private string GetWCFImplementation(Operation operation)
	{
		if(operation.Response != null)
		{
			string result = "";
    		WCFOperationContract wcfOc = GetObjectExtender&lt;wcfoperationcontract&gt;(operation);

            if(wcfOc.InvokesWorkflow)
            {
				string nl = "\r\n\t\t\t"; 
                result = "// Build the parameters for the Workflow" + nl;
                result += "Dictionary&lt;string object=""&gt; parameters = new Dictionary&lt;string object=""&gt;();" + nl;

                foreach(MessagePart mPart in operation.Request.MessageParts)
                { 
                    result += "parameters.Add(\"" + mPart.Name +"\", " + operation.Request.Name + "." + mPart.Name+ ");" + nl; 
                }

                result += "// Execute the Workflow" + nl;
                result += "// TODO: change myWorkflow to the name of the workflow to execute" + nl;
                result += "WFHost.ExecuteWorkflow(typeof(myWorkflow), parameters);" + nl;
                result += "// Build the response based on the parameters" + nl;
                result += GetResponseElement(operation.Response) + " response = new " + GetResponseElement(operation.Response) + "();" + nl;

                foreach(MessagePart mPart in operation.Response.MessageParts)
                { 
                    if(mPart is DataContractMessagePart)
                    {
                        DataContractMessagePart dcmPart = (DataContractMessagePart)mPart;
                        result += "response." + mPart.Name + " = (" + dcmPart.Type + ")parameters[\"" + mPart.Name + "\"];" + nl; 
                    }
                    else if(mPart is XsdElementMessagePart)
                    {
                        XsdElementMessagePart xsdmPart = (XsdElementMessagePart)mPart;
                        int nPos = xsdmPart.Element.IndexOf('?');
                        string strTemp = xsdmPart.Element.Substring(nPos + 1, (xsdmPart.Element.Length - (nPos + 1)));
                        result += "response." + mPart.Name + " = (" + strTemp + ")parameters[\"" + mPart.Name + "\"];" + nl; 
                    }
                }

                result += "return response;" + nl;
            }
            else
            {
                result = "return null;";
            }

			return result;
		}

		return string.Empty;
	}
#&amp;gt;
&lt;/string&gt;&lt;/string&gt;&lt;/wcfoperationcontract&gt;&lt;/wcfservicecontract&gt;&lt;/wcfoperationcontract&gt;&lt;/wcfoperationcontract&gt;&lt;/pre&gt;
&lt;p&gt;Below is the generated code based on the template changes:&lt;/p&gt;&lt;pre&gt;//------------------------------------------------------------------------------
// &lt;auto-generated&gt;
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// &lt;/auto-generated&gt;
//------------------------------------------------------------------------------

using System;
using System.Workflow;
using System.Workflow.ComponentModel;
using System.Workflow.Runtime;
using System.Workflow.Runtime.Hosting;
using System.Workflow.Activities;
using WCF = global::System.ServiceModel;

namespace WCF.ServiceImplementation
{	
	/// &lt;summary&gt;
	/// Service Class - MyService
	/// &lt;/summary&gt;
	[WCF::ServiceBehavior(Name = "MyService", 
		Namespace = "WCF.ServiceImplementation", 
		InstanceContextMode = WCF::InstanceContextMode.PerSession, 
		ConcurrencyMode = WCF::ConcurrencyMode.Single )]
	public abstract class MyServiceBase : WCF.ServiceContracts.IMyServiceContract
	{
		#region MyServiceContract Members

		public virtual WCF.MessageContracts.Response MyOperation(WCF.MessageContracts.Request request)
		{
			return null;
		}

		#endregion		
		
	}
	
	public partial class MyService : MyServiceBase
	{
		#region MyServiceContract Members

		public override WCF.MessageContracts.Response MyOperation(WCF.MessageContracts.Request request)
		{
			// Build the parameters for the Workflow
			Dictionary&lt;string object=""&gt; parameters = new Dictionary&lt;string object=""&gt;();
			parameters.Add("RequestPayload", Request.RequestPayload);
			// Execute the Workflow
			// TODO: change myWorkflow to the name of the workflow to execute
			WFHost.ExecuteWorkflow(typeof(myWorkflow), parameters);
			// Build the response based on the parameters
			WCF.MessageContracts.Response response = new WCF.MessageContracts.Response();
			response.ResponsePayload = (ServiceResponse)parameters["ResponsePayload"];
			return response;
			
		}

		#endregion		
		
	}
	
}
&lt;/string&gt;&lt;/string&gt;&lt;/pre&gt;
&lt;p&gt;I made similar changes for the ASMX implementation but tailored them towards using the native workflow hosting methods. As you can see, you can make some minor modifications to the new version of the Service Factory, and provide a lot of additional functionality without much work. &lt;/p&gt;
&lt;p&gt;If you decide you want to keep the original Technology Extensions intact, you can always create another Technology Extension based on one of the original extensions with just your changes. This way you can offer users of the service factory multiple custom implementations.&lt;/p&gt;
&lt;p&gt;I think the direction the Service Factory Team has taken this new version of the service factory is really promising and I hope to see them apply these new extensions to the rest of the factories. Keep up the good work!&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.pheedo.com/click.phdo?x=6cda6ad746d942b9a1110d0715a4fa12&amp;u=115376"&gt;&lt;img src="http://www.pheedo.com/img.phdo?x=6cda6ad746d942b9a1110d0715a4fa12&amp;u=115376" border="0"/&gt;&lt;/a&gt;&lt;/p&gt;&lt;iframe src="http://ads.geekswithblogs.net/a.aspx?ZoneID=5&amp;amp;Task=Get&amp;amp;PageID=31016&amp;amp;SiteID=1" width=1 height=1 Marginwidth=0 Marginheight=0 Hspace=0 Vspace=0 Frameborder=0 Scrolling=No&gt;
&lt;script language='javascript1.1' src="http://ads.geekswithblogs.net/a.aspx?ZoneID=5&amp;amp;Task=Get&amp;amp;Browser=NETSCAPE4&amp;amp;NoCache=True&amp;PageID=31016&amp;amp;SiteID=1"&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;a href="http://ads.geekswithblogs.net/a.aspx?ZoneID=5&amp;amp;Task=Click&amp;amp;Mode=HTML&amp;amp;SiteID=1&amp;amp;PageID=31016" target="_blank"&gt;
&lt;img src="http://ads.geekswithblogs.net/a.aspx?ZoneID=5&amp;amp;Task=Get&amp;amp;Mode=HTML&amp;amp;SiteID=1&amp;amp;PageID=31016" width="1" height="1" border="0"  alt=""&gt;&lt;/a&gt;
&lt;/noscript&gt;
&lt;/iframe&gt;
&lt;img src="http://geekswithblogs.net/jlavin/aggbug/115376.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jim Lavin</dc:creator>
            <guid>http://geekswithblogs.net/jlavin/archive/2007/09/15/115376.aspx</guid>
            <pubDate>Sat, 15 Sep 2007 09:59:56 GMT</pubDate>
            <wfw:comment>http://geekswithblogs.net/jlavin/comments/115376.aspx</wfw:comment>
            <comments>http://geekswithblogs.net/jlavin/archive/2007/09/15/115376.aspx#feedback</comments>
            <wfw:commentRss>http://geekswithblogs.net/jlavin/comments/commentRss/115376.aspx</wfw:commentRss>
            <trackback:ping>http://geekswithblogs.net/jlavin/services/trackbacks/115376.aspx</trackback:ping>
        </item>
    </channel>
</rss>