Do you have the new version of the Guidance Automation Toolkit and the Guidance Automation Extensions installed? Well, then it’s time we take off on a trip into the “zone” just like Thomas likes to say… In this case, we’re leaving to the “guidance-zone”!
So what are the possibilities? What kind of guidance package do we want to develop? What audience will it target? What procedures will it automate? That’s a whole set of questions we have to answer before starting the development phase. But we have to do something else too… Before you can start off experimenting fully with the Guidance Automation Toolkit, there are some basic concepts that one should understand well so we can take full advantage of the capabilities of the toolkit.
One of the first concepts, a true GAPD (Guidance Automation Package Developer) should master, is the diference between bound and unbound recipes. When do we use bound recipes, or when do we choose to implement unbound recipes? …
Some basic things to know…
The goal of a recipe, is automating a serie of actions, that are normally manually performed by a developer in each project. Using a recipe will ensure that these series of actions will be performed correctly and consistently. A recipe, can trigger a number of actions which have to be defined within the recipe. You can use an existing action (meaning one provided by the Recipe Framework) within the XML-file, or you can develop your own custom actions. The only key to defining your own actions, is making them inherit the Microsoft.Practices.RecipeFramework.Action-class.
More information and an example of developing actions, will follow later…
In most of the cases, a recipe needs a set of arguments to execute its actions. These arguments can be either required or optional. We can gather these arguments in two different ways: we let the user define the argument’s value using a wizard or we can use value providers, which are implementations of interfaces that can be used by the framework to gather arguments. The strength of value providers lies in the ability to intercept changes in other arguments, so you always obtain a value that’s completely up-to-date. Value providers implement the Observer pattern in this way. When an argument which is observed, changes it’s value, it notifies all the arguments that are observing it, so they can update their values.
Getting to the different recipe types
As we all have derived from the title by now, there are two kind of recipes within the Recipe Framework: bound and unbound recipes.
A bound recipe, is a recipe that is associated with a specified Visual Studio solution element. This can be a solution, a project, a class file… The recipe will only appear in the context menu on the item you have specified to bound it to (only on solution level for example).
A bound recipe can again be recurring or non-recurring.
Easily said: a recurring recipe is like a recipe to add a test class, it wil contain a basic test method from which to start, it wil contain the correct namespace references to get you started, and so on… You can add as many test classes as you want, meaning that that specific recipe, is recurring.
When using a non-recurring recipe, once the recipe is executed, you can’t choose to execute it again. A case scenario in which you would want to create a non-recurring recipe is when you want to create a static gateway. Imagine this static singleton class only serves to hold single instances of objects and provide it for usage in other classes using properties. We could develop a recipe that would create this class, for all objects you want to instantiate just once (you could let the user choose these classes in a wizard for example). You only want to create this static gateway once, so once the recipe is executed, it is never again available. It’s possible you would want to regenerate this static singleton class, adding more or less objects in the wizard, but let’s assume in this case, you would do this in a phase of your project where this decision can’t vary.
Bound recipes are bound to a bound recipe reference. A bound recipe reference is an object used by the Recipe Framework to associate a particular recipe with a particular launch point - or speaking in Visual Studio terms, a command. The reference enables the recipe to be executed, and stores it’s state (for example, has it been executed already?).
On the other hand, we have unbound recipes. Unbound recipes aren’t asociated with any item within Visual Studio. The recipe is not “placed” on solution-, project-, nor item-level. These kind of recipes work in a whole other way. Unbound recipes are associated with unbound recipe references, and these references, selects the solution items it’s associated with or used in.
As I just said, the unbound recipe reference chooses the solution items to appear on, but it does this according to a selection condition, a so called Target Condition. This condition is written in code, thus it is defined by us, the GAPD’s. It can be a simple condition and be satisfied by a very broad number of scenarios, or quite complex thus only satisfied in very specific cases. A TargetCondition is a piece of code returning a boolean value which specifies whether the condition was met or not.
Let’s discuss a concrete example. You’re developing a guidance package, which contains a number of folders inside the Domain-project that you have predefined for usage. Since you specify an “Exceptions” folder, you want all the custom Exceptions placed inside this folder, that’s obvious. You also developed a start for custom Exception classes, which you defined as “Exception class” (which is the same as a normal class, but already inherits the ApplicationException class and contains the correct namespace instead). You only want Exception classes within the specific Exceptions-folder, but how do we enforce it???
Well, this is where the unbound recipes come to the rescue. You write a simple condition in code, which checks if the item on which the user clicked is a folder, and also checks it name…. If the recipe is satisfied, an extra command appears in the context menu of that folder: “Add new Custom Exception class”.
How does VS deal with this?
Actually I really wondered how this worked when I started with GAT. When we register a guidance package, one of the main things that happen, is that Visual Studio is told what commands we want to specify on each item in Visual Studio. In the Visual Studio world, this is not dynamic. The commands are loaded before the package, thus Visual Studio needs to know what “icons” it has to display on context-menu’s before anything. That’s why the registering process is so important (of course, this is not the only reason). The action behind the command, that’s where the guidance package comes to the scene. Olaf Conijn explained this to us on the VISUG-event about Software Factories we had a while ago.
So, what about the unbound recipes? How could this be predefined? They actually seem quite dynamic since the commands to unbound recipes are only shown if the condition was satisfied… Well, every time a solution element is selected, Visual Studio queries the Recipe Framework and calls the TargetCondition of each unbound recipe in the package. If the condition returns true, the corresponding command is displayed in the context menu. In the last sentence, I want to emphasise “displayed in the context menu”. VS uses dynamic visibility for unbound recipes. When registering a package, the items on which the unbound recipes can be available, are in deed registered. But it’s when such an item in VS is clicked, that VS adjusts the visibility of commands according to the TargetCondition of the unbound recipes.
So, I think -and hope- you now understand the difference between bound and unbound recipes.
To summarize it in a sentence, a bound recipe is a recipe attached to a particular item in your project while unbound recipes aren’t attached to any item, but contain a serie of conditions which in combination decide whether you can or not execute the recipe in a certain scenario.
Stay tuned for more GAT-talks!