DSL
***Disclaimer: This information applies to Visual Studio 2010 Beta 1 only.***
Another new feature of the new version of the DSL toolkit is the ability to locks certain operations in a DSL. The idea is that we can define locking policies for a DSL and those policies are executed at runtime to constraint specific operations such as Add/Delete/Update etc.
These locking policies can be defined at three different levels , Store, Partition and ModelElement respectively.
Defining our own locking policies
It is really simple to create our own policy class, we just need to create a class that inherits from ILockingPolicy.
Then we need to define the locks that we are going to return, and for that we have three levels of granularity:
- Store
- Partition
- ModelElement
Locks are transitive following the “Store –> Partition –> ModelElement” object hierarchy.
That means that if we set locks at the Store level, those locks also apply to the Partition and the ModelElement levels.
The lock types are:
public class MyLockingPolicy : ILockingPolicy
{
public Locks RefineLocks(ModelElement element, Locks proposedLocks)
{
return proposedLocks;
}
public Locks RefineLocks(Partition partition, Locks proposedLocks)
{
return proposedLocks;
}
public Locks RefineLocks(Store store, Locks proposedLocks)
{
// TODO: return corresponding locks and they will also apply to Partition and ModelElement
}
}
Injecting our own ILockingPolicy
The Store class constructor sets the locking policy by querying for a ILockingPolicy service. That means that one possible way to inject our own locking policy is to override the GetService method and return our policy class:
internal partial class MyDocData
{
private ILockingPolicy myLockingPolicy;
public override object GetService(Type serviceType)
{
if (serviceType == typeof(SLockingPolicy) || serviceType == typeof(ILockingPolicy))
{
if (myLockingPolicy == null)
{
myLockingPolicy = new MyLockingPolicy();
}
return myLockingPolicy;
}
return base.GetService(serviceType);
}
}
At runtime the DSL infrastructure will query for existing locks using a set of extension methods located in the new ImmutabilityExtensionMethods class and constraint or not an operation accordingly.
The hardware model sample
I published a sample that shows how to lock a DSL.
The scenario is that there are two personas:
- Developer: He uses the VS full IDE to author a hardware model.
- Architect: He uses an Isolated shell to review the model and cannot do any modifications to it.
The locks are being set by detecting the shell SKU.
Pablo
***Disclaimer: This information applies to Visual Studio 2010 Beta 1 only.***
In the first part of this series I described general concepts around the ModelBus infrastructure. In this second part I will describe the model bus service and how MEF is involved.
The ModelBus as a Service
The Microsoft.VisualStudio.Modeling.Sdk.Integration.Shell.10.0 is a VS Package that defines one core service, the IModelBus service.
The model bus service creates and uses its own MEF composition container with two directory catalogs pointing to:
- %LocalAppData%\Microsoft\VisualStudio\10.0\Extensions
- %VS10_Install_Dir%\Extensions
A DSL exports an adapter manager and provides some metadata to specify which adapter this manager manages:
[Export(typeof(ModelBusAdapterManager))]
[HandlesAdapter(“MyAdapterId”)]
public partial class MyAdapterManager : VsModelingAdapterManager
{
…
}
The main responsibility of the model bus is to find registered adapter managers, for that the composition container is used:
container.GetExportedObjects<ModelBusAdapterManager>()
In order to register a DSL with the model bus we need to do the following steps:
- Update the model definition to introduce a domain property that will hold the model reference
- Update the model serialization behavior to include the model reference serialization
- Create an adapter for the DSL
- Create an export an adapter manager for the DSL
- Deploy the DSL under extensions (done automatically at compilation time via the VS SDK msbuild target)
Stay tuned,
Pablo
***Disclaimer: This information applies to Visual Studio 2010 Beta 1 only.***
One of the new main features of the new version of the DSL toolkit is the “ModelBus”. The model bus basically allows a model element to reference another model element (ModelBusReference). These two model elements could be defined either in the same DSL or in different ones. With a ModelBusReference we can do interesting model interactions such as navigating the references back and forth, performing validation across models, etc.
The ModelBus infrastructure lives in two core assemblies:
- Microsoft.VisualStudio.Modeling.Sdk.Integration.10.0
- Microsoft.VisualStudio.Modeling.Sdk.Integration.Shell.10.0
Microsoft.VisualStudio.Modeling.Sdk.Integration.10.0
As the name implies, this assembly contains concepts that are totally decoupled from Visual Studio.
The main generic concepts are:
ModeBusAdapter
Is a bridge between the model bus and the underlying model. Therefore each model has its own adapter.
It’s main responsibilities are:
- Get all references of an element type
- Resolve a reference into an object
- Get the view associated with the model
ModelBusAdapterManager
Is used to create and control the lifetime of adapters.
It’s main responsibilities are:
- Create adapters
- Create reference to a model
- Return the list of supported adapters
- Return the list of exposed element types. An exposed element type is an element that can be referenced by another element
ModelBusReference
Encapsulates information about a reference in the model bus. It is composed of:
- Adapter reference
- Adapter id
- Model display name
- Element display name
It is being serialized as:
ModelBusAdapterReference
Represents the base class for adapter references
ModelBusView
Allows a client to manipulate the design surface of the model (if applicable)
It’s main responsibilities are:
- Open view
- Close view
- Show view
- Hide view
- Select UI elements
Then we have a set of derived classes that are specific to DSLs:
- ModeBusAdapter<- ModelingAdapter
- ModelBusAdapterReference <- ModelingAdapterReference
ModelingAdapter
Represents the base class for DSL based adapters. In this case the model is a DSL model.
ModelingAdapterReference
Encapsulates information about a model element reference, it is composed of:
- Model element id
- View id
- Model file absolute path
It is being serialized as:
Microsoft.VisualStudio.Modeling.Sdk.Integration.Shell.10.0
In this assembly there are concepts that are coupled to the Visual Studio infrastructure.
VsModelingAdapterManager
This adapter manager introduces the notion of document handlers that knows how to handle a DocData concept.
VsModelingView
This implementation is tied to the DocView concept.
Stay tuned,
Pablo
***Disclaimer: This information applies to Visual Studio 2010 Beta 1 only.***
There is a really useful utility in the new version of DSL, that is the Store Viewer.
The API to use it is really simple:
using Microsoft.VisualStudio.Modeling.Diagnostics;
StoreViewer.Show(store);
With this viewer already implemented, the next logic step for me is to have a VS debugger visualizer. We should cross the fingers and maybe in the next Beta 2 we will see it.
Pablo
My eighteenth How do I is up.
Scenario
Let's say that we start from the Minimal Language DSL template and we have an ExampleElement domain class that has a Name domain property with the Is Element Name flag set to true.
Because it has the IsElementName flag, every time that we create a ExampleElement with the UI (toolbox or model explorer) the element is created with a default name. This default name is composed of the type name of the concept plus an index. Example : ElementName1, ElementName2, etc.
The focus of this How do I shows how to create the model element programmatically.
Interfaces and classes needed
Code snippet
Wrong way:
private void CreateElement(Store store)
{
using(Transaction tx = store.TransactionManager.BeginTransaction("Add ExampleElement"))
{
ExampleModel root = store.ElementDirectory.FindElements<ExampleModel>().First() as ExampleModel;
ExampleElement element =
this.Store.ElementFactory.CreateElement(ExampleElement.DomainClassId) as ExampleElement;
root.Elements.Add(element);
tx.Commit();
}
}
Right way:
private void CreateElement(Store store)
{
using(Transaction tx = store.TransactionManager.BeginTransaction("Add ExampleElement"))
{
ExampleModel root = store.ElementDirectory.FindElements<ExampleModel>().First() as ExampleModel;
ExampleElement element =
store.ElementFactory.CreateElement(ExampleElement.DomainClassId) as ExampleElement;
ElementOperations elementOperations =
new ElementOperations(store as IServiceProvider, store);
ElementGroup elementGroup = new ElementGroup(store);
elementGroup.Add(element);
elementGroup.MarkAsRoot(element);
elementOperations.MergeElementGroup(root, elementGroup);
tx.Commit();
}
}
Assemblies needed
- Microsoft.VisualStudio.Shell.Interop
- Microsoft.VisualStudio.Modeling.Sdk.Shell
- Microsoft.VisualStudio.Modeling.Sdk.Diagrams
Stay tuned,
Pablo
Here are some pictures for the VSX Developer Conference with me in the front. :)
The videos are going to posted at MSDN soon...

Pablo
As Victor announced it, after great efforts the T4 Editor v1.0 was finally out.
I don't need to tell you how much the user experience can drastically change when you use it, having syntax coloring, intellisense, code generation preview, support for multiple T4 hosts, etc etc.
Check it out here
Pablo
My fourteenth How do I is up.
Scenario
Let's say that we want to cancel an editing operation in a model element property if the new value meets a certain condition. The focus of this How do I tackles this scenario.
In the next sample we have a MyLanguage dsl and a MyElement domain class and we want to cancel the editing operation over the "Foo" property if the new value is empty.
Interfaces and classes needed
Code snippet
public partial class MyLanguageDomainModel
{
protected override Type[] GetCustomDomainModelTypes()
{
var types = new List<Type>();
types.AddRange(base.GetCustomDomainModelTypes());
types.Add(typeof(MyElementChangeRule));
return types.ToArray();
}
}
[RuleOn(typeof(MyElement), FireTime = TimeToFire.TopLevelCommit)]
public partial class MyElementChangeRule : ChangeRule
{
public override void ElementPropertyChanged(ElementPropertyChangedEventArgs e)
{
if(!e.ModelElement.Store.TransactionManager.CurrentTransaction.IsSerializing)
{
if(e.DomainProperty.Name.Equals("Foo"))
{
if(string.IsNullOrEmpty(e.NewValue.ToString()))
{
//The name cannot be empty, therefore we cancel the change
PackageUtility.ShowError(e.ModelElement.Store, "Value cannot be empty");
e.ModelElement.Store.TransactionManager.CurrentTransaction.Rollback();
}
}
}
}
}
Assemblies needed
- Microsoft.VisualStudio.Modeling.Sdk, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
Stay tuned,
Pablo
My ninth How do I is up.
Scenario
Let's say that we have some "Add rules" in our model to perform some custom operation every time a model element is added. One common problem is that we want to perform this operation only when the user creates a model element on demand, this can be translated to the user drags a shape from the toolbox, uses the model explorer, etc. But the add rule is also fired when the store is being deserialized and somehow we need to distinguish both cases. The focus of this How do I tackles this scenario.
Interfaces and classes needed
Code snippet
var transaction = e.ModelElement.Store.TransactionManager.CurrentTransaction;
if(transaction != null && !transaction.IsSerializing)
{
//TODO: perform operation
}
Assemblies needed
- Microsoft.VisualStudio.Modeling.Sdk
Stay tuned,
Pablo
My sixth How do I is up.
Scenario
The scenario here is really simple, we want to get the shape associated with a model element or vice versa. The focus of this How do I tackles this scenario.
Interfaces and classes needed
Code snippet
Getting the model element corresponding shape:
PresentationViewsSubject.GetPresentation(modelElement)
.ForEach(pel =>
{
ShapeElement shape = pel as ShapeElement;
//TODO: use shape
});
Getting the model element corresponding shape:
ModelElement modelElement = PresentationViewsSubject.GetSubject(shape);
//TODO: use model element
Assemblies needed
- Microsoft.VisualStudio.Modeling.Sdk.Diagrams
Stay tuned,
Pablo
TTxGen is a generic single file generator based on the text templating engine.
What is that???, Some background:
Single file generators or custom tools are COM components registered with Visual Studio that generate code.
To implement a custom tool you basically need to implement the IVsSingleFileGenerator interface. For more detail you can read kzu's post
A custom tool also needs to be registered and associated to file a extension.
The IVsSingleFileGenerator interface declares two methods:
int DefaultExtension (
out string pbstrDefaultExtension
)
This method is used to specify the extension of the generated file.
int Generate (
[InAttribute] string wszInputFilePath,
[InAttribute] string bstrInputFileContents,
[InAttribute] string wszDefaultNamespace,
[OutAttribute] IntPtr[] rgbOutputFileContents,
out uint pcbOutput,
[InAttribute] IVsGeneratorProgress pGenerateProgress
)
This method is used to generate the code that we want. It receives the name of the file associated with custom tool, the content of the file and it returns a Byte[] with the generated code.
One serious drawback is that we need to implement, register and deploy one custom tool for each representation of custom code that we want to generate.
Ok that's all for custom tools, now the funny part...
As you may know the text templating (t4) engine is now part of Visual Studio 2008. Based on this I created an implementation of a generic single file generator using t4.
The idea here is to have the same context as in custom tools. For that I have created a custom directive processor that injects the context needed on the tt:
<#@ output extension=".xml" #>
<#@ ParentFileInjector processor="TTxGenDirectiveProcessor" requires="fileName='Sample.xml'" #>
<#= this.ParentFileName #>
<#= this.ParentFileContent #>
If we want to access the parent file name we can use the ParentFileName variable, and for the content we can use the ParentFileContent.
TTxGen comes with some tooling to create a xGen template with the proper directives and context to start generating code:
The Create xGen Template command unfold a tt template, default the extension to the extension of the parent file and it automatically replace the requires argument.
You can download TTxGen from codegallery.
Pablo
What about adding any C# 3.5 feature (linq, extension methods, partial methods, etc) inside a text template file?
The procedure is really easy, just add the "C#v3.5" or "VBv3.5" to the language directive:
<#@ template language="C#v3.5"#>
<#@ Import Namespace="System.Linq" #>
<#@ Assembly Name="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" #>
Happy code generation...
Pablo
The first CTP of the SFT or Clarius Software Factory Toolkit was released yesterday.
It has a lot features that will help you to author a guidance package:
- VB Net support (Create a guidance package using VBNet)
- DSL integration (Integrate your recipes with DSLs)
- Eventing (Launch recipes based on VS events)
- Scripting (Use the script value provider to assign values to recipe arguments inline)
- Possibility to register a guidance package on the VS Exp Hive
- Fixes for GAX known issues:
- .gpstate and TFS issue fixed
- and much more...
Please download it now and tell us your story. You could win a free MSDN Team Suite subscription.
On the next posts I will cover in more detail some of these features.
Pablo
As part of the DSL and GAX posts regarding integration, I would like to recommend an article made by Victor and Mauro named Guidance Automation Toolkit and Domain-Specific Language Tools for Visual Studio 2005: Integration Scenarios.
It describes the process of integrating both technologies, I provided some tips of my previous posts: Integrating DSL RTM and GAX part one , Integrating DSL RTM and GAX part two , Integrating DSL RTM and GAX part three
A must read...
Pablo
On the part two, I talked about launching recipes from the DSL Designer.
Another posibility is to launch a recipe from the DSL Language Explorer. For that we need the command bar GUID and ID.
The good news is that the commandbar GUID is the same that the one for the DSL Designer
The ID for the Language Explorer will always be 65537.
Pablo
Once we have the guidance package and the DSL package living on the same visual studio hive one thing that we will need for sure is the ability to launch recipes from the DSL designer.
For that we need to find out the command bar GUID of the DSL designer.
Steps:
- Open the DSL solution
- Open the Constants.cs file located under the DslPackage.csproj
- Look for:
internal static partial class Constants
{
// Menu identifier
public const string Language1CommandSetId = "a00ac613-8df8-4537-b7c7-89d31a44651c";
}
- Update the HostData section of the recipe manifest
<
HostData
>
<
CommandBar
Guid
="a00ac613-8df8-4537-b7c7-89d31a44651c"ID="65536"/>
</
HostData
>
Notice that the ID for the DSL RTM version will always be 65536.
Pablo
A guidance package and a DSL package are registered by default in different visual studio hives. A guidance package lives on the main hive and a DSL package lives on the Experimental one.
So the first thing that we need to do is migrate one of the packages so the live on the same hive.
I am going to migrate the DSL package to the main hive, the procedure is very simple:
A DSL solution consists of 2 projects, the Dsl.csproj and DslPackage.csproj and we need to edit both:
Dsl.csproj:
Change the line:
<PropertyGroup>
<StartProgram>$(DevEnvDir)\devenv.exe</StartProgram>
<StartAction>Program</StartAction>
<StartArguments>/rootsuffix Exp /DesignTimeRun "..\..\..\Debugging\Debugging.sln"</StartArguments>
</PropertyGroup>
To
<PropertyGroup>
<StartProgram>$(DevEnvDir)\devenv.exe</StartProgram>
<StartAction>Program</StartAction>
<StartArguments>/DesignTimeRun "..\..\..\Debugging\Debugging.sln"</StartArguments>
</PropertyGroup>
DslPackage.csproj:
Change the line:
<PropertyGroup>
<StartProgram>$(DevEnvDir)\devenv.exe</StartProgram>
<StartAction>Program</StartAction>
<StartArguments>/rootsuffix Exp /DesignTimeRun "..\..\..\Debugging\Debugging.sln"</StartArguments>
</PropertyGroup>
To
<PropertyGroup>
<StartProgram>$(DevEnvDir)\devenv.exe</StartProgram>
<StartAction>Program</StartAction>
<StartArguments>/DesignTimeRun "..\..\..\Debugging\Debugging.sln"</StartArguments>
</PropertyGroup>
And
<TargetRegistryRoot>Software\Microsoft\VisualStudio\8.0Exp</TargetRegistryRoot>
To
<TargetRegistryRoot>Software\Microsoft\VisualStudio\8.0</TargetRegistryRoot>
After this changes the guidance package and the DSL will live in the same visual studio hive, which means that one is going to be "visible" by the other and viceversa.
Pablo
To execute a recipe you need to find out which is the GUID for the DSL. This GUID is different for every DSL project that we create.
So, you need to open the GeneratedCmd.cs class file located at \Shell on the Designer project and search for the following constant:
public const string guidDSLNAMEMenuString = "GUID";
For example if we have a DSL named DSLFoo, then we need to search for the constant named guidDSLFooMenuString.
Once we have the GUID, we need the ID. For the DSL menu it will always be 256.
If we supose that we have a guidDSLFooMenuString constant with a value of “d309f791-903f-11d0-9efc-00a0c911004f“, then the CommandBar will be:
<CommandBar Guid="d309f791-903f-11d0-9efc-00a0c911004f" ID="256"/>
Pablo
Gaston Milano developed an addin fron Visual Studio 2005, to support intellisense and syntax coloring for T4 Templates.
It works really great.
Pablo