In an earlier post I promised to share some more details about integrating DSL Tools with GAT. I am using some of the “techniques” in this post to experiment with integrating our Service Description Language with Service Factory.
As far as I know there are two possible approaches for integrating DSL Tools with GAT. In the first approach we simply execute GAT recipes from with the DSL Tools and pass the necessary arguments (we collect from the DSL model) to the recipe in the “execute call”. In the second approach we create “value providers” that are able to read the DSL model (selected shape, model root, etc.) and can be used directly in the recipe definition (“ValueProvider” tag) in GAT. It’s obvious that the second approach offers a more solid solution. For my experiment I tried both approaches. To be honest I don’t expect that I need my “experimental code” for too long. I can’t believe that Microsoft isn’t going to make something available to support this “out of the box”. This post of Mauro indicates that I might be rightJ.
Let’s have a look at the first approach and execute the “GenerateASMXDataContractFromSchemas” recipe from the “ASMXGuidancePackage” from the DSL message shape to generate the datatypes for all DataContracts modelled in the message shape of the DSL. The screenshot from this example can be found in this post.
Experimental hive
Before we start we have to make sure that the DSL isn’t using the “Exp hive”. GAT is registered in the “main hive” so we have to use the same hive both the DSL Tools and GAT for our integration experiment.
I did this by changing the .csproj files for the “DSL” project and the “DSLPackage” project.
In the “Dsl.csproj” file I deleted the “/rootsuffix Exp” from the <StartArguments> tag
<StartArguments>/rootsuffix Exp /DesignTimeRun "..\..\..\Debugging\Debugging.sln"</StartArguments>
In the “DslPackage.csproj” I did the same action and also deleted “Exp” from the “<TargetRegistryRoot>”
<TargetRegistryRoot>Software\Microsoft\VisualStudio\8.0Exp</TargetRegistryRoot>
Service Factory Guidance Packages
Of course we need to debug the new code for calling recipes in our DSL. To make sure the GAT packages are enabled for the “DSL debugging solution” I created a new solution based on the “ASMX Service” template of the “Web Service Software Factory” package. After that I changed the “debugging solution” for the DSL by replacing “Debugging\Debugging.sln” in the “<StartArguments>” tag in both project files with the correct path and name of the new solution based on the “ASMX Service” template.
After making the above described changes, the newly created Service Factory solution opens up as the debugging solution (in a new VS2005 instance) for our DSL. So, now we are ready to write some code to actually call the GAT recipes from the DSL.
Reference RecipeFramework dll’s
To make the code compile we need to reference the “Microsoft.Practices.RecipeFramework.dll” and the “Microsoft.Practices.RecipeFramework.Extension.dll” from the DslPackage project. The “Extension” dll is part of Service Factory and doesn’t have a strong name. To solve this issue we can open the “ASMX Guidance Package.sln” located in “C:\Program Files\Microsoft Service Factory” (default location), sign the extension project and compile it.
Code
To make the code work we need the following using statements:
using Microsoft.Practices.RecipeFramework.Services;
using Microsoft.Practices.RecipeFramework.Extensions.CommonHelpers;
I assume the code stated below is coded in the “OnMenu…” method for a custom command for one of the shapes in the DSL. Please not that the code example is just demo code!
EnvDTE.Project dataTypeProject = null;
Hashtable arguments = new Hashtable();
EnvDTE.DTE dte = this.CurrentDocData.Store.GetService(typeof(EnvDTE._DTE)) as EnvDTE.DTE;
IRecipeManagerService recipeManager = (IRecipeManagerService)this.ServiceProvider.GetService(typeof(IRecipeManagerService));
GuidancePackage serviceFactory = recipeManager.GetPackage("CustomASMXGuidancePackage");
EnvDTE.Project serviceProject = DteHelperEx.FindProjectByName(dte, "Service", false);
foreach (ProjectItem project in serviceProject.ProjectItems)
{
if (project.Name.Contains("DataTypes"))
{
dataTypeProject = (EnvDTE.Project)project.SubProject;
}
}
In this first code snippet we can see that get a reference to the Visual Studio infrastructure. After that we get a reference to the “RecipeManager”. We will use this reference to instantiate the Service Factory Guidance Package. On my machine this is called “CustomASMXGuidancePackage”. This is because I already made some other modifications in the package.
The recipe that I am using in this example needs a reference to the Service Factory “DataType” project to execute correctly. We can use the “DteHelperEx” class located in “Microsoft.Practices.RecipeFramework.Extension.dll” to help with that. Unfortunately I wasn’t able to get a reference to this “DataType” project directly. This is probably because the project is located under the “Service” solution folder in the Service Factory solution structure. I didn’t take the time to investigate this any further so I took another approach for this demo code.
First I get a reference to the “Service” Solution folder which of type EnvDTE.Project. After that, I iterate over the ProjectItems in the “Service” solution folder to get a reference to the “DataTypes” project.
// Add DSL DataContract's to Service Factory DataType Project
Message message = ((MessageShape)this.SingleSelection).ModelElement as Message;
if (message != null)
{
foreach (DataContract dataContract in message.DataContracts)
{
if (DteHelperEx.FindItemByName(dataTypeProject.ProjectItems, Path.GetFileName(dataContract.SchemaLocation), true) == null)
{
dataTypeProject.ProjectItems.AddFromFileCopy(dataContract.SchemaLocation);
}
}
}
arguments.Add("DataContractsProject", dataTypeProject);
serviceFactory.Execute("GenerateASMXDataContractFromSchemas", arguments);
In the second code snippet we can see that we get a reference to the selected message shape in the model, iterate over all the data contracts in the message to add the XSD files that describe the datacontract to the “DataType” project (if not already added). The XSD files are referenced in a “SchemaLocation” property for the DataContract in the DSL.
After that we add the reference to the “DataType” project, needed as an argument for the recipe, to the Hashtable named “arguments”. You can find the necessary arguments for the recipe in the xml file that the recipe is defined in. In this case “GenerateASMXDataContractFromSchemas.xml” (located in “C:\Program Files\Microsoft Service Factory\Guidance Packages\ASMX Guidance Package\ASMX Guidance Package\Recipes\ASMX”). The last line of the code snippet finally executes the recipes on the instance of the guidance package.
Of course in this very simple example we are only sending one argument to a recipe that doesn’t have a “wizard form” attached to collect the values for the arguments. But the same approach can be used for recipes that do include wizard forms. However in that case it might not make sense to just display the values that are passed from the DSL to the recipe wizard form and ask the user to press the “Next” button on the wizard form.
For that situation it might be better to modify the recipe and/or write a value provider that can be used to read the DSL model values directly from the recipe. In a next post we will have a look how we can change a Service Factory recipe to make use of a custom build value provider that reads the DSL model.
[Update: added missing link]