Welcome to NetFxFactory Sign in | Join | Help

Papers

  • Magnify your diagram

    Default layout mechanisms offered by DSL Tools are not challenged by VS’s 2010 edition. It's not a reason to be disappointed but to acknowledge efforts produced into other areas such as extensibility. DSL Tools wisely called for help to offer alternative solutions that are far more than blinded workarounds. Indeed, the solution chosen is supporting WPF.

    DSL Tools knew benefit from the announced marriage between this technology and Visual Studio.

    This being said, to illustrate this choice, this post proposes to migrate the AirTraffic example published few months ago. This sample presented a mechanism to support WPF right from the 2008 version.

    And the result is really encouraging, and allows thinking that WPF support isn't reduced to form based design.

    Below are the steps to embed AirTraffic into VS:

    • Migrate your model by using the really cool dsl migration utility (I still wonder why this is not included right into VS conversion wizards)
    • Create a minimal WPF Designer dsl project
    • Leverage renewed copy and paste mechanism and simply copy the original project artifacts into the new WPF Designer DSL project
    • Copy and paste custom code and specific dsl parameters (Customize connection builder).

    screenshot1  screenshot2

    • Transform all templates
    • Focus on UI project and especially ViewControl class
      • Extract code from AirReader wpf application, migrate only graph control.
    • Light a candle or give slaps when you are in front of minor bugs.
      • Reference required assemblies for ui project into dsl package, too.
      • Bind the collection view on a list wrapping the LinkedElementCollection of Airports. Indeed, unfortunately the LinkedElementCollection isn't an ObservableCollection but implements the old school interface IBindingList. This unfortunately excludes most features exposed by CollectionViewSource such as Filter however collection binding from data templates works as expected.
    • Don't run the project yet unless you want to define a model from the xml editor.
    • Keep the faith by developing a tool window to enrich the model displayed by your superb graph.
    • Add a WPF UserControl to the Dsl Package project
    • Inherit from ModelExplorerToolWindow class.
        [CLSCompliant(false)]
        [GuidAttribute("F4DD2E3D-CCD3-47B0-B64F-0D52A69AC7FA")]
        public partial class AirTrafficSearchToolWindow : ModelExplorerToolWindow
        {
            #region ctors
    
            public AirTrafficSearchToolWindow(IServiceProvider serviceProvider)
                : base(serviceProvider)
            {
                InitializeComponent();
            }
    
            #endregion
    
            #region methods
    
            protected override void OnDocumentWindowChanged(ModelingDocView oldView, ModelingDocView newView)
            {
                base.OnDocumentWindowChanged(oldView, newView);
                var l_modelingDocData = this.DocData as ModelingDocData;
                if (l_modelingDocData == null)
                {
                    return;
                }
                AirTrafficBrowser.DataContext = l_modelingDocData.Store.ElementDirectory.FindElements(false)[0];
            }
    • Weep for joy in front of WPF support offered by Visual Studio
      • Update the xaml associated to your new control by replacing the root element with ModelExplorerToolWindow
    <dslfx:ModelExplorerToolWindow 
        x:Class="Mexedge.AirTraffic.AirTrafficSearchToolWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:dslfx="clr-namespace:Microsoft.VisualStudio.Modeling.Shell;assembly=Microsoft.VisualStudio.Modeling.Sdk.Shell.10.0"
        xmlns:atp="clr-namespace:Microsoft.Samples.KMoore.WPFSamples.AnimatingTilePanel;assembly=J832.Wpf.BagOTricksLib"
        xmlns:dd="clr-namespace:Bea.Wpf.DragAndDrop;assembly=Bea.Wpf.DragAndDrop"
        xmlns:dsl="clr-namespace:Mexedge.AirTraffic;assembly=Mexedge.AirTraffic.Dsl"
        xmlns:local="clr-namespace:Mexedge.AirTraffic">
        <StackPanel Grid.Column="0" Margin="5" x:Name="AirTrafficBrowser" Background="#FF000635">
            <StackPanel.Resources>
    • Extract the code dedicated to the left panel of the AirReader application.
    • Invest a minimum on drag and drop mechanism and congratulate Bea Stollnitz work because the investment is really minimal thanks to her very detailed post.
      • You can safely stop bubbling event after custom handling.
    • Declare your user control as a standard tool window from Package class.
        [ProvideToolWindow(typeof(AirTrafficSearchToolWindow))]
        [ProvideToolWindowVisibility(typeof(AirTrafficSearchToolWindow), Constants.AirTrafficEditorFactoryId)]
        internal sealed partial class AirTrafficPackage
        {
            protected override void Initialize()
            {
                base.Initialize();
                this.AddToolWindow(typeof(AirTrafficSearchToolWindow));

    screenshot3

    Definitively, it would be a shame to reduce WPF use to form based design. And, the time of old school model explorer is running out.

    airtraffic2010

    Of course, a screencast showing the end result is available here.

    The code will be available soon once updated to Visual Studio 2010 RC.

  • Provide an improved user experience to your DSL's

    Introduction

    Oslo, charming Norwegian city is not only renowned for the cuteness of its female population. One of the most anticipated project from Microsoft Connected System Division is a source of hope but also raises a high level of expectations. The wait is growing and it fosters impatience, not only because it is not Microsoft's first shot in dsl area as it has been publishing a DSL Toolkit for a few years through Visual Studio.

    Several press releases help to position these projects or products, far from requesting a single product, it is certain that, from a user perspective, it would be great to have a common module (Windows Modeling Foundation, System.Modeling...). The argument mostly used by Microsoft's representatives is that DSL Tools are limited to Visual Studio, this article demonstrates that this argument is mostly driven by marketing considerations.

    The goal of this article is to present a way to leverage WPF technology in order to improve your DSL's editors user experience.

    Because of its tight links with Visual Studio, DSL Toolkit did not surf the WinFX wave initiated a few years ago (WCF,WPF, WF). Visual Studio 2010 expected in the next few months should address this area. In the meantime, if you happen to see Oslo's Quadrant how can someone using DSL Tools not to be tempted to mope around? Quadrant has numerous pros, one of them being that it leverages WPF technology.

    A 2008 post presented a first shot at bringing DSL Tools and WPF together: http://altinoren.com/PermaLink,guid,643ec407-4fe1-4238-a24e-20ef8b134a99.aspx

    This post raised high hopes but also a fair amount of issues and limitations. It is time to present a minimal solution to address these.

    The background leading to bring DSL Tools and WPF emerged when facing limitations and obstacles around complex graphical layout which was an obstacle to its adoption by end users.

    The sample

    This article is based on a minimalist sample. It is composed of a single concept: Airport and a single relation: Flight (AirportReferencesDestinations).

    An airport can be linked to another airport, this relation being a Flight. A rule enforces each Flight to have a corresponding return flight.

    metamodel

    The Model

    The meta-model cannot be easier to draw. One only have to define a custom ConnectionBuilder in order to create the definition of a flight, the return flight as well as a rule managing these flights deletion. Suppressing a flight leads to suppressing automatically its corresponding return flight.

    Once these easy steps completed, one only have to trigger code generation, compilation and deployment in order to play with the model editor automatically created.

    In no time you end up with a diagram that becomes more convoluted than filling your tax records.

    Model

    The solution

    The basic idea is to have classes generated by DSL Tools (Mexedge.AirTraffic.Dsl) exposing WPF binding mechanism.

    Several solution can be foreseen. The obvious one is certainly to generate some code. Because all classes inherits from Microsoft.VisualStudio.Modeling.ModelElement they cannot therefore inherit System.Windows.DependencyObject. However, they can implement System.ComponentModel.INotifyPropertyChanged thus leveraging WPF binding thanks to forward compatibility of existing Windows Forms technology.

    But this solution is very intrusive, the DSL project will need to be modified and given the changes announced with Visual Studio 2010, the less intrusive you get, the smoother the transition to Visual Studio 2010 and its DSL Tools will be.

    This being said, the scope of solutions drastically reduced. However, WPF binding leverages several mechanisms to bind data. Indeed, don't you ever wondered how some classes which did not implement any interface or inherit and class can still be bound to WPF controls ? You wouldn't be the only one:

    http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/9365bb6a-b411-4967-9a03-ae2a810fb215

    Don't you think that it seems like a good path to follow? We will take advantage of the mechanisms surrounding System.ComponentModel.TypeDescriptionProvider class.

    http://msdn.microsoft.com/en-us/library/bb613546.aspx

    But far from wanting to resort to code generation and thus the use of the attribute System.ComponentModel.TypeDescriptionProviderAttribute, salvation will come from the method System.ComponentModel.TypeDescriptor.AddProvider.

    Indeed, thanks to this method, the required behavior will be added dynamically to our classes therefore preventing any constraint on their design.

    TypeDescriptionProvider

    When loading a model, each class will be given a TypeDescriptionProvider. To do so, the model's metadata dictionary provided through DSL Toolkit will be leveraged.

    Our TypeDescriptionProvider class named BindingTypeDescriptionProvider will be in charge of creating, if required, an ICustomTypeDescriptor for a given instance of a ModelElement.

    ICustomTypeDescriptor

    Users familiar with DSL Toolkit know the mechanisms around TypeDescriptionProvider. Indeed, they are the one allowing personalizing the property grid associated to a concept or a relation. We will use Microsoft.VisualStudio.Modeling.Design.ElementTypeDescriptor and Microsoft.VisualStudio.Modeling.Design.ElementPropertyDescriptor offered by DSL Toolkit in our quest to provide data binding. Our implementation of ICustomTypeDescriptor interface named BindingTypeDescriptor will inherit from Microsoft.VisualStudio.Modeling.Design.ElementTypeDescriptor class.

    Given this choice, we are structuring both implementation and development. We will divide those based on the nature of the property to bind; and thanks to the metadata put in place by DSL Toolkit will be able to identify the nature of any given property.

    PropertyDescriptor

    We need to provide 3 PropertyDescriptors, each one targeting a specific kind of property:

    • DomainPropertyPropertyDescriptor: simple types (string …) properties that are not linked to relations between concepts,
    • IsOneDomainRolePropertyDescriptor: properties addressing relations with multiplicity 1,
    • IsManyDomainRolePropertyDescripor: properties addressing relations with multiplicity n

    Whatever PropertyDescriptor, the postulate is to provide a bidirectional mechanism, OnValueChanged method implemented by them should be executed if the value of the property to which they are associated changes. To do so, we are going to leverage DSL Toolkit's supervision module. For instance, as soon as the value of a simple type property is changed, the relevant OnValueChanged method must be executed. For other properties, different events are available; it is up to us to subscribe to the relevant ones.

    Properties addressing relations with multiplicity n requires special attention. Indeed, they are materialized by a collection (Microsoft.VisualStudio.Modeling.LinkedElementCollection) that is nothing close to what WPF would expect. We therefore have to act as a mediator between LinkedElementCollection and ObservableCollection. The class named BindingLinkedElementCollection has been created to address this special need, leveraging events exposed by DSL Toolkit.

    Once these several classes developped, work is done.

    The realization

    A couple of videos illustrating the result of this development are available (Step by step, AirTraffic). The source code illustrating this article is freely available (AirTraffic). The Visual Studio solution composed of 3 modules:

    • AirReader: this is a WPF application that offers a new user experience to edit the AirTraffic model, this project leverages the Kevin's bag'o'tricks library weel known by WPF afficionados, and Bea Stollnitz wpf drag’n’drop solution.
    • Dsl: this is composed of 2 projects Dsl and DslPackage defining the AirTraffic metamodel,
    • Miami: this project Miami.Modelling.Sdk contains, among other things, the code presented in this paper.

    The Miami module also addresses the use of DSL Tools outside Visual Studio. To do so, it offers a Repository class allowing loading and saving a model but also activating databinidng.

    Thus, loading a model and bind it is reduced to the following lines of code:

    DslBundle l_dslBundle = new DslBundle(fileName, typeof(AirTrafficDomainModel));
    DslSettings l_dslSettings = new DslSettings() { IsBindable = true };
    m_repository = new Repository(l_dslBundle, l_dslSettings);
    Traffic l_traffic = (Traffic)m_repository.Load();
    this.DataContext = l_traffic;

    Saving a model is even easier:

    m_repository.Save();

    This module also offers other functionalities.

    Editor

    What's next?

    If in the name of experimentation, this article shed some light on using DSL Toolkit outside Visual Studio, developing a WPF based model editor integrating this technology is envisioned. This is the direction that Visual Studio 2010 is taking. It should be the subject of a next article, unless the next subject becomes the personalization of the Store class inside Visual Studio. Indeed, several mechanisms allow taking control of it in order to open up the model to modifications triggered by external events (a real time collaboration scenario for instance).

    We could also deal with another Oslo's technical pillar that makes DSL Toolkit users jealous: the database. The various events exposed by DSL Toolkit allows saving modifications into a database instead of or in conjunction (for logging) to the file system offering numerous advantages (replay, collaboration etc…).

    Special thanks

    Nicolas Farcet (Thales), Jean Marc Prieur (Microsoft), Alain Metge (Sogeti), François Merand (Microsoft)


    Download the complete solution here.

    A step by step screencast showing how to enable WPF Data Binding is available here.

    A screencast showing a scenario taking advantage of the functionalities presented in this paper is available here.

    The pdf version of this article is available here.

  • Multiply Dsl points of view

    Introduction

    DSL Tools are becoming more popular everyday and naturally questions are increasing in volume on MSDN forums dedicated to Visual Studio Extensibility. However, this fact is not the only reason to advocate that DSL Tools gained in maturity. It is better for now to focus on a recurring request in the form of multiple views of a single diagram. This is mainly derived from the fact that models implemented using DSL Tools are getting bigger and bigger by the day and it becomes quickly important to be able to occult some aspects of the model while putting the emphasis on others. The goal of this article is to present a solution to implement such functionality.

    Screenshot : Multiply dsl points of view

    Alan Cameron Wills told us

    As soon as 2005, Alan Cameron Wills suggested a way to put in place this mechanism as an answer to a question on the above mentioned forum. However, while the answer clearly paved the way of the solution presented here, he omitted a few steps when described the procedure. In conjunction with the sparse level of documentation available around this area, this probably explains why no one came up with a solution yet.

    With this starting point, a few alternative ways have been pursued. One based on the creation of a DocData instance for each diagram that needs to be displayed has been successful… until one of Alan's missing steps reminds you that this might be more complex than originally anticipated. The revelator has been the discovery of the ProvideEditorLogicalView attribute that can decorates the Package class. This new attribute tends to think that Alan's suggestion might be incomplete and only regret the low level of documentation around this area. Using this attribute activates 2 mechanisms: OpenView (method defined in DocData base class), MapLogicalView (method owned by EditorFactory).

    As we will explain in this article, the efforts required to enable multiple diagrams are not limited to only overriding these two methods. Some actions will necessary on both DSL Tools and Visual Studio sides. This implies therefore working on two projects: Dsl and DslPackage.

    A lot of choices will be made during the implementation of multiple diagrams and will be explained during the course of this article. The readers are invited to submit their though and opinions on these choices.

    DslPackage

    Developments made in this project are focused mostly around 3 classes. Not surprisingly, it is about customizing classes related to document management within Visual Studio: EditorFactory, DocView and DocData. The mechanisms involved by DSL Tools make a large use of these and even generate an implementation of them.

    EditorFactory

    The work required on this class is only overriding 2 methods:

    • The first one was mentioned earlier and is about taking advantage of the MapLogicalView method in order to support multiple diagrams on a single model.
    • The second one is about using the previous override (MapLogicalView) in order to indicate to the class representing a document view which diagram is associated with it. By default, none is defined.

    MapLogicalView

    A first question arises: « Should a logical view be associated to each diagram type that will be defined on top of the default diagram generated by the DSL Tools?”.

    The short answer is no. This is not a unique identifier that will associate a logical view with the diagram to display, this is the context associated to the display request. A single identifier thus a single logical view is used in our case.

    Therefore the mechanism relies on the second parameter of the MapLogicalView method. This parameter is essential in order to find which diagram to display. Indeed, the purpose of this method is to return a string that identifies a logical view specified via an id and a context. In our sample, we return the context as a string.

    Even if the solution proposed relies on a single view identifier, each diagram instance will have a dedicated document view instance as described in the next paragraph.

    if (logicalView == Constants.LogicalView1)
    {
       return (string)viewContext;
    }
    

    CreateDocView

    The purpose of this method is to create a document view based on several parameters, one of them being provided by the call to the MapLogicalView method.

    It also allows specifying the text that will be displayed in the tab title that will host the view and therefore the diagram.

    The goal is to specialize a view based on the diagram to display, this method now returns an instance of the DocView class taking into account the result of the MapLogicalView method. The DocView class must therefore expose a new constructor handling this new parameter.

    editorCaption = string.Format(" [{0}]", physicalView);
    return new ExtendedClassDiagramDocView(docData, this.ServiceProvider, physicalView);
    

    DocView

    The work required in this area is limited to 2 actions :

    • Develop a new constructor in order to handle the parameter returned by the MapLogicalView method,
    • Override the LoadView method in order to use this new parameter

    The first action does not require any further explanations and it is almost the case for the second. Indeed, in the context of the example provided, this is the name of the diagram that is returned by the MapLogicalView method that is being used as the identifier of the physical view. This being said, overriding the LoadView method is only about not using the first item in the list of diagrams loaded by DSL Tools but the one with the same name as the identifier.

    l_diagram = string.IsNullOrEmpty(m_physicalView) ? l_diagramList[0] : l_diagramList.Find(delegate(Diagram diagram) 
    { return diagram.Name == m_physicalView; }); 
    if (l_diagram != null)
    {
        this.Diagram = l_diagram;
        return true;
    }
    

    This being said, a new question arises : Can a user create diagrams if the model has already been loaded?

    The sample provided allow such a functionality. In order to allow this, when a new diagram is created it is synchronized with the model. Therefore if this new diagram displays elements of the model created by its own creation, the graphics associated to the elements will be created when the diagram is created or loaded. This synchronization mechanism relies on the definition of a rule with the Dsl project actived when creating a diagram element.

    ([RuleOn(typeof(Diagram), FireTime = TimeToFire.TopLevelCommit)]).
    

    The details of this rule will be presented later on when dealing specifically with the Dsl project.

    DocData

    Today, DSL Tools allows saving model and its associated diagram in dedicated files. Supporting multiple diagrams supports the same level separation for each diagram. Therefore, a file is created for each new diagram. However, it is good to maintain the same user experience within Visual Studio. A visual link should help identifying the file in which the model is saved and the files where the diagrams are saved. Offering the same user experience can lead to navigation issues if the number of diagrams thus the number of associated files reach high numbers. Furthermore, opening a “diagram” file, as opposed to a “model” file, does not trigger a dedicated visual designer. Because the diagrams are saved using XML, Visual Studio uses its default XML editor. It is therefore not relevant to allow opening this kind of files via a double click in the solution explorer. It is also important to maintain features that are now mainstream such as “Save as” or “Include”.

    To answer each of these requirements, it has been decided to leverage the OpenXML format and specially its dedicated library that is now offered in the .Net Framework through the System.IO.Packaging namespace. All the files related to the diagrams will therefore be saved inside a single file. The later on top of saving the diagrams on disk will also compress these. As demonstrated in the sample accompanying this article, the users will only face, even though we introduce multiple diagrams, only two files one with a “diagramx” extension similar to the new file extension convention used within Microsoft Office products.

    This being said DocData class does not have to handle the serialization mechanism involved in the DSL Tools. It delegate this work to the SerializationHelper class that will be details later on.

    The modifications required on the DocData class on top of overloading the OpenView class are twofold:

    • Adapt the mechanism managing the.diagram files in order to handle .diagramx. It is nothing more than copying one mechanism and apply it to the second,
    • Call within the load and save methods the serialization mechanisms supporting multiple diagrams as described earlier.

    The actions impact very sparingly the default behavior of the DocData class therefore readers are invited to refer to the source code provided in the sample should they require further details.

    OpenView

    As mentioned earlier, diagrams can be instantiated on the fly even if the model has been modified. Therefore, this is when the OpenView method is called that a check is done in order to identify which type of diagram is to be displayed. If the diagram in question does not exist yet, an instance will be created within this method. To do so, only the parameter specifying the logical view context is taken into account. This parameter is used to determine which type of diagram is to be instantiated.

    Of course, a next release of the solution exposed here could include a more elaborated mechanism to link a type of diagram to string (dedicated attribute decorating the class associated to a diagram etc…) and not only rely on the name property of the diagram. This would allow adding new parameters to the diagram instantiation.

    It is important to note that this should happen within a DSL Tools transaction because this new diagram instance has to be inserted into the DSL Tools store.

    Following this instantiation, the OpenView method exposed by the base class is called therefore leveraging the Visual Studio mechanisms orchestrating the call to the methods mentioned earlier (MapLogicalView etc…).

    The most curious readers will notice that the DocData class provided in the DSL Tools API: ModelingDocData specifies its own implementation of the OpenView method. Because the DocData class generated by the DSL Tools inherits from it, it leverage this implementation by default. Using this one would constraint us to use the diagram instance to be displayed as the visualization context. This is an option that be used later on. We have every reasons to think that the DSL Tools development team wanted to offer multiple diagrams support. Digging into one of the first betas available confirms this though.

    public override void OpenView(Guid logicalView, object viewContext)
    {
        string l_viewContext = (string)viewContext;
        Diagram l_diagram = this.Store.ElementDirectory.FindElements<Diagram>().SingleOrDefault<Diagram>(diagram => diagram.Name == l_viewContext);
        if (l_diagram == null)
        {
            using (Transaction transaction = this.Store.TransactionManager.BeginTransaction("DocData.OpenView", true))
            {
                if (l_viewContext == "Flow")
                {
                    l_diagram = new FlowDiagram(this.Store, new PropertyAssignment(Diagram.NameDomainPropertyId, l_viewContext));
    
                }
                else if (l_viewContext == "Package")
                {
                    l_diagram = new PackageDiagram(this.Store, new PropertyAssignment(Diagram.NameDomainPropertyId, l_viewContext));
                }
                else
                {
                    throw new NotImplementedException();
                }
                l_diagram.ModelElement = this.RootElement;
                transaction.Commit();
            }
        }
        base.OpenView(logicalView, viewContext);
    }
    

    ProjectItem Templates

    The previous paragraph introduced it, a new file with diagramx extension is to replace the default .diagram file in order to enable multiple diagram. This involves modifying the item project models produced by the Dsl solution in order to replace .diagram in diagramx.

    The files to modify are xxx.diagram, CSharp.tt and VisualBasic.tt.

    Package

    The work done on the DslPackage project will be complete once the two actions on the Package class will be done:

    • Decorate the Package class with the LogicalView attribute as described in the introduction, this enables multiple views of the same model.
    • Decorate the Package class with the RelatedFiles attribute in order to support the new .diagramx extension.
    [ProvideEditorLogicalViewAttribute(typeof(ExtendedClassDiagramEditorFactory), Constants.LogicalView1Value, IsTrusted = true)]
    [ProvideRelatedFile("." + Constants.DesignerFileExtension, Constants.DiagramxExtension,
     ProjectSystem = ProvideRelatedFileAttribute.CSharpProjectGuid,
     FileOptions = RelatedFileType.FileName)]
    [ProvideRelatedFile("." + Constants.DesignerFileExtension, Constants.DiagramxExtension,
     ProjectSystem = ProvideRelatedFileAttribute.VisualBasicProjectGuid,
     FileOptions = RelatedFileType.FileName)]
    internal sealed partial class ExtendedClassDiagramPackage
    {
    }
    

    Dsl

    The actions undertaken on the DslPackage paved the way to supporting multiple views of a single model within Visual Studio. As mentioned, these actions are not enough and require modifying the Dsl project as well. The first one is to extend the serialization mechanisms in order to take into account multiple diagrams and OpenXml to save them on disk. These mechanisms are leveraged within the DocData class and exposed by the serialization helper class.

    SerialisationHelper

    The modifications to this class won’t involve overrides but introduce new methods: LoadModelAndDiagrams, SaveModelAndDiagrams and SaveDiagrams.

    Readers would certainly notice that numerous developments mentioned since the beginning of this article could have been generated. This will be one of the remarks mentioned in the conclusion of this article.

    LoadModelAndDiagrams

    This new method simply uses the default logic generated by the DSL Tools and only clearly separates what is related to loading the model and what is related to loading a diagram (which has its own dedicated method). The first advantage of this is to easily call it for each diagram.

    The last issue to resolve is that all the files associated with the multiple diagrams are compressed into a single file. This is a no brainer when leveraging the System.IO.Packaging namespace offered in WindowsBase.dll as described below.

    // Load the model
    ModelElement l_rootElement = this.LoadModel(serializationResult, store, modelFileName, schemaResolver, validationController);
    
    // Use OpenXml Api
    using (Package pkgOutputDoc = Package.Open(diagramxFileName, FileMode.Open, FileAccess.Read))
    {
        foreach (var packagePart in pkgOutputDoc.GetParts())
        {
        this.LoadDiagram(serializationResult, store, l_rootElement, packagePart.GetStream(FileMode.Open, FileAccess.Read), schemaResolver, validationController);
        }
    }
    

    As you probably have noticed, it is required to create our own SerializationvalidationObserver class in order to follow the way the method generated by DSL Tools works. Indeed, the generated implementation for this class is set to private thus preventing us from reusing it.

    SaveModelAndDiagrams

    This paragraph starting with a similar remark than the previous one : it is a shame that the InternalSaveModel and InternalSaveDiagram are set to private. This forced us to duplicate them in order to be able use the functionalities they provide in the SaveModelAndDiagram.

    There is no fancy work done in this method, the only highlight would be the use once again of the System.IO.Packaging namespace in order to produce only file for all the diagrams.

    using (Package pkgOutputDoc = Package.Open(diagramxFileName, FileMode.Create, FileAccess.ReadWrite))
    {
        foreach (var memoryStream in l_memoryStreamDictionary.Keys)
        {
            byte[] l_bytes = memoryStream.ToArray();
            Uri uri = new Uri(string.Format("/diagrams/{0}", l_memoryStreamDictionary[memoryStream]), UriKind.Relative);
            PackagePart part = pkgOutputDoc.CreatePart(uri, System.Net.Mime.MediaTypeNames.Text.Xml, CompressionOption.Maximum);
            using (var partStream = part.GetStream(FileMode.Create, FileAccess.Write))
            {
                partStream.Write(l_bytes, 0, l_bytes.Length);
            }
        }
    }
    

    Serializer

    So far, we did not mentioned how a new diagram is defined. Far from wanting to maintain an unbearable suspense, this paragraph partially lifts the veil. A new diagram will inherit from the default diagram generated by the DSL Tools. This choice implies a few pros:

    • It allows using the DSL Tools visual designer to define the graphical elements associated with the model, the user experience is therefore just slightly changed.
    • It allows using the default underlying mechanisms involved with the diagram management and specially its serialization.

    However this decision involves modifying the serializer associated to the default diagram (ClassDiagramSerializer in the sample code). Indeed, when reloading a diagram the correct diagram type must be reloaded.

    To do so, it has been decided to add to the Xml representation a new attribute named type. It indicates which type of diagram is to be instantiated. The management of this new attribute requires to overload the WritePropertiesAsAttributes method.

    Of course, other solutions could have been chosen such as associating a dedicated element name for each type of diagram, similarly to what is done usually in XAML.

    WritePropertiesAsAttributes

    To activate the call to the overloaded method, it is required to add at least one DomainProperty to the default diagram.

    Then, we only need to use to WriteAttributeString method exposed by the XmlWriter class (an instance of the writer is provided as a parameter of this method)

    protected override void WritePropertiesAsAttributes(SerializationContext serializationContext, ModelElement element, XmlWriter writer)
    {
        writer.WriteAttributeString("type", element.GetType().FullName);
        base.WritePropertiesAsAttributes(serializationContext, element, writer);
    }
    

    CreateInstance

    If a diagram’s Xml representation now indicates its type, we need to use this information when instantiating a diagram using its representation.

    To do so, it is required to override the CreateInstance method of the Serializer associated to the default diagram.

    protected override ModelElement CreateInstance(SerializationContext serializationContext, XmlReader reader, Partition partition)
    {
        String l_typeAttribute = reader.GetAttribute("type");
        Assembly l_assembly = this.GetType().Assembly;
        Type l_type = l_assembly.GetType(l_typeAttribute);
        ModelElement l_diagram = (ModelElement)Activator.CreateInstance(l_type, partition);
        return l_diagram;
    }

    FixUpDiagramOnElementAddedRule

    All the mechanisms allowing saving and loading a diagram have been created. As described in the previous chapter, it is also required to modify the synchronization mechanisms that provide an up to date view of the diagram event if the underlying model has been modified.

    By default, DSL Tools relies on a mechanism based on a generate rules: FixUpDiagram.

    As we have done in some other areas during this article, we have to modify this mechanism in order to support multiple diagrams. Unfortunately, this modification is not trivial, it is easier to deactivate the default rule and create new one to handle our needs.

    The modification mostly revolves around on a single method : GetParentForRelationship. For those who are fans of Reflector, the generated version of the GetParentRelationship method selects the first associated diagram. This cannot be sufficient when several diagrams need to be updated. This is why a new method has been developed that takes a diagram instance as an input parameter and searches its associated parent. ..

    GetParentForRelationship

    This method does not rely on any specificity related to the defined Dsl language. It could even be provided within the DSL Tools SDK. As mentioned, it must take into account, when searching for a parent element, the instance of the diagram provided as a parameter.

    if (shape != null && shape.Diagram == diagram)
    {
        sourceShape = shape;
        break;
    }
    

    It could also not return any parent, indeed a diagram can have no element or relation to display.

    if (sourceShape == null || targetShape == null)
    {
       return null;
    }
    

    ElementAdded

    The modification to operate here is to provide a diagram instance to the GetParentForRelationship method. This will be done for every diagrams in the In Memory Store (IMS).

    if (childElement is ElementLink)
    {
        ReadOnlyCollection<Diagram> diagrams = childElement.Store.ElementDirectory.FindElements<Diagram>();
        for (int i = 0; i < diagrams.Count; i++)
        {
            parentElement = GetParentForRelationship(diagrams[i], (ElementLink)childElement);
            if (parentElement != null)
            {
                DslDiagrams::Diagram.FixUpDiagram(parentElement, childElement);
            }
        }
    }
    

    Fixup

    This method uses the same logic as the ElementAdded method but only on the diagram instance provided in parameter. This static method will be used by the rule triggered when a new diagram is added. This rule aims to synchronizing the diagram with the model.

    The other methods exposed by this class are exact copies of those provided by the FixupDiagram class generated by the DSL Tools. This requirement highlights the fact that a set of T4 templates could greatly ease the process of supporting multiple diagrams .

    DiagramAddRule

    The sample provided with this article allows creating a new diagram after loading the model. This functionality requires the activation of several mechanisms among which the synchronization of the diagram with the model.

    This synchronisation is based on the FixUp rule presented earlier. However, it is not about browsing all the model elements and apply them the FixUp rule with the newly created diagram instance. It has to be done based on the nature of the elements and on their graphical representation within the diagram. In the sample provided, the elements representing an association DomainRelationship are dealt with only after all the others.

    [RuleOn(typeof(Diagram), FireTime = TimeToFire.TopLevelCommit)]
    internal sealed class DiagramAddRule : AddRule
    {
        public override void ElementAdded(ElementAddedEventArgs e)
        { 
            for (int i = 0; i < l_modelElementList.Count; i++)
            {
                ModelElement item = l_modelElementList[i];
                if (item.GetDomainClass().DomainModel.Id == l_domainModelInfo.Id && item.Id != l_diagram.ModelElement.Id && !(item is ModelRootHasTypes))
                {
                    FixUpDiagramOnElementAddedRule.Fixup(l_diagram, item);
                }
            }
            for (int i = 0; i < l_elementLinkList.Count; i++)
            {
                ModelElement item = l_elementLinkList[i];
                if (item.GetDomainClass().DomainModel.Id == l_domainModelInfo.Id && item.Id != l_diagram.ModelElement.Id && !(item is ModelRootHasTypes))
                {
                    FixUpDiagramOnElementAddedRule.Fixup(l_diagram, item);
                }
            }
        }
    }
    

     

    Defining a new diagram

    All the plumbing required to support multiple diagrams are now completed.

    It is time to present the steps required to define a new diagram on top of the default one. The steps listed below are based on the sample provided.

    1. Open Solution Explorer
    2. Right-click on the CustomCode folder of the Dsl project, Add Class
    3. Enter the name of your diagram.
    4. Modify the namespace if required.
    5. Inherits from the ClassDiagram class.
    6. To declare your new class to DSL Tools,
      1. Decorate your class with DomainObjectId attribute by specifying only a new guid.
        [DomainObjectId("xx-xx-xx-xx-xx")]
      2. Add your class to the custom type collection returned by GetCustomDomainModelTypes method of your DomainModel class.
        public partial class ExtendedClassDiagramDomainModel
        {
            protected override Type[] GetCustomDomainModelTypes()
            {
                return new Type[]
                {
                    typeof(xxx),
                };
    7. Override the ShouldAddShapeForElement method to filter the model elements displayed by this diagram.
    8. Customize your diagram by following DSL Tools guidelines. Use the DslDefinition editor to define shapes that will be used in your new diagram.

    Conclusion

    The sample provided here is like a wink to Visual Studio 2010. Similarly to VS10, it comes to the rescue of the lonesome class designer which was one of Visual Studio 2005’s highlights.

    That being said, the sample does not generate any code. This is not the only discriminator, if Visual Studio 2010 has also used the DSL Tools, they are not based on a single diagram but leverages the Bus Designer known as Codename Backplane that will ship with the next version of Visual Studio. Even if it can be an answer to the issue being addressed in this article, a lot of questions arises regarding the rules definition, the usage of overriding mechanisms as well as the developer user experience. It seems that most of the underlying mechanisms are already provided in the DSL Tools to support multiple diagrams on a single model. Will users have both, complementary, solutions provided out of the box by Microsoft at some point?

    What’s next

    The solution presented here is part of the Panoptes project. Because this project does not require collaborative tools it has not been ported to Codeplex. All the code developed within this project is included in this article. In addition to what has been detailed in the previous chapters, a filtering mechanism on the toolbox has also been implemented.

    The next step is the development of a set of T4 templates in order to improve the user experience. As indicated before, all the code produced here could be generated. Once these templates created, adding a new Domain Specific language Designer project template leveraging the revised T4 templates.

    By this time, maybe such a feature will be provided out of the box by the DSL Tools. Chances are that such a feature would lead to an extended Dsl Definition Designer supporting the definition of multiple diagrams (by providing dedicated swimlanes?).

    In parallel to these «industrialization » improvements, new functionalities could also be developed such as filtering the property grid based on the diagram displayed or activating drag and drop between diagrams.

    If this article ignites some vocations in contributing to this project, Codeplex can be a place of choice to extend on this.


    Download the complete solution here.

    A screencast showing a scenario taking advantage of the functionalities presented in this paper is available here.

    The pdf version of this article is available here.

  • Let your community site leverage Windows Live

    1. Why

    Most web sites require managing users from personal details to authentication. Event though, ASPNet provides some tools ease this process with a Sql Server backend database. This can be tedious to manage and implies a responsibility to keep personal data safe furthermore people put some time and effort into filling in their Windows Live Id profile information. Of course some web sites make their business on having a huge user database. However community web sites such as netfxfactory does not gain any added value from managing its own user database. Various blogs and papers on the web explain how to leverage Windows live authentication mechanism to validate a local user (see here here and here). They mostly let someone associate its web site account with his Windows Live Id however it’s name, dob, email etc… are still managed localy by the web site (this is the functionalities offered by the Memebership provider offered with the Windows Live SDK). Only the authentication mechanism is leveraged here.

    The goal of this article is to go one step further and externalize as much as possible the user management. Managing locally user’s data requires an extra effort from the user to keep his information up to date. We will demonstrate a solution using Windows Live based on a custom aspnet membership provider used to pull out information from Windows Live such as User info but also his contacts info.

    This is a first step towards S+S as promoted by Microsoft.

    2. Solution

    a. Register live application

    In order to be able to take advantage of Windows Live API one needs to register his application. This registration process requires several pieces of information such as a return Url that will be called after a successful sign in along with a set of parameters allowing to complete the authentication process on our site. The form bellow shows the information to supply:

    WindowsLiveApplicationID

    Note: In a dev environment, you can add a static DNS resolution on your dev machine in order to resolve the domain name to 127.0.0.1 Edit your host file (c:\windows\system32\drivers\etc\host)
    Note: It seems that Windows Live does not let you register domain name that does not contains “dots” in it.

    Upon completion of this form, you are being attributed an ApplicationID that your application have to provide along with the secret key.

    b. ASP.Net related

    Acknowledgement

    The membership provider mechanism offered by ASPNet offers a large functional perimeter allowing managing for instance password expiration, reset etc… Because we intend to rely as much as possible on Windows  Live there is no need to implement such functionality on our web site.

    Custom Windows Live Membership provider

    Based on the acknowledgement detailed above, the main method of AspNet Membership provider that we are interested in is ValidateUser but instead of validating a username password as we usually do, we will only have to ensure that the current user has signed in properly on Windows Live (therefore owns a valid authentication and delegation token).

    Because Windows Live authentication only returns the id of the logged in user and extra step is required to retrieve personal information about the user. This step is done in the overridden GetUser() method. A REST request is sent to retrieve information details about the user such as his/her name, email(s) and so on. this can then easily accessed throughout the site using Membership.GetUser() method.

    Custom Windows Live Membership user

    In order to facilitate the use of MembershipUser, we provide a specialized one that exposes the user’s Windows live information. Here the choice has been made to not include the complete address book but just the user’s details. A quick look at the Windows Live Api Rest Provider described bellow would do the trick.

    Custom Windows Live Contact Api Rest Provider

    This class is responsible for exposing methods that wraps calls made to the Windows Live services exposed as REST. Its main task is to build the requests to send from Windows Live Services (url constructs, token and authentication assignment as well as deserialization)

    c. Windows live

    Delegated authentication

    Windows Live delegated authentication is mechanism allowing taking advantage of Windows Live ID authentication to prove to Windows Live services (Contact, Photos, Application Storage and so on) that a trusted relationship exists between the user and a your web site. it suits perfectly the bill of our requirements, i.e. handle locally user’s data as less as possible as stated here: “Sign-in and consent management functions are performed by the Windows Live ID service, so you don't have to worry about implementing these details. Another advantage to Delegated Authentication is that Windows Live ID profile data is not shared with your Web site. As a result, you can focus more on the functionality of your application, and less on managing user data”.

    The corner stone of delegation authentication is that users own their own data. They can decide which sites get access to that data and what those sites can do on the user's behalf with that data.

    Consent management

    Windows Live let users decide what information they want to share with a web site leveraging Windows Live authentication. Anyone with a Windows Live account can manage his consents at the following address: https://consent.live.com. This page sums up all the sites you have given access to as well as the access mode (read, update, write …). You can revoke access of any of these sites at any time before your consent expires.

    Here is what the consent management page looks like:

    WindowsLive_Consent

    The Windows Live Contact consent can even be done on a per contact basis. Unfortunately, there is no way to select/unselect  a group of contact indeed it can become tedious to select/unselect individual contacts when you address book contains 30+ contacts.

    WindowsLive_AllowConsent

    d. Implementation

    1. Windows Live protocol

    Most of the protocol related code is taken from the Windows Live API samples. I won’t  drill into too much details here, have a look at the classes LiveLogin, LiveConsentToken and LiveAuthenticatedUser in the sample code provided with this paper.

    2. Windows Live delegated authentication process

    wlid del auth

    The delegated authentication process is composed of the following steps (these include the local authentication steps as well):

    1. A user requests the web site’s home page (default.aspx). Because he is not yet authenticated and forms authentication has been setup on the site he is redirected to the login page (login.aspx),
    2. The login page offers, through an iFrame pointing to Windows Live login page, a sign in link,
    3. Once the user clicks on it, he is rediricted to the classic Window Live login page,
    4. Windows Live then calls the web site that initiated the authentication (wlid.axd) with a signin action,
    5. The handler checks if the request already contains a valid Windows Live consent token. If the consent token is valid then go to step 7. If no consent token is available then a new one is requested through the Windows Live consent form,
    6. Once the user has granted the required delegation rights to the site, Windows live then calls back the handler  (wlid.axd) with a delegated authentication(delauth) action.
    7. The user is then validated against the membership provider, signed in on the web site and finally redirected to the home page.

    Steps 7 and  8 on the schema above  represents the web site requesting user’s details such as full name, emails, location etc… as the only information provided to the site by the windows live login is an ID that uniquely identifies the user as well as a token.

    Windows Live callback handler

    This callback handler is used to handle Windows Live callback upon successful authentication and consent agreement. The  address that exposes this HttpHandler is the one specified in the Windows Live Application registration form known as ReturnUrl. It is possible to add extra parameters (referred as a context) to the initial request made for authentication in order to supply additional information to the callback. For instance, one can supply a url that must be called at the end of authentication and consent agreement phase.

    The callback handler needs to be able to address 4 types of action on the callback made by Windows Live:

    • signin,
    • signout,
    • clear cookie,
    • delegated authentication

    This handler is an important piece of the process as it handles requests made by  Windows Live and act on the local web site accordingly. Its action is mostly based on redirections and local authentication.

    Windows Live Asp net membership provider

    This custom membership provider is quite different from the one that are usually presented. Because our goal is to externalize as much as possible the user management there are quite a few methods that usually requires a specific implementation but that are not relevant in our case. For exemple, ChangePassword, ChangePasswordQuestionAndAnswer, GetPassword and so on.

    Here are the methods and properties that needs to be implemented in our WindowsLiveMembershipProvider:

    Methods:

    ValidateUser,
    GetUser

    Properties:

    ApplicationName,
    EnablePasswordReset,
    EnablePasswordRetrieval,
    RequiresQuestionAndAnswer,
    RequiresUniqueEmail

    The most interesting parts of this membership provider are obviously the ValidateUser and GetUser methods. The first one ensures that the current user own a valid Windows Live token and the last returns a custom membership user that contains all the details about the user such as his name, his email address(es), location etc…

    Here is the implementation of the ValidateUser method:

    public override bool ValidateUser(string username, string password)
    {
        System.Diagnostics.Debug.WriteLine("ValidateUser");
    
        HttpCookie __consentCookie = HttpContext.Current.Request.Cookies[WindowsLiveConstants.COOKIE_CONSENT];
    
        if (__consentCookie != null)
        {
            LiveConsentToken __token = LiveMembershipProvider.LiveLogin.ProcessConsentToken(__consentCookie.Value);
            return __token.IsTokenValid;
        }
        return false;
    }
    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

    and the GetUser method:

    public override MembershipUser GetUser(string username, bool userIsOnline)
    {
        MembershipUser __user = null;
        long __lid;
        string __delegationToken;
        if (IsConsentTokenValid(out __lid, out __delegationToken))
        {
            //get the user details from Windows live contact api
    //to improve responsiveness user details can be tested on the current user to see if they have already been retrieved
    LiveContactsRestProvider __liveContactsRestProvider = new LiveContactsRestProvider(__lid, __delegationToken); Owner __owner = __liveContactsRestProvider.GetOwnerDetails(); __user = LiveMembershipUser.CreateLiveMembershipUser(__owner, this.Name); } return __user; }
    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

    As described earlier, because the default membership user only contains a user id, it is required to create a specialized membership user.

    Here is the details of its implementation:

        public sealed class LiveMembershipUser
            :MembershipUser
        {
            private Owner _owner;
    
            private LiveMembershipUser(Owner owner, string providerName, string name, object providerUserKey, string email, string passwordQuestion, string comment, bool isApproved, bool isLockedOout, DateTime creationDate, DateTime lastLoginDate, DateTime lastActivityDate, DateTime lastPasswordChangedDate, DateTime lastLockoutDate)
                :base(providerName, name, providerUserKey, email, passwordQuestion, comment, isApproved, isLockedOout, creationDate, lastLoginDate, lastActivityDate, lastPasswordChangedDate, lastLockoutDate)
            {
                _owner = owner;
            }
    
            public Owner Owner
            {
                get { return _owner; }
                set { _owner = value; }
            }
    
            public static LiveMembershipUser CreateLiveMembershipUser(Owner owner, string providerName)
            { 
                string __userName = String.Format("{0} {1}", owner.Profiles.Personal.FirstName, owner.Profiles.Personal.LastName);
    
                //Look up the default address if none pickup the first email available
                List __emails = owner.Emails.Where(email => email.IsDefault == true).ToList();
                string __email = (__emails.Count == 1) ? __emails[0].Address : owner.Emails[0].Address;
    
                LiveMembershipUser __user = new LiveMembershipUser(
                                                    owner,                  //owner detais
                                                    providerName,           //membership provider name
                                                    __userName,             //user's name
                                                    owner.WindowsLiveID,    //providerUserkey
                                                    __email,                //email
                                                    string.Empty,           //password question
                                                    string.Empty,           //comment
                                                    true,                   //is approved
                                                    false,                  //isLockedOut
                                                    DateTime.MinValue,      //creation date
                                                    DateTime.Now,           //last login date
                                                    owner.LastChanged,      //last activity date
                                                    DateTime.Now,           //last password change date
                                                    DateTime.MinValue);     //last lock out date
                
                return __user;
            }
        }
    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

    3. Windows Live Contact data (lack of) API

    Microsoft does not provide (yet) a .Net object model representing the Windows Live Contact data, the only thing close is the schema of the data returned by the REST service. So I had to build up my own. Here is the class diagram representing the Windows Live Contact object model as I represented it:

    LiveContacts

    4. Windows Live Contact Rest provider

    This last piece is nothing more than a helper allowing to query the Windows Live Contact services exposed as REST. It is in charge of building the request url, provide the authentication details , call the service and deserialize the xml returned into .Net objects described in the previous step.

    The authentication is provided by a specific header added to the request:

    __request.Headers.Add(WindowsLiveConstants.DELEGATION_HEADER_KEY, 
    String.Format(WindowsLiveConstants.DELEGATION_HEADER_VALUE_FORMAT, _delegationToken));
    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

    The header must conform to the following pattern:

    public const string DELEGATION_HEADER_VALUE_FORMAT = "DelegatedToken dt=\"{0}]\"";
    .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

    5. Use a user’s contact list

    Once the user is logged in on the site, we can now use Windows Live Contacts to retrieve his friends. In the sample provided with this article, the about.aspx page lists the user’s friends that have published on NetFxFactory.

    The sample uses rss feeds to get the posts from NetFxFactory and populate this sample site.

    Now, we can look up the user’s contacts in order to retrieve those that published something on NetfxFactory*. Then we easily provide a list of friends as well as a link to a page listing all the posts made by a particular contact.

    *Acknowledgement: Because NetFxFactory is powered by Community Server an helper has been done in order to map IDs from CS to Windows Live unique IDs.

    f. Sign in on Windows Live with Cardspace

    Windows Live allows you to associate a Carspace card to your Windows Live account in order to sign in via Cardspace instead of the usual email/password pair. This saves you from handling Cardspace on your site (specially the required SSL certificate used to authenticate the web site requesting the Card), simply delegate the work to Windows Live that will handle request and validation of the card.

    To associate a card to your Windows Live account, navigate here and follow the registration process. Once completed, you get the following options when signing in Windows Live:

    WindowsLive_Cardspace

    3. Samples

    An implementation of NetFxFactory meeting Windows Live is available at this address: http://live.netfxfactory.org. This sample is an ASPNET application that uses Windows Live delegated authentication, as demonstrated in this paper, to logon the site. Once logged in, the user is able to browse the site and display rss feeds from www.netfxfactory.org. The “My Friends” section displays all the user’s Windows Live Contacts in two groups: the user’s friends who posted an article to NetFxFactory and the friends who did not. In the group of contacts that wrote an article on NetFxfactory, there is a link to a dedicated page that displays all the articles posted by a particular contact. The source code is available here (note: you need to register an application as demonstrated above and update the web.config and login.aspx files accordingly. Check out readme.txt within the Zip archive).

    4. Going further

    As we have seen in this paper, it is pretty straight forward to take advantage of Windows Live ID to handle all most of the user management related to a community site. We voluntarily set aside the problem of storing custom data (for instance user’s role, preferences etc…) that can easily be externalized as well using Windows Live Application Base Storage (check http://dev.live.com/livedata/sdk/ for details and samples).

    a. Claims and Federation

    Taking advantage of Windows Live is great in a sense but wouldn’t  be nice to be able to raise the abstraction one level up and use claim based authentication on your web site? Using claims and Federation one would be able to target multiple identity providers such Windows Live, Facebook, Yahoo and so on, abstract their respective implementation details and only manage claim evidence on client side to deal with authentication, user management and so on. This will be the subject of a paper to come.

    b. Geneva (formerly known as Zermatt)

    Microsoft Code Name Geneva is Microsoft’s answer to federation and claim based identity. There is a thorough paper presenting Geneva and its implementation written by Keith Brown from Plurasight LLC available here. Microsoft Sharepoint is meant to go down the way of claims based authentication. Even though there are no clues as of today that Zermatt will be used as the underlying technology to implement such authentication mechanism, there are good chances that both can interoperate.

    Geneva is one of the pillars beyond Veracruz project.

    This is a hot subject as proven by the Microsoft 2008 PDC agenda and the Issue 16 of the Architecture Journal.

    c. Veracruz

    Veracruz project will rely on such a mechanism and leverage Facebook capabilities (authentication, user/profile/contact management) in conjonction with Windows Sharepoint Services in order to make identity federation at heart of the system.

  • Snip your dsl into prototypes

    Preamble

    Mary Jo Foley revealed the love story between Microsoft and modeling a few months ago. But the information that must retain our attention is that Oslo will be certainly the city chosen for this honeymoon. Indeed, a few years ago Microsoft did the first move when appointing famous modeling engineers and developing DSL Tools in order to seduce this model on the first date. Since then, Microsoft never stopped declaring its love and even promoted it as an essential pillar of its SOA strategy.

    So, for connected system developers, having a look at DSL tools seems to be a good way to understand the perspectives that Oslo can bring into the SOA panorama.

    That being said, this post targets DSL model prototyping.

    Introduction

    Everybody knows patterns promoted by the famous Gang of Four, when defining a model the same problematic applies. Some concepts are based on the same structure or a concept can be an aggregate of existing concepts. It would be very useful to be able to define reusable patterns in DSL as we can do for code.

    This post will present a solution to integrate such mechanisms into your DSL tools projects. DSL tools offer many customization points that we will use to accomplish our goal. Unlike technologies such as WCF, WPF or WF, DSL tools offers customization points rather than extensibility mechanisms. The promised feature codenamed Backplane (designer bus) or Oslo will maybe change this situation.

    This post will present a solution to integrate such mechanisms into your DSL Tools projects.

    DSL Tools offers many customization points, we'll use them to respond to our needs.

    Unlike technologies such as WF or WCF, with DSL Tools we use more customization mechanisms than extensibility mechanisms. The promised Backplane feature (designer bus) and even Oslo may lead Microsoft to change its orientation towards this and introduce more extensibility rather than customization.

    How to

    DSL Tools offers basic features to support prototyping that are used essentially to customize dragging and dropping of a dsl toolbox item onto a diagram.

    These features are essentially based on a single class: ElementGroupPrototype.

    We must not interact directly with this class. A builder design pattern was set up in order to produce a prototype. ElementGroup class must be used instead because it provides a more convenient developer experience. This class has basic methods to build your prototype from selected model elements: Add, AddGraph, AddRange. The AddGraph method adds automatically embedded children and associated relationships of the added model element. Prototyping can be seen as a clone to whom a specific property is set with an autogenerated value. Indeed, when you merge your elementgroupprototype on your model, the created model element takes into account if one of its properties has an associated ElementNameProvider allowing assigning a unique value to this property.

    These features can be used to enable copy and paste operations as well. It will be our first step, it allows to easily adopt prototype mechanisms.

    We won’t dig into the details about this as it's already described into the "Domain-Specific Development with Visual Studio DSL Tools" book that must be considered as a bible for people using DSL Tools (See page 419 "Implementing Copy and Paste").[1]

    An implementation of these operations is available as well within the library developed by the DslFactory community; this library named "DslFactory Utilities" is available on Codeplex. We will take into account various fore mentioned work. We'll add the cut operation support and enable all these operations from the visual studio standard edit menu.

    The next step will be to customize the ModelExplorer window, especially the contained tree view in order to add our own nodes and mechanisms to list user defined prototypes.

    Finally, we will take advantage of DSL tools prototype serialization to persist prototypes on disk thus allowing offering a similar mechanism as code snippets does.

    We'll use a DSL Tools solution based on the Domain-Specific Language Designer Class Diagrams template in order to present this solution. We add to this template solution a folder named “CustomCode” in the “DSL” and “DSL package” projects

    Download

    Download the complete solution here.

    A screencast showing a scenario taking advantage of the functionalities presented in this paper is available here.

    The pdf version of this article is available here.

    Enable cut, copy and paste operations

    Here is the step by step procedure:

    · Define copy, cut and paste operation logic,

    · Register these operations in order to be able to call them via the standard and contextual menus.

    Declare your new commands

    1. Open Solution Explorer.

    2. Go to the DslPackage project.

    3. Open the Commands.vsct file.

    4. Define a button for each operation

    <Button guid="guidCmdSet" id="cmdidCopy" priority="0x0902" type="Button">
      <Parent guid="guidCmdSet" id="grpidContextMain"/>
      <CommandFlag>DefaultDisabled</CommandFlag>
      <CommandFlag>DefaultInvisible</CommandFlag>
      <CommandFlag>DynamicVisibility</CommandFlag>
      <Strings>
        <ButtonText>Copy</ButtonText>
      </Strings>
    </Button>

    5. Declare its associated symbols.

    5.a Open the GeneratedCode/Constants.cs

    5.b Copy the value of the Constants.xxxCommandSetId constant.

    5.c Assign it to the value attribute of the GuidSymbol xml element

    <GuidSymbol name="guidCmdSet" value="{xxxx-xxx-xx-xx-xxx}" >
      <IDSymbol name="cmdidCopy" value="0x804"/>
    </GuidSymbol>

    6. Create a static Commands class to reference the information associated to your new commands.

    6.a Right-click on the CustomCode folder of the DslPackage project, Add Class

    6.b Enter "Commands" as name.

    6.c For each command, declare the constant integer used as value for the value attribute of the IDSymbol xml element associated to the command.

    internal static class Commands
    {
        private const int CopyCommandId = 0x804;
        private const int CutCommandId = 0x805;
        private const int PasteCommandId = 0x806;
    
        public static readonly CommandID Copy;
        public static readonly CommandID Paste;
        public static readonly CommandID Cut;
    
        static Commands()
        {
            Copy = new CommandID(Constants.xxxCommandSetId, CopyCommandId);
            Paste = new CommandID(Constants.xxxCommandSetId, PasteCommandId);
            Cut = new CommandID(Constants.xxxCommandSetId, CutCommandId);
        }
    }

    Implement your commands

    7. Open or create a partial class of your CommandSet in the "CustomCode" folder.

    8. Develop for each operation an handler in order to display (or hide) the command depending on the context and an handler that executes the command when it is selected.

    private void OnStatusCopyOrCut(object sender, EventArgs e)
    {
        MenuCommand command = sender as MenuCommand;
        command.Visible = command.Enabled = 
            ((this.CurrentSelection.Count > 0) && 
            !(this.GetSelectedObject(0) is Diagram));
    }
    private void OnMenuCopy(object sender, EventArgs e)
    {
        Diagram l_diagram = this.CurrentDocView.CurrentDiagram;
        ElementGroup l_elementGroup = new ElementGroup(l_diagram.Partition);
    
        foreach (object selectedObject in this.CurrentSelection)
        {
           ShapeElement l_shapeElement = selectedObject as ShapeElement;
           if (l_shapeElement != null && 
               l_shapeElement.ModelElement != null && 
               l_shapeElement.ModelElement is ModelType)
           {
              l_elementGroup.AddGraph(l_shapeElement.ModelElement, true);
           }
         }
         if (l_elementGroup.RootElements.Count == 0) return;
         IDataObject data = new DataObject();
         data.SetData(l_elementGroup.CreatePrototype());
         Clipboard.SetDataObject(
            data,
            false,
            10,
            50);
    }

    For the copy and cut operations, we display commands only if the current selection contains objects other than current Diagram.

    For the paste operations, we could check if the clipboard data content can be merged with the current diagram.

    The cut operation logic adds to the copy operation logic a step that deletes the copied elements.

    As you can see, if you downloaded the complete solution, we added to our partial CommandSet class 2 additional things:

    • An operation GetSelectedObject to get a selected object from its index in the current selection list.
    • A property CurrentStore to easily access the current IMS.

    9. Override the GetMenuCommands() method to add your new commands to the context menu.

    9.a Instantiate a DynamicStatusMenuCommand for each operation.

    protected override IList<MenuCommand> GetMenuCommands()
    {
      IList<MenuCommand> l_commandList = base.GetMenuCommands();
    
      DynamicStatusMenuCommand l_cmdCopy = 
         new DynamicStatusMenuCommand(
           new EventHandler(OnStatusCopyOrCut),
           new EventHandler(OnMenuCopy),
           Commands.Copy);
      
      l_commandList.Add(l_cmdCopy);
      ...
      return l_commandList;
    }

    In the introduction, we wanted to enable the standard Visual Studio cut, copy, paste commands on our shapes. The only remaining difficulty is to identify the commands associated to them. They are available in the well known System.ComponentModel.Design.StandardCommands

    By now, you must only instantiate a new DynamicStatusMenuCommand class by using the relevant StandardCommands field for each command (StandardCommands.Copy etc…).

    Deploy your new commands

    It is required to increment the Menu Resource Index for DSL Tools to take into account your new commands.

    10. Open GeneratedCode\Package.tt

    11. Increment the second integer of the ProvideMenuResource attribute.

    [VSShell::ProvideMenuResource("1000.ctmenu", 2)]

    12. Transform all templates

    13. Rebuild.

    14. Enjoy

    image

    Figure 1 - Visual Studio Edit Menu

    Conclusion

    So, it works fine but the current behavior is very restricted not only because operations are enabled only for ModelType and its derived types. When you copy a class that contains operations, they are not copied. It’s the expected behavior, indeed, by default; the copy propagation is not enabled. So, if you want to change this behavior, you only need to set to true the property PropagatesCopy of ModelClass Domain Role of ClassHasOperations relationship.

    image

    Figure 2 PropagesCopy property

    The recommendation is to allow a fine grained customization of the prototype creation from a domain class. To do this, you can define an interface IPrototypable to declare the domain class that support copy operation and to allow the customization of the creation of an ElementGroupPrototype from it.

    internal interface IPrototypable
    {
        ElementGroupPrototype GetPrototype();
    }

    To implement this interface by a desired domain class, it’s only required to create a dedicated partial class and to implement the default prototype logic:

    public ElementGroupPrototype GetPrototype()
    {
        ElementGroup l_elementGroup = new ElementGroup(this.Store);
        l_elementGroup.AddGraph(this, true);
        return l_elementGroup.CreatePrototype();
    }

    Customize the model explorer

    Add your own nodes

    The model explorer user interface is based on a tree view; the idea here is to add custom nodes to the treeview in order to list user-defined prototypes.

    Unfortunately, if it’s possible to customize the existing nodes (label, image, visibility), no real mechanism are offered to add our own nodes. However, a workaround exists: the treeview loading mechanism is done by a single method: RefreshBrowserView()

    If it was a virtual method, we could override it to add our own nodes (This one is going straight to connect.microsoft.com, so feel free to vote for it). In the mean time, you can have a look at the methods that use it thanks to Reflector: ModelExplorerToolWindow. OnDocumentWindowChanged, and ModelExplorerTreeContainer.ElementEventsEndedEventHandlerImpl.

    We can override the first method to add a call to our own refresh browser view logic. The second method is a callback registered for ElementEventsEnded IMS event, we can add or own callback for this event.

    To add custom nodes to the model explorer, there is only one constraint, custom node class must inherit from Microsoft.VisualStudio.Modeling.Shell.ExplorerTreeNode. It is required to call the method ProvideNodeText in order to populate the Text property of your node if it is not set it in its constructor.

    Below is a basic implementation of a custom node:

    public class DefaultTreeNode : ExplorerTreeNode
    {
            public DefaultTreeNode(String name, String text)
            {
                if (name == null)
                {
                    throw new ArgumentNullException("name");
                }
                if (text == null)
                {
                    throw new ArgumentNullException("text");
                }
                this.Name = name;
                this.Text = text;
            }
            protected override string ProvideNodeText()
            {
                return this.Text;
            }
    }

    1. Go to the DslPackage project

    2. Open or create a partial class of your ModelExplorerTreeContainer (ModelExplorer) in the "CustomCode" folder.

    3. Add it this method :

    internal void RefreshBrowserViewExt() 
    { 
    // Add your custom logic here 
    }

    4. Add your custom logic to load your nodes in the model explorer.

    5. Create a callback that calls your previously created method.

    private void ElementEventsEndedEventHandlerImplExt(object sender, ElementEventsEndedEventArgs e)
    {
        this.RefreshBrowserViewExt();
    }

    6. Override SubscribeToImsEvent method and add your own callback in order to call your custom refresh browser logic.

    protected override void SubscribeToImsEvent(Store newStore)
    {
        base.SubscribeToImsEvent(newStore);
        // HACK : Check above if the store is null.
        newStore.EventManagerDirectory.ElementEventsEnded.Add(
            new EventHandler<ElementEventsEndedEventArgs>(
            this.ElementEventsEndedEventHandlerImplExt)
            );
    }

    7. Override UnsubscribeToImsEvent method to remove your callback.

    protected override void UnsubscribeToImsEvent(Store oldStore)
    {
      base.UnsubscribeToImsEvent(oldStore);
      if (oldStore != null)
      {
          oldStore.EventManagerDirectory.ElementEventsEnded.Remove(
           new EventHandler<ElementEventsEndedEventArgs>(
           this.ElementEventsEndedEventHandlerImplExt));
      }
    }

    8. Open or create a partial class of your ModelExplorerToolWindow in the "CustomCode" folder of the DslPackage.

    9. Override OnDocumentWindowChanged method in order to call your custom refresh browser logic view.

    protected override void OnDocumentWindowChanged(
                ModelingDocView oldView, ModelingDocView newView)
    {
       base.OnDocumentWindowChanged(oldView, newView);
       ((YourModelExplorer)this.TreeContainer).RefreshBrowserViewExt();
    }

    Add your own model explorer commands

    You can add your own commands in the context menu associated to the model explorer.

    The only differences between this and doing it for the context menu associated to the current diagram are:

    • The ids to use: guidCommonModelingMenu and cmdidMenuExplorerCommand.
    • The mandatory definition of a guid symbol associated to the command

    1. Go to the DslPackage project.

    2. Open the Commands.vsct file.

    3. Define a button for your operation using the relevant ids.

    <Button guid=" guidMenuExplorerCommand" 
           id="cmdidMenuExplorerCommand" priority="0x0902" type="Button">
      <Parent guid="guidCommonModelingMenu" id="grpidExplorerMenuGroup"/>
      <CommandFlag>DefaultDisabled</CommandFlag>
      <CommandFlag>DefaultInvisible</CommandFlag>
      <CommandFlag>DynamicVisibility</CommandFlag>
      <Strings>
        <ButtonText>My command</ButtonText>
      </Strings>
    </Button>

    4. Declare the symbol associated to it.

    <GuidSymbol name=" guidMenuExplorerCommand " value="{xxxx048E-739A-474A-94CC-CDA7347C52E7}" >
      <IDSymbol name=" cmdidMenuExplorerCommand " value="0x807"/>
    </GuidSymbol>

    5. Create a static Commands class to reference the information associated to your new commands.

    a. Right-click on the CustomCode folder of the DslPackage project, Add Class

    b. Enter "Commands" as name.

    c. Declare the constant guid used as value for the value attribute of the GuidSymbol xml element associated to your command.

    d. Declare the constant integer used as value for the value attribute of the IDSymbol xml element associated to your command.

    6. Open or create a partial class of your ModelExplorerTreeContainer (ModelExplorer) in the "CustomCode" folder.

    7. For each command implement a handler in order to display (or hide) the command depending on the context and a handler that is executed if the command is selected.

    8. Override the AddCommandHandlers method to add your new commands to the model explorer context menu.

    public override void AddCommandHandlers(IMenuCommandService menuCommandService)
    {
        base.AddCommandHandlers(menuCommandService);
    
        DynamicStatusMenuCommand l_cmdMyCmd = new DynamicStatusMenuCommand(
            new EventHandler(OnStatusCmd),
            new EventHandler(OnMenuCmd),
            Commands.MyCmd);
        menuCommandService.AddCommand(l_ cmdMyCmd);
    }

    9. Open the GeneratedCode\Package.tt

    10. Increment the second integer of the ProvideMenuResource attribute.

    [VSShell::ProvideMenuResource("1000.ctmenu", 3)]

    11. Transform all templates

    12. Rebuild

    13. Enjoy

    Define prototype mechanism

    Here we’re going to offer to users a mechanism to prototype a model element. To do this, we use the logic presented to enable copy operations and to customize the model explorer.

    The customization of the model explorer must allow:

    Adding custom node when a user defines a prototype.

    Handling drag and drop on prototype node from the model explorer.

    Saving user-defined prototypes beyond the scope of visual studio instance (persisted on disk).

    Add custom node when a user defines a prototype

    1. Create a serializable class Pattern

    a. Right-click on the CustomCode folder of the DslPackage project, Add Class

    b. Set its name to "Pattern".

    2. Add to it two properties : Name (String) and Prototype (ElementGroupPrototype)

        [Serializable]
        public sealed class Pattern
        {
            public String Name { get; set; }
            public ElementGroupPrototype Prototype { get; set; }
    
            public Pattern(String name, ElementGroupPrototype prototype)
            {
                this.Name = name;
                this.Prototype = prototype;
            }
       }

    3. Implement an ExplorerTreeNode dedicated to your class.

    public class PatternTreeNode : ExplorerTreeNode
    {
       private Pattern m_pattern;
    
       public PatternTreeNode(Pattern pattern)
       {
           if (pattern == null)
           {
               throw new ArgumentNullException("pattern");
           }
           m_pattern = pattern;
           this.Name = pattern.Name;
       }
    
       public Pattern Pattern
       {
           get { return m_pattern; }
       }
    
       protected override string ProvideNodeText()
       {
           return m_pattern.Name;
       }
    }

    4. Open or create a partial class of your ModelExplorerTreeContainer (ModelExplorer) in the "CustomCode" folder.

    5. Add private field to store Pattern objects.

    private List<Pattern> m_patternList = new List<Pattern>();

    6. Code an Add and Remove method to manage the pattern list and to update the model explorer

     internal void AddPatternTreeNode(String name, ElementGroupPrototype prototype)
     {
         string l_name = name;
         if (string.IsNullOrEmpty(name))
         {
             l_name = this.GetNewPatternName();
         }
         Pattern l_pattern = new Pattern(l_name, prototype);
         this.AddPatternTreeNode(l_pattern);
         m_patternList.Add(l_pattern);
     }
     private void AddPatternTreeNode(Pattern pattern)
     {
         TreeNode l_rootTreeNode = this.ObjectModelBrowser.TopNode;
         TreeNode l_prototypesTreeNode = l_rootTreeNode.Nodes["Prototypes"];
         PatternTreeNode l_patternTreeNode = new PatternTreeNode(pattern);
         l_patternTreeNode.UpdateNodeText();
         this.InsertTreeNode(l_prototypesTreeNode.Nodes, l_patternTreeNode);
     }
     private void RemovePatternTreeNode(PatternTreeNode patternTreeNode)
     {
         patternTreeNode.Remove();
         m_patternList.Remove(patternTreeNode.Pattern);
     }

    7. Load into the Model Explorer your list of patterns by using a specific RefreshBrowserViewExt method

    internal void RefreshBrowserViewExt()
    {
        this.ObjectModelBrowser.BeginUpdate();
        if (this.ObjectModelBrowser.TopNode != null)
        {
            TreeNode l_rootTreeNode = this.ObjectModelBrowser.TopNode;
            DefaultTreeNode l_defaultTreeNode = new DefaultTreeNode("Prototypes");
            this.InsertTreeNode(l_rootTreeNode.Nodes, l_defaultTreeNode);
            l_defaultTreeNode.UpdateNodeText();
            for (int i = 0; i < m_patternList.Count; i++)
            {
                this.AddPatternTreeNodeToObjectModelBrowser(m_patternList[i]);
            }
            l_defaultTreeNode.Expand();
        }
        this.ObjectModelBrowser.EndUpdate();
    }

    8. Similar to what has been done for copy operations; add to your diagram’s context menua method to prototype the user’s selection. The only difference is that it is not the Clipboard that will receive the created ElemtGroupPrototype but the ModelExplorer. To get a pointer to our ModelExplorer, we add a strongly typed property to our ModelExplorerToolWindow that casts its generic TreeContainer property.

    private void OnMenuPrototype(object sender, EventArgs e)
    {
        Diagram l_diagram = this.CurrentDocView.CurrentDiagram;
        ElementGroup l_elementGroup = new ElementGroup(l_diagram.Partition);
        foreach (object selectedObject in this.CurrentSelection)
        {
            // Pick out shapes representing Component model elements.
            ShapeElement l_shapeElement = selectedObject as ShapeElement;
            if (l_shapeElement != null && l_shapeElement.ModelElement != null && 
                l_shapeElement.ModelElement is ModelType)
            {
                l_elementGroup.AddGraph(l_shapeElement.ModelElement, true);
            }
        }
        this.PrototypeExplorerToolWindow.PrototypeExplorer.AddPatternTreeNode(
                null, l_elementGroup.CreatePrototype());
    }

    9. Don’t forget to increment the second integer of the ProvideMenuResource attribute.

    image

    Figure 3 Diagram Context menu

    Implement prototype node drag and drop from the model explorer

    The tree control contained in the ModelExplorer is simply the well known System.Windows.Forms.TreeView control. Thus , it offers the same features such as drag and drop.

    To customize its behavior, you can override your ModelExplorer's OnCreateControol method ( due to ContainerControl base class inheritance )

    1. Open or create a partial class of your ModelExplorerTreeContainer (ModelExplorer) in the "CustomCode" folder.

    2. Override the OnCreateControl () method to customize the tree control.

    protected override void OnCreateControl()
    {
       base.OnCreateControl();
       this.ObjectModelBrowser.LabelEdit = true;
       this.ObjectModelBrowser.KeyUp += new KeyEventHandler(ObjectModelBrowser_KeyUp);
       this.ObjectModelBrowser.ItemDrag += new ItemDragEventHandler(ObjectModelBrowser_ItemDrag);
       this.ObjectModelBrowser.AfterLabelEdit += new 
         NodeLabelEditEventHandler(ObjectModelBrowser_AfterLabelEdit);
    }

    3. Implement the desired logic for each event handled. We decide to offer:

    • Prototype deletion by selecting it and pressing the delete key.
    • Prototype renaming by clicking on it.
    • Prototype drag and drop (as you can see it, it’s straightforward to activate it, thanks to ElementGroupPrototype).
    private void ObjectModelBrowser_KeyUp(object sender, KeyEventArgs e)
    {
       if (!e.Handled && e.KeyValue == (int)Keys.Delete)
       {
                    TreeNode l_treeNode = this.ObjectModelBrowser.SelectedNode;
                    if (l_treeNode != null && l_treeNode is PatternTreeNode)
                    {
                        this.RemovePatternTreeNode((PatternTreeNode)l_treeNode);
                    }
       }
    }
    private void ObjectModelBrowser_ItemDrag(object sender, ItemDragEventArgs e)
    {
      if (e.Item is PatternTreeNode)
      {
       this.ObjectModelBrowser.SelectedNode = (TreeNode)e.Item;
       this.ObjectModelBrowser.DoDragDrop(
         ((PatternTreeNode)e.Item).Pattern.Prototype, DragDropEffects.Copy);
      }
    }
    private void ObjectModelBrowser_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
    {
      if (e.Node is PatternTreeNode)
      {
        ((PatternTreeNode)e.Node).Pattern.Name = e.Label;
      }
    }

    image

    Figure 4 Customized model explorer

    Save user-defined prototypes beyond the scope of visual studio instance.

    Saving user-defined prototypes is only a matter of serializing it. Indeed, ElementGroupPrototype can transit through the Clipboard. So, the serialization mechanism is already supported. We use it to save them in a basic file.

    1. Create a static class to manage serialization (and deserialisation) of user-defined prototype list into a file.

    a. Right-click on the CustomCode folder of the DslPackage project, Add Class

    b. Enter “UserDefinedPrototypeSerializationHelper" as its name.

    2. Add a method to implement serialization and deserialization mechanism.

    internal static void Serialize(List<Pattern> patternList, string file)
    {
      if (!Directory.Exists(Path.GetDirectoryName(file)))
      {
        Directory.CreateDirectory(Path.GetDirectoryName(file));
      }
      using (Stream stream = File.Open(file, FileMode.Create))
      {
        BinaryFormatter l_binaryFormatter = new BinaryFormatter();
        l_binaryFormatter.Serialize(stream, patternList);
      }
    }
    
    internal static List<Pattern> DeSerializeObject(string file)
    {
      List<Pattern> l_patternList = null;
      using (Stream stream = File.Open(file, FileMode.Open))
      {
        BinaryFormatter l_binaryFormatter = new BinaryFormatter();
        l_patternList = (List<Pattern>)l_binaryFormatter.Deserialize(stream);
      }
      return (l_patternList == null ? new List<Pattern>() : l_patternList);
    }

    3. The deserialization mechanism used in the experimental hive requires that you provie a specific a AssemblyResolve mechanism in order to resolve dsl assemblies.

    static UserDefinedPrototypeSerializationHelper()
    {
                AppDomain.CurrentDomain.AssemblyResolve += 
                   new ResolveEventHandler(CurrentDomain_AssemblyResolve);
    }
    
    private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
      if (args.Name.IndexOf("DslPackage") > 0)
      {
        return typeof(UserDefinedPrototypeSerializationHelper).Assembly;
      }
      else if (args.Name.IndexOf("Dsl") > 0)
      {
        return typeof(YourDomainModel).Assembly;
      }
      return null;
    }

    4. Open or create a partial class of your ModelExplorerTreeContainer (ModelExplorer) in the "CustomCode" folder.

    5. Add specific code to the overridden method OnCreateControl () to deserialize saved user-defined prototypes and to load it into the ModelExplorer.

    protected override void OnCreateControl()
    {
        String l_fileName = this.GetPrototypeFileName();
        if (File.Exists(l_fileName))
        {
            m_patternList = PrototypeSerializationHelper.DeSerializeObject(l_fileName);
        }
        base.OnCreateControl();
        this.ObjectModelBrowser.LabelEdit = true;
        …

    6. Override the Dispose method to save when the user-defined prototypes into a file when the ModelExplorer is closed.

    protected override void Dispose(bool disposing)
    {
         if (disposing)
         {
             String l_fileName = this.GetPrototypeFileName();
             UserDefinedPrototypeSerializationHelper.Serialize(m_patternList, l_fileName);
         }
         base.Dispose(disposing);
    }

    Going further

    This article lists a fair amount of customization mechanisms and promotes ElementGroupPrototype class. It’s possible, of course to go further. You can add export and import feature on prototypes in order to share them in a similar way as visual studio templates or code snippets. If the fact that you can associate an ElementNameProvider to only one property is too limitating, one can use an interface (IPrototypable to broaden the customization of the model element created. For instance, customizing the property grid associated to the prototype allows defining a description to it.

    It would be very useful if the Model Explorer could support domain class namespaces (as model element base classes).

    Add to toolbox

    Before ending this article, we add to the ModelExplorer context menu a specific command to allow adding to the toolbox and user-defined prototype.

    1. Implement an “Add to toolbox” command (See paragraph entitled “Add your own model explorer commands”)

    2. Use IToolboxService service mechanism in the execution logic of your command and take advantage of ModelingToolboxItem class to create and add to the toolbox an item dedicated to your prototype.

    private void OnMenuAddToToolBox(object sender, EventArgs e)
    {
      IToolboxService l_toolBoxService = 
            (IToolboxService)this.ServiceProvider.GetService(typeof(IToolboxService));
    
      if (this.ObjectModelBrowser.SelectedNode is PatternTreeNode)
      {
        Pattern l_pattern = ((PatternTreeNode)this.ObjectModelBrowser.SelectedNode).Pattern;
    
        ModelingToolboxItem l_modelingToolboxItem = new ModelingToolboxItem(
                 l_pattern.Name, 1,  l_pattern.Name, 
                (Bitmap)ImageHelper.GetImage(
    PrototypeDomainModel.SingletonResourceManager.GetObject("ModelClassToolboxBitmap")), 
                             "Prototypes", "Prototypes", "", "Prototype", l_pattern.Prototype, 
                            new ToolboxItemFilterAttribute[] {                            new ToolboxItemFilterAttribute(PrototypeToolboxHelper.ToolboxFilterString, ToolboxItemFilterType.Require)  });
    
        l_toolBoxService.AddToolboxItem(l_modelingToolboxItem, "Prototypes");
      }
    }

    image

    Figure 5 Customized toolbox

    Workaround

    Unfortunately, the treeview control has unexpected behavior when you want to customize the status of a context menu command.

    If you want command displayed only for a specific tree node, you basically use SelectedNode property of ObjectModelBrowser (on the ModelExplorer instance). But this property doesn’t return the relevant selected node if you do not select the node with a left click before right clicking it to display the context menut.

    To work around this problem, one can implement a specific callback for the AfterSelect event raised by the treeview that uses the selection service.

    1. Open or create a partial class of your ModelExplorerTreeContainer (ModelExplorer) in the "CustomCode" folder.

    2. Add specific code to the overrided OnCreateControl () method to add a callback for AfterSelect event raised by the contained treeview (ObjectModelBrowser).

    protected override void OnCreateControl()
    {
      String l_fileName = this.GetPrototypeFileName();
      if (File.Exists(l_fileName))
      {
        m_patternList = UserDefinedPrototypeSerializationHelper.DeSerializeObject(l_fileName);
      }
      base.OnCreateControl();
      this.ObjectModelBrowser.LabelEdit = true;
      this.ObjectModelBrowser.KeyUp += new KeyEventHandler(ObjectModelBrowser_KeyUp);
      this.ObjectModelBrowser.ItemDrag += 
      new ItemDragEventHandler(ObjectModelBrowser_ItemDrag);
                this.ObjectModelBrowser.AfterLabelEdit += 
      new NodeLabelEditEventHandler(ObjectModelBrowser_AfterLabelEdit);
                this.ObjectModelBrowser.AfterSelect += 
      new TreeViewEventHandler(ObjectModelBrowser_AfterSelect);
    }

    3. Implement your callback by using the selection service to customize the selection mechanism associated to our custom tree node.

    private void ObjectModelBrowser_AfterSelect(object sender, TreeViewEventArgs e)
    {
        if (e.Node is PatternTreeNode)
        {
            this.SelectionService.SetSelectedComponents(
              new object[] {((PatternTreeNode)e.Node).Pattern});
        }
    }

    Support prototype auto-layout on drag and drop

    When you drag and drop a prototype or when you copy and paste a model element, the associated shapes created by this action are displayed by default on the same position.

    To avoid this, one can call AutoLayoutShapeElements method of the diagram on which modelelements are dropped.

    Here is below a code extract to accomplish this task.

    1. Open Solution Explorer.

    2. Go to the Dsl project.

    3. Open or create a partial class of your Diagram in the "CustomCode" folder.

    4. Override the OnDragDrop method to layout automatically dropped elements.

    Take into account model element namespace

    There are multiple ways to customize the model explorer; one of them is to define a new tool window.

    You can act on the default model explorer representation and take into account the namespace associated to the domain class of a model element.

    A smart Class designer

    The sample solution coming along with this paper is, as described in the introduction, based on the class diagram DSL template. If one of Rosario’s promises is to offer a set of new diagrams built on DSL tools, it would be very useful if these diagrams provide prototyping capability. For instance, a class diagram could assist developers by offering by default a set of code design patterns (gang of four…) and offering a simple sharing capability between users following code snippet footsteps.

     

     


    [1] You can as well navigate on the dedicated MSDN Forum to Visual Studio extensibility.

    Download the complete solution here.

    A screencast showing a scenario taking advantage of the functionalities presented in this paper is available here.

    The pdf version of this article is available here.

  • Acropolis, what is the real target?

    Adopting new technology is not just about building a “Hello World” Application but testing its capabilities to match precise requirements for real-world situations. After a fair amount of playing and digging in, this article aims to provide an overview about Acropolis. At the same time, I will suggest solutions to enhance some of the drawbacks identified in the current build that I outline in this paper.

    Rapid Application Development

    Acropolis provides an integrated designer within Visual Studio Orcas where we can drop “Parts” and set up their properties. This is an excellent way for “Beginners” or for Quick and Dirty purpose. Unfortunately, we stick on this design when we start to apply enterprise productivity. Let’s me take an example; an application that loads different workspace depending on “User Profile” can’t be integrated in Acropolis NavigationManager. We need to call manually the method “BindToCollection” on “Microsoft.Acropolis.PartFx.NavigationManager” because when “AcropolisApplication” is activated, it performs the “ChildParts” binding on the NavigationManager.

    Another RAD orientation is to associate “Part” of its “View” with a custom Attribute “ViewExtensionInfo”. A more convenient way to address this would be to use an external mapping file.

    At least, Services Dependencies concept is pretty cool by design, but in Real World application, we don’t know in advance all the components involved in a composite application.

    It makes sense that most efforts are driven towards RAD. Acropolis is at a very early stage and probably focused on a seduction phase, it has to gain its audience. This means that it targets a wider audience and it uses WPF more for its simple and sexy look and feel, but it seems that is done to the detriment of Enterprise reality.

    On the road to Enterprise Productivity

    The current build of Acropolis is RAD oriented but it provides many opportunities to be extended.

    Extensibility

    One of the beautiful things that Acropolis provides is a real reusable framework that comes from CAB and so on. This API’s is very useful when we want to provide custom implementations on top of Acropolis. This means that we can participate to Acropolis effort and provide an enterprise orientation to ensure its specific requirements are addressed.

    Theme Manager

    Implementing custom theme is done by inheriting from “Microsoft.Acropolis.Windows.Theme”. Themes registration can be done by the method “AcropolisApplication.RegisterThemeAssembly” by specifying your theme object type as parameter, the registration have to be done when “AcropolisApplication” starts (override the method “OnStartup”). This is the easiest way but makes it hard coded. On the other hand, you can declare your themes by configuration as follow:

    <configuration>
      <configSections>
        <sectionGroup name="microsoft.acropolis">
          <section name="themeInfo" 
                   type="Microsoft.Acropolis.ThemeInfoConfigSection,Microsoft.Acropolis.PartFx,Version=2.0.0.0,Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
        </sectionGroup>
      </configSections>
      <microsoft.acropolis>
        <themeInfo>
          <themes>
            <add assembly="[AssemblyName]" type="[Namespace.ThemeObjectType]"/>
          </themes>
        </themeInfo>
      </microsoft.acropolis>
    </configuration> 
    
    

    The theme configuration above can be loaded by “ConfigFileThemeProvider” class that implements “IThemeProvider”. This class loads all the types implementing the “ITheme” interface when the type attribute on “theme\add” element is a star (“*”), otherwise, it loads the type specified explicitly. Once again, we need to register manually when on Application starts by calling “ThemeService.Instance.RegisterThemes”. I suggest implementing an automatic theme registration mechanism when it is specified by configuration. In addition, having a property “IsDefault” on “ThemeConfigElement” allows specifying theme externally without setting “Theme” property on the “AcropolisApplication”.

    Part Model

    Part Composition

    Acropolis Composition applies a “Composite” Pattern to represent a hierarchical object model for “Part”. However, the user Interface representation within WPF doesn’t provide by default a child Parts chaining, it has to be done manually. It should be best to provide this behavior by default within the Composition Engine.

    With Acropolis, there is no limit to add child parts. I’m very anxious in regards to this behavior because it might generate issues on how developers make their application. What I mean by this is that Part is not only focused on the business case it addresses, it also has to be aware of its “environment” in the sense that it has to take into account the layout or the window it is loaded in.

    To address this, I suggest a new Part Composition Object Model that allow for anyone to compose an application layout based on “PartView”.

    First, let’s make sure we’re all in the same page by presenting the new Object Model in the figure below:

    On the left side:

    1. Workspace” represents the Root Level of your application; basically, it’s a Window. Workspace has one or many Layout child,
    2. Layout” represents a single active view on the Workspace, Layout has one or many Slot child,
    3. Slot” represents a zone (like a canvas), Slot has one or many Component,
    4. Component” represents the Developers Unit of Work, it can have a child that we can call “Sub-Component

    On the right side:

    You notice that it’s the UI Control mapping for our Part Object Model. Don’t worry, this model is not really different than the one provided by default by Acropolis provides. The goal is the same but the approach is a little bit different using WPF Control. So, let’s talk about the advantage of this model, several capabilities come to us:

    1. Parts Composition is not only for the Developers but also for Designer. Both Layout and Slot aims to compose your application layout design, allowing Developers to work only on the Component View.
    2. Allows a clean composition of Parts
    3. Layout and Slot allow defining a “Scope” level because they act as a “Container”. This is an important feature in all cases to apply “Event Notification”, “Data”, “Services” and so on.
    4. Avoid confusion about using “ActiveParts” versus “ActivePart” Navigation Property. By using WPF “ItemsControl” Control, the only one thing we do is to call the “ActiveParts” property without taking care about children. Now, the big question is: How can I apply a Layout Pattern on Workspace, LayoutChildParts and SlotChildParts? The answer is very simple, WPF is powerful; ItemsControl provides a property called “ItemsPanelTemplate”, this property allows defining any kind of layout (LayoutPane in Acropolis) like Tab, Grid, Stack, …

    Screenshots are more meaningful than a long sentence:

    Figure 1: Single (Main Layout) Layout on a Workspace, Slots (Menu, Navigation and Main Content) are in a “StackPanel” with Vertical orientation.

    Figure 2: Single (Main Layout) Layout on a Workspace, Slots (menu, Navigation and Main Content) are in a “TabPanel”.

    Figure 3: Layouts (Main Layout and Second Layout) are in a “TabPanel” on a Workspace, Slots are in a “StackPanel” with Vertical orientation.

    Part Communication

    Building Composite application tends to avoid thigh coupling between Parts. Unfortunately, NavigationManager is not going this way with NavigateTo method that implies a dependency between two parts. The alternative is to provide a custom NavigationManager implementation.

    Part provides a set of features as “Services Dependencies”, “Data Publishing” and “EventNotification”. This is sounds good but the lack is in the PartView side that consumes them. Of course, by code behind, we can access the Part via “IPartViewContract”, right. Now, what about XAML? It would be more convenient to be able to develop parts without requiring registering events or calling external services in code behind.

    Part Life Cycle

    One of the big surprise with Acropolis is there is no real life cycle applied on Part or on Acropolis Application. It is very important to have a well known, pre defined, life cycle applied on all components. I suggest having something like ASP.NET’s Life Cycle; it simplifies the implementation of custom code, and allows knowing exactly what happen behind the scene at each and every stages of the execution. By experience, doing something as a black box induces some frustration and slows down technology adoption.

    Conclusion

    Personally, I see Acropolis taking the RAD approach and not target industrialization at this stage. Acropolis is heavily based on the famous composite UI application bloc (CAB) project, so a big impatience is born to get the same level of functionality that CAB provided. I would love to see the next Acropolis releases address more heavily tools and enterprise environment. Will this request be heard? We’ll see...

  • Service Referencing part II: dynamic service discovery

     

    Introduction

    As outlined in the first part of this article, referencing services greatly extends their visibility and promotes their capitalization. When most of the time services are deployed somewhere and accessible from a fairly static location, it happens that, to cater particular business needs that we will be outlined later on this post, some services requires to be discoverable in order to be accessed transparently regardless of their particular location at the time. This document covers such a requirement by exposing how to combine open standards and WCF to publish services in such a way that they can be discovered dynamically at runtime based on custom business criteria.

    Service Referencing

    Dynamic service referencing and discovery in general are complementary to static referencing and address different business requirements.

    Dynamic Service Discovery

    Requirements

    Prior to take advantage of discovering services at run time, two major points must be addressed and settled. First, because discovery has a cost that is not negligible it must be used only to address particular business needs. Then, it must be handled with the scope of a business contract. This contract defines the way the service can be discovered as well as the options available for interacting with it.

    Discovery is costly

    By nature, discovery is costly either on the network because discovery requests will have to be sent across the whole network using multicast or either on pure processing because the discovery will add a few extra steps in the way you access a service. By using discovery, you cannot just have the service’s access points in your configuration or in a repository (remember part I of this paper).

    A full use case of taking advantage of discovery requires those steps:

    · Send a request on the network for services matching a business requirement,

    · Receive all the responses from the services,

    · Select the most relevant one,

    · Act accordingly which means extracting the selected service’s metadata in order to dynamically generate a proxy in order to communicate with this service.

    As you can see these are extra steps that increase the client side effort required to communicate with a service. Compared to just using Visual Studio’s “Add web/service reference”, it becomes obvious that it would be foolish to use discovery all over the place just for the sake of calling dynamically services. This highlight once again the fact that discovery must respond to a business requirement and must be evaluated as such before deciding on using it.

    Discoverability driven by business need

    Taking the decision to expose a service as discoverable must be done in the light of a business requirement. The overhead induced by such discoverability must be balanced with the benefits you get out of it. Some business scenarios require services to be discoverable because they’re occasionally up, they are nomad and are not always accessible at the same location and so on… We will quickly describe few scenarios later on this paper.

    Existing service selection solutions: BAS/TPM

    Existing products and technologies already address service selection based on a set of business rules; a product such as Microsoft Biztalk offers Trading Partner Management (TPM) as well as Business Activity Services (BAS) that allows selecting business partners. This mechanism rests on well defined static business contracts as well as a predefined set of rules. This is similar to what discovery requires in the sense that business contracts must be settled first before starting discovering services. The rules equivalent used by BAS/TPM is represented as criteria. Where BAS/TPM offers dynamic selection of existing, referenced services endpoints, discovery allows going one step further and adds dynamic communication between client and service.

    Business contract settled

    The first thing to complete before even thinking about launching Visual Studio to hit the discovery API and start discovering dynamically services is to clearly define the business contracts responsible to formalize the way a client can interact with such a service. This also helps identifying if the service in question should even be discoverable at all. Doing this preliminary step with discovery in mind will help you evaluate which operations should be discoverable and can, and probably will, have an impact on the way you design and decompose your business contracts. This is a crucial step to complete in order to ensure a proper use of discovery and avoid useless discovery overhead on operations that do not require it.

    Negotiation phase finalized

    The approach taken here in relation to the discovery requires that a “negotiation process” occurred prior to starting discovering services. Those services are already well known, they are driven by a well known business contract, and the only unknown piece of information at the time of discovery is the location of the service responding to a particular business need. The lever allowing switching between existing services are defined by search criteria as explained bellow.

    Search criteria defined (Extended search criteria with business context which is not included in business contract)

    Discovering services is all about being able to specify custom, business oriented criteria that are relevant to the discovery. Those criteria must be though through and evaluated in such a way that they can conveniently be set in place, computed and interpreted. At the same level that one must define the business contracts that will dictate the way services are exposed as discoverable, one must define the search criteria that will be used to discover such services.

    Use cases

    Because, as outlined previously, one must be careful to where to take advantage of discovery, it is important to outline a few sample use cases where discovery can help addressing business requirements. The most obvious advantage of using discovery features in your environment is to reduce the effort required to register your services within an information system as well as provide business attributes in order to easily discover relevant services based on business criteria.

    Goal: Reduce registration effort

    Here are three sample use cases that highlight the benefits of using discovery to either reducing registration service effort, cope with services occasionally connected as well as nomad users that requires to connect to specific, context driven, services.

    Nomad services

    A salesman visiting clients worldwide often has to connect to the office VPN to either download or upload data in order to do day to day work. By taking advantage of discovery as well as his geographical position, he can transparently find to the most relevant local branch to connect to in order to

    Disconnected services

    When dealing with occasionally connected services, it is important, in order to offer the best quality of service, to be able to find active services that respond to particular business requirements. Using discovery in such scenario, allows querying the information system for active services addressing a particular business requirement. This query mechanism can be sharpened to include specific business or technical parameters to ease the look up process and provide a transparent experience from a consumer point of view.

    N3RD – Command Center

    During wild fires in south of France, firemen have to deploy a remote command center close to the incident. Because of time constraints, there is no time to waste to setup and register this new, temporary, command center. This is where discovery comes into place; it allows the remote command center to register onto the network so the calls related to the fire are routed automatically to this new virtual station and not to the existing fire stations. Once the fire is over, this temporary, remote command center is then switched off and disappears from the information system. No calls can be routed anymore to this command center.

    Video demonstration

    A demo video of WS-discovery in action is available here.

    This demo addresses the N3RD Command Center use case described above and contains 3 parts:

    1. The client runs a probe match on the network and several stations respond to it. (Client doesn't know which fire station must be selected).

    2. Fire stations respond to the client request and indicate their respective geographical position. The client then picks the closest one.

    3. The client sends a custom probe message indicating its geographical position, only services located within a 3 miles range respond to the request. The client picks the closest one.

    In the last part, we used header injection in the address in order to call dynamically the service.

    Standardization

    There are multiple ways to technically address such a capability. Where anyone could implement its own solution to this problem it is critical to offer the most open solution. This is all about discovery therefore if you want your services to be discoverable the worst path to follow would be to implement a custom proprietary discovery protocol. In the same way, open standards allow exposing services metadata, so regardless of the platform you are working with; you can query, read and interpret those metadata. The same concern is true for discovery, being discoverable requires following standards so third parties or different platforms can still discover your services.

    Business service must focus on business it serves

    Adding to a service the capability of being discoverable must not interfere with the business purpose of the service. Such a capability must not require any specific code on the service itself and must be able to be plugged side by side with the service it exposes as discoverable. The implementation that we will describe later on takes into account this fact.

    Dynamic discovery is a technical service

    Discovery is a purely technical process allowing addressing business requirements. Such a capability must not interfere with the business in any way. The discoverability footprint must be reduced to the maximum in order to ensure proper business throughput.

    Use a specification with large adoption

    Discovery is all about the ability to be discoverable for the services requiring to be exposed as such. It is therefore crucial to follow standards in order to ease interoperability with existing technologies or products. There are numerous standards driven by consortiums that helps standardization and promotes interoperability. Within the WS-* galaxy, there is one specification targeting discovery: WS-Discovery.

    WS-Discovery

    The implementation presented here is based on Vipul Modi’s work published here that offers a very good baseline for building an effective and extendable business oriented service discovery.

    This specification, driven by few major actors of the IT world such as BEA, Microsoft, Intel, Canon etc…, defines a multicast discovery protocol to locate services. As a WS-* specification, it quickly becomes the de facto protocol when dealing with dynamic service discovery. The implementation presented bellow follows this specification and ensure a wide openness with existing platforms as well as the largest scope to provide the widest discoverability. The full WS-Discovery specification is available here.

    Maturity

    This specification is already widely available for device discovery thanks to UPnp, SSDP and Windows Rally initiative. Windows Vista uses this standard for discovering devices capabilities and offering or exposing relevant functionalities. For example, when displaying your network neighborhood, your residential gateway can expose its configuration page so Windows Vista can add an extra contextual menu allowing opening up the gateway’s configuration regardless of its actual IP address.

    Smart implementation

    This discovery process as outlined in the WS-Discovery specification follows the famous Publish - Subscribe pattern where a discoverable service joining the network sends a multicast Hello message notifying all the listening parties that it is available. In the same way, before shutting the service down, it notifies the same listening parties that it is leaving and will therefore not be available to serve requests anymore by sending a Bye message. During the time the service is available and discoverable, it is able to responds to discovery messages sent across the network, again following WS-Discovery specifications. This technical capability must not interfere with the business purpose of the service. Such a service cannot afford to take execution time to respond to discovery messages.

    Relayed and diffused infrastructure

    As mentioned above, a service cannot waste time servicing pure technical operations. Therefore, a dedicated mechanism must be setup to expose the discovery capability of the business service without interfering with the business value the service brings. In order to address that, a separate, purely technical, service is automatically created and started along with the business service. It runs as separate instance with the only purpose of exposing discovery and responding to discovery requests. The diagram bellow outlines the architecture put in place clearly separate business value from technical capability brought by WS-Discovery:

    As this diagram suggest, there is a clear separation between the business scope of the service and client and the discovery process and the discovery process never interfere with the business operations.

    Few messages exchanged

    WS-Discovery is based on very simple, yet powerful, set of messages. By offering Hello and Bye messages, the publish/subscribe pattern can be easily implemented. In conjunction with those two messages, come two types of messages allowing querying for discoverable services: Probe and Resolve.

    Identity card to extend

    The specification describes a common identity card specifying a set of common attributes that a discoverable service must expose. As described in the class diagram bellow, these are: AnyAttributes, AnyElelments, EndpointAddressAugust2004, MetadataVersion, Scopes, Types and XAddress.

    Extensibility driven

    Thanks to WS-Discovery specification openness (with AnyAttribute and AnyElements) the messages are endlessly extensible to cater any specific business requirements. One of the goals of the WS-Discovery implementation exposed here was to provide the most convenient extensibility model in order to allow developer to easily define custom, business oriented messages that provides a friendly business centric client side discovery mechanism. The class diagram bellow describes this architecture:

    As you can see, by inheriting from ServiceProperties class, it is trivial to declare a custom, business focused, specialization of it that exposes the geographical position of the service declared as discoverable. Using the same technique, messages can be specialized as EmergencySiteHello class suggests. Again, client side, the same technique is used to create business centric MatchCriteria that allows discovering services based on business attributes, see CallerMatchCriteria.

    Going further

    Microsoft Surface

    Such a technology could greatly extend the applicative discoverabilities of Microsoft Surface. Any application can easily take advantage of this discovery mechanism based on business or technical contracts as well as custom criteria thus react appropriately with devices or applications in range of the Surface.

    Going further than just a way to connect applications and devices, such a technology can open a broad new range of scenarios where opened applications could be modified or notified by discovered devices or services and provide a deeper interaction between human and machine.

    ESB Toolkit (Resolve UUID)

    A cornerstone of an enterprise service bus (ESB) is the service repository that it maintains. The notion of static and dynamic referencing address in this two part article can be directly applied to an ESB by taking advantage of both UDDI for the static service referencing and WS-Discovery for dynamic service discovery. The extra step that an ESB should offer is a dedicated proxy allowing taking advantage of WS-Discovery in order to provide a transparent communication initiation.

    Getting started with our implementation

    This article comes with a fully featured getting started kit walking you through two samples in order to get you started quickly with our implementation of WS-Discovery. The kit is available here

    Links

    The pdf version of this paper is here.
    The demo video is here.

    Format: wmv
    Duration: --:--

  • Acropolis NavigationManager Extensions

    1 - Introduction

    In my previous post, I said that Acropolis provide out of the box 2 kinds of Navigation Model : "SinglePartNavigation" and "MultiPartNavigationManager".

    In conclusion of my previous post, I have shortly described the limitations of these 2 navigation models, highlighting the fact that the Navigation between Components is hard coded on the application Design. Therefore, enforcing dependencies between Components.

    That's why, it is more convenient to build our own navigation engine. This means that it is required to understand Acropolis Components Life Cycle during an Acropolis Application execution.

    2 - Acropolis Application Life Cycle overview

    Acropolis Application starts with the "Microsoft.Acropolis.Windows.AcropolisApplication" class that inherits from WFP Application class. When it is initialized, it performs binding (BindToCollection) on both NavigationManager and LifetimeManager as children parts (ChildPartsCollection) as parameters.

    The diagram below shows you an overview of Acropolis Application Life Cycle, it is not intended to be detailled further in this post:

    3 - Manage User Navigation

     

    Acropolis introduces an extra level of abstraction between a "part" UI and its services with the notion of business contract represented by interfaces. Unfortenatly, this way is not followed by default by the Navigation Manager. Parts are stongly linked between them via the "NavigeTo" method.

    3.1 - Acropolis "Part" Life Cycle

    "Microsoft.Acropolis.PartFx.Part" is a not a Visual Component. It aims to provide a clean separation between "View" and "Business Behavior". So, how is a Part linked to a View ? The link is acheived by calling "BindToContract" method with "Microsoft.ACropolis.PartFx.IPartViewContract" as parameter. An instance of IPartViewContract is generated during Acropolis Project build, this instance inherits from "Microsoft.Acropolis.PartFx.PartViewContractTearOffBase".

    "Part" is being used when ChildPartsCollection is passed through "BindToObjectCollection" method of "NavigationManager". On top of that, Part has been attached on an instance of "Microsoft.Acropolis.ComponentModel.InstanceMoniker". What does it mean ? For the sake of simplicity, it's a kind of metadata for "Part" for my understanding. This instance of moniker is used to deal with NavigationManager to create "PartNavigationItem".

    Now, we know what happens behind the scene, so what can we do to make an abstraction between NavigationManager and Part ?

    In order to do that, we attach a new property named "Definition" (means as "Family") on the Part. Definition defines a set of Actions (click event on a button) that can be raised. Each Action defines one or more outputs where each one of them is associated to a Definition and a Zone where the output should be displayed.

    See below an overview of a XML fragment of Definition:

    <?xml version="1.0" encoding="utf-8"?>
      <Project>
        <NavigationItem>
          <Definition name="def1">
          <Action name="action1">
            <Output slot="slot1" partRef="FQN"/>
          </Action>
          <Action name="def2">
            <Output defRef="def10" slot="slot2" partRef="FQN"/>
            <Output defRef="def20" slot="slot1" partRef="FQN"/>
          </Action>
        </Definition>
        <Definition name="def10">
          <Action name="action1">
            <Output slot="slot2" partRef="FQN"/>
          </Action>
        </Definition>
        <Definition name="def20">
          <Action name="action1">
            <Output slot="slot1" partRef="FQN"/>
          </Action>
        </Definition>
      </NavigationItem>
      <SlotNavigationItem>
        <Item name="slot1"/>
        <Item name="slot2"/>
      </SlotNavigationItem>
    </Project>

    What is the difference bewteen Acropolis default NavigationManagers and UserActionNavigationManager? UserActionNavigationManager is fully driven by external XML data source.

    4 - UserActionNavigationManager

    In Acropolis, to provide our own NavigationManager we just have to inherit from "Microsoft.Acropolis.PartFx.NavigationManager". That's all. Of course, we need to override the following methods "BindToObjectCollection", "DetachFromObjectCollection" and "NavigateTo" to provide our own navigation logic regarding the XML fragment above.

    Remember, see below NavigationManager Life Cycle:

    UserActionNavigationManager aims to make the navigation to be dynamic and flexible without hard coding it in your code the navigation logic.

    4.1 - Reuse Components in different Scenario

    In a Software Industry, Applications must be composed of Components (not an unit of Control) that defines the developper's Unit of Work. Working on component allows developper to focus only on the business target.

    4.2 - Test User Navigation Scenario

    Who never said that testing application scenario can be a nigthmare ? UserActionNavigationManager might be an answer because, we can replay each Action without requiring the real application. Therefore, we can build navigation scenario with the XML fragment as datasource.

    5 - Demo Development

    Implementing UserAction Navigation Model on top of Acropolis is on track. The roadmap is not yet defined because it's depending on the next build of Acropolis. I have spent many hours to investigate on current build so I think, something might be changed. This theme was introduced during the session 5 of the .Net Entreprize Realization Day and will be developed further over the next months.

    The next post should be a screencast on "UserActionNavigationManager".

    Stay tuned... I will post regularly here my findings around Acropolis and more detailled papers on netfxfactory.

  • Service referencing part I: Business service cartography

    Introduction

    Not knowing precisely the services developed and deployed in an environment is not something anyone serious about service oriented architectures can afford. In this regards, business service cartography is a key pillar of SO architectures and valuable help to get insight on your business related developments. Being able to draw a map of your business, at any time, is a must to start capitalizing, industrializing and get enough insight to predict with maximum reliability the impact of change within an organization.

    Beneath the notion of business services cartography lays two main directions of work:

    The first one, which is the goal of the first part of this article, is driven by the necessity to formalize contractually business processes, services and operations and is seen as static referencing.

    The second one, which will be addressed in our next article, addresses the agility required by such a capability in the form of dynamic referencing.

    Static referencing

    The notion behind static referencing rests on the fact that by coupling a repository to a negotiation process you can have enough materials to build the first blocks towards contractual business services definition.

    When talking about services and repository, one of the first acronyms that come up is UDDI. Universal Description Discovery and Integration is the de-facto standard when it comes to web services repository. Because UDDI comes for free with any version of Windows Server 2003, it is an inexpensive way of building business repositories. Thanks to its very expandable categorization mechanism it is the ideal candidate to build your business service repository on. Even though, its API has its flaws, once you get your grip on the philosophy behind the concepts of tModels, Categories, Bindings and so on, you are ready to rumble. This is what will be covered later on in the implementation section of this paper. The business data being referenced within the UDDI repository has to be contextualized to reflect business cases.

    Once the repository issue is sorted, the next step is to find a streamlined way to formalize a negotiation process. When you mention business process and Microsoft in the same sentence, developers like us think about Microsoft Biztalk straight away. Even if the first versions had their draws, we must say that Microsoft Biztalk with 2006 release made a good step forward and more importantly in the right direction. It is therefore a safe bet to rely on Business Activity Services (BAS) and its trading partner management (TPM) feature to handle the negotiation process associated to the business repository. The negotiation phase outlined here is not about “magically” discovering new business partners but to effectively choose the most appropriate one for a particular situation. All these suppliers are already referenced within the information system and well known.

    In order to achieve the level of introspection, preciseness and completeness required to draw an effective cartography of your business, Microsoft released a new methodology, technologically agnostic, meant to help you conduct the tedious phase of defining and sometime discovering your business services cartography. This methodology, called Motion, is provided by Microsoft Consulting Services. This is far from being trivial to achieve but once completed it offers a very valuable source of data that helps you drive your business as well as quickly spot where to put your $$ on.

    Uddi

    Introduction

    UDDI is an industry wide initiative based on XML technology aiming at facilitating the publication and discovery of web services on the web. This initiative driven by OASIS has been pushed by many major actors of the IT world such as Microsoft, IBM, BEA, HP and so on. To accelerate its adoption, they quickly provided public repositories implementing this technology on the web. Unfortunately, even if it had the advantage o f showcasing the technology, those public repositories cannot be seen as the source of new commercial partnerships between companies. Indeed, it is not enough to remove the technological constraints to pretend to automate this process; UDDI is best seen as a facilitator. A similar effort to what has been done on the technological point of view has to be realized on the business side. Companies addressing the same market should come up with a common formalization of their business if they want to go further into the discovery process and follow the footsteps of business initiatives such as RosettaNet. Standardization requires a massive investment, this is why before targeting the WWW, it can be valuable to address a smaller scope that is the company and use UDDI private repositories.

    As a reminder, a UDDI repository is composed of:

    · White pages: similarly to phone books, they list company details, contact information and other company identifiers.

    · Yellow pages: they organize data published based on business information. These pages list all the business services.

    · Green pages: they regroup all the technical information related to the referenced business services.

    In a situation where it is about focusing on private repositories, the enterprise notion is replaced by the notion of departments or business units.

    As outlined earlier, this paper does not intend to address business classifications therefore we will focus on what we described earlier as the “Green pages” and how to publish such information in relation to Windows Communication Foundation (WCF).

    Service cartography

    Publishing services offered by each departments of a company does not come for free. However, for a reasonable investment a first step towards companywide service cartography is possible.

    Indeed, using private repositories will allow the company, once each department completed its publication process, to benefit from a central location with a description of each business services available within its information system.

    A solution exists to ensure that each department maintains the private repositories up to date are to include two new steps within the development cycles. The first one that precedes the development phase constrains the business analysts to, before writing down specifications of a new service, ensure that none is already available within the company by search through the UDDI repository. This is during this phase that the investment made on the business classification takes all its sense because the analysts will take into account the business value of the services as well as their usage context. The first obvious advantage of such as procedure is to promote capitalization and reuse.

    The second step is address during the deployment phase where it consists of publishing the new services developed. This task, accomplished by technicians, is all about publishing into the repository the business information provided by the analysts as well as the technical information related to the service such as its access points, security policies and so on.

    Service dependencies

    Departments within a company are not isolated from one another and usually do not work in an autonomous manner. Describing these dependencies can provide crucial information when it comes to address new business requests as well as modifying existing services offerings.

    By formalizing those dependencies, companies can foresee the impact of a particular modification companywide and more easily estimate its impact. UDDI offers specific elements to define dependencies between companies therefore departments. However, these elements cannot be used to describe service dependencies. The solution is therefore to define dedicated elements.

    Typical scenario

    This scenario which is illustrated in the video provided with this article demonstrates the whole process of referencing services, how they can be searched, published and the extra level of information it offers to developers. The scenario is inspired by the reference implementation provided by the Web Service Software Factory team.

    The action takes place at Global Bank where a new module needs to be implemented. This module requires getting customer related information as well as handling payments. Three personas from a typical development cycle are involved in this process.

    Business analyst

    He is responsible to check that no existing services are available prior start specifying as well as ensuring the relevance and accuracy of the business categorization information added to each WCF artifacts published in the UDDI repository. He is also responsible for defining the business context and business categorization to be applied to the services that he is specifying.

    Service developer

    The second actor involved is the service developer; he has the responsibility to develop the service as well as its publication into the UDDI repository. If business categorizations are already available, he can include them into the service configuration file in order to be picked up automatically when publishing the service. To do so, he uses the Visual Studio add in provided to publish directly from the IDE into UDDI (as shown in the video).

    Client developer

    Finally, the client developer can then use Visual Studio’s “add service reference” feature to search the UDDI repository and find the WCF service he needs and if WSDL information have been publishing let Visual Studio generate a proxy to communicate with the service.

    The last step in this process that is not shown in the video is that the business analyst, once the service is published into UDDI repository, will add all the business related categorization information to make this service discoverable easily to maximize its reuse.

    The video available with this article demonstrates the whole process: from the business analyst who first checks for existing services, then the service developer who publishes the new service into the UDDI repository and finally the client developer who uses UDDI to find services and their WSDL if any and just adds a service reference directly from Visual Studio.

    Going further

    Transactional context

    Most attentive will have noted that the actual version of UDDI does not offer a transactional context. Even if this lack will probably be addressed in the future UDDI release, there is no reason to constrains ourselves to not transactional publication. By migrating the UDDI web services to WCF we could easily take advantage of the built-in transactional support thus offering a more reliable experience when it comes to publishing information into the UDDI repository.

    Reporting

    As soon as the repository is in place and that the first services have been published, it becomes trivial to extract reports presenting the number of business services, what is the department publishing the most services, what is the most used security policy, does these critical services are all secured and so on…

    And why not expect a LINQ provider to ease the creation of the queries used to generate reports?

    Extend service publication

    It would be very useful to offer a mean to personalize the WCF binding representation mechanisms in the form of a categorization in order to take into account all its properties and not only Enum and bool. It could also be valuable to publish the WSDL document associated with a service by taking advantage of the import / export mechanisms offered by WCF. The first step could be that when loading the service description during the publication process, indicate in the WSDL/MEX address in the tModel’s overview document associated with the binding.

    Service deployment

    The deployment of services could be automated by developing dedicated actions within TFS team projects.

    Wrap-up

    The goal of this first part was to introduce the concept of business service cartography and how, with a minor investment, UDDI can address this problematic. The most expansive part of the business service cartography is not the server or the software to operate it but the level details that you are willing to put into the categorization of your business data. In the article, we propose our own interpretation of the WCF categorization and can be seen as a starting points to derivate it to suit your business description. This solution offers a technical vision of your information system in terms of WCF services and let you easily extract meaningful report as well as promoting reuse of existing services and capitalization instead of re developing the same services.

    The add in provided is also a starting points to integrate the business cartography into the development cycle by facilitating the publication process.

    However, be extremely careful, even if the most vicious of you could have thought about it, and even if all the information to do it is available, your UDDI repository is not intended to address dynamic discovery. As outlined in the introduction, UDDI is all about static referencing that can be automated but more importantly tightly linked to your development cycle. It is not about updating the repository information each and every time the service starts. The problematic of dynamic discovery will be covered in the next part of this article.

    Downloads

    Pdf version including technical implementation details is available here
    Visual Studio addin installer is available here
    Video is available here

     

     

    Format: wmv
    Duration: 15:34

  • An effective way to access WCF services

    Introduction

    This document introduces the concept of leveraging a service metadata in order to retrieve at runtime its endpoint configuration therefore avoiding duplicating the configuration information on both service and all its clients.

    If you ever had to deal with WCF, the first thing you probably heard about was "ABC" where A stands for Address, B for Binding and C for Contract. Well, thanks to the SmartChannelFactory, client side you are only required to known A and C because B is inferred from the service metadata.

    Concept

    WCF offers a very seamless way to handle service end point configuration. This is done via both the service configuration file and the client's. Specially in an intranet and even more in development environment, where you have to take care of both the cook and his clients, maintaining each and every client up to date with the latest service config can be quite tedious specially if you have a large number of clients to update. One way to overcome this problem is to retrieve the service's metadata in order to infer the services endpoints configuration instead of deploying via configuration file on each client.

    This is achieved via the MetadataResolver.Resolve(...) method, I won't drill in too much details about this method, the important thing to remember is that giving a service's wsdl address you get a ServiceEndpointCollection corresponding to the endpoints configuration defined in the service's config file. Thanks to this method, clients no longer requires to know at design time the exact configuration necessary to dialog with a WCF service. Only two pieces of information are required to start "talking" to a service. The first one is obviously the contract. Along with the contract, you will also need to know the address of the service; be careful here the address required here is not the endpoint address which is provided in the metadata but the address of the service's WSDL. Indeed, this WSDL document contains all the info required to configure the client endpoint(s).

    Global architecture

    Figure 1 bellow presents briefly the conceptual overview behind SmartChannelFactory as well as all the actors involved. As you can easely see each application has its own ServiceEndpointCollection repository and is in charge of requesting the metadata from the service and then ensures that the cached information is in sync with the service. This is done via polling at a defined interval rate (see "Service endpoint cache management" section for more details about the caching policy implemented). The client then uses the information straight from the repository in order to create dynamic proxy.

    Figure 1: Conceptual overview

    The next diagram shows an implementation overview of SmartChannelFactory. A couple of interesting things to note from this diagram:

    • SmartChannelFactory's GetBestEndPoint method is virtual allowing to provide custom Endpoint sorting mechanism,
    • ServiceEndPointCollectionRepository is a static class,
    • ServiceEndPointCollectionRepository background worker is used to poll and invalidate the cache if needs be.
    • Two events can invalidate the cache; first if the service is no longer accesible then the wsdl timestamp do not match (see "Service endpoint cache management" section about wsdl timestamp).

    Figure 2: Implementation diagram

    MetadataResolver

    The MetadataResolver class allows retrieving and importing service's metadata as a collection of ServiceEndPoint. The Resolve method, responsible for returning the ServiceEndpointCollection offers several signatures, the one I am interested in here takes 4 parameters described bellow:

    If you look at the ServiceEndpointCollectionRepository.Resolve method, you may wonder why did I added the following line:

    System.Net.WebRequest.Create(serviceMexAddress).GetResponse();

    As a matter of fact, this single line allows me to test if the service is available at the address provided, the exception catched allows me to handle nicely this eventuality.

    Best endpoint selection

    Some time ago, I wrote a post dealing with the same problem. This time the approach is a little different in the way the best endpoint is selected for a particular situation.


    Two things are important here:

    • First, the comparer Network.CompareServiceEndpointByZone which sorts the ServiceEndpoints based on the client's securityzone relative to the service
    • Then, the virtual method GetBestEndPoint allows you to define your own sorting algorithm if required.

    The code is pretty self explanatory but here is a short pseudo code description of what happens inside GetBestEndPoint:

    Foreach serviceEndPoint in ServiceEndPointCollectionRetreivedFromServiceMetadata 
    Get the serviceEndPoint's Uri 
    Get the serviceEndPoint's Security Zone based on its Uri structure 
    Add current serviceEndPoint the list of ServiceEndPoint if Securityzone is relevant 
    Sort the list of ServiceEndPoint using custom comparer 
    return 1st item of the list if any.
    

    Service endpoint cache management

    Because WCF does not force in anyway (and it is not its job) a caching mechanism of the WSDL document and because we cannot rely only on the fact that IIS can cache such a document, we need some kind of flag in the wsdl document that will let us know if the document has changed.

    Wsdl limitation

    WSDL standard offers no built-in way to timestamp the generated description document. This fact is quite anoying if you want a way to invalidate, client side, a cached representation of the ServiceEndPoint configuration. The idea here is on server side to plug-in custom code within the wsdl document generation process in order to insert a custom timestamp. Then client side, the ServiceEndPoints configuration client side along with the WSDL's timestamp is cached. Then using a background worker, the repository will check regularly if the server side wsdl timestamp changed in order to invalidate client's cache and retrieve the new ServiceEndPoints configuration if any.

    Workaround (server side)

    The workaround to add a timestamp to the wsdl is to cheat and use the documentation section of the wsdl. It is really easy to inject custom documentation to the wsdl generated thus using a custom section to define the timestamp is child play.

    Here are the 2 steps required to do so:

    1) implement IWsdlExportExtension

    2) implement IContractBehavior

    The IWsdlExportExtension interface exposes 2 methods, only one of them is interesting to us here, the method is called ExportContract. This method allows us to inject into the documentation section of the wsdl a custom xml.

    In our case the following section is added:

    <wsdl:documentation>
      <rd:contractWsdlTimestamp 
        timeStamp="2006-10-11 09:44:47Z" xmlns:rd="http://schema.rioterdecker.net/ServiceModel/Wsdl" />
    </wsdl:documentation>

    The other method exposed by the IWsdlExportExtension allows customizing the wsdl at Endpoint level which is no use to us in this case.

    The other interface, IContractBehavior, exposes 4 methods, none of them actually requires custom code to be written only the inheritance is required (you'll understand why a little bit further down this post).

    At this stage, you may wonder, well that all good but how do I add this functionality to my service? The answer is quite simple as well. Seat back, relax and think about what you see more and more often in .Net code... Open up Reflector, browse quickly a system*.dll assembly and you'll see several of them. OK you want a clue, here you go: ServiceContract, OperationContract, DataContract. Now you got it, attributes of course. The trick is to create a custom attribute that implements those interfaces and add it to the service Contract. You end up with something looking like this:

    [ServiceContract()]
    [WsdlContractTimestamp()] 
    public interface IService1 
    { 
      [OperationContract]
      string MyOperation1(string myValue); 
      [OperationContract] 
      string MyOperation2(DataContract1 dataContractValue); 
    } 

    Just by adding this custom attribute, you extend the wsdl generated in order to add a timestamp. Can't get much simpler...

    Client side usage

    Now that the wsdl generated for your service includes a timestamp, you need to extract it from the wsdl in an easy way. To do so, before importing the contract from the wsdl I add my custom importer to the WsdlImportExtensions collection. This custom importer is able to read the timestamp information out of the wsdl.

    So what does this WsdlContractTimestampImporter class? Well remember what we did to add the timestamp info... You got it, IWsdlExportExtension so we implemented the Ying, now we do the Yang: IWsdlImportExtension.

    IWsdlImportExtension exposes 3 methods: BeforeImport, ImportContract and ImportEndPoint. Same deal as we did server side, we only need to implement the ImportContract method to extract the timestamp info. The code is very simple so no need to drill in further. Am sure you're wondering, well how do I access this info? And why did we had to use the IContractBehavior interface server side? Eheh, in the ImportContract method you just have to extract the timestamp info out of the documentation section of the WSDL and then add it to the contract's behavior via the ImportContract context parameter (remember that the Wsdl timestamp attibute added to the contract inherits from IcontractBehavior). From this point, the timestamp info is accessible via the contract's behaviors whereever in your client code.

    The idea, client side, is to cache in a dictionary the service's uri (used as the key) and an object storing the timestamp and the associated ServiceEndpointCollection as the dictionary value. This allows retrieving the serviceEndpoints for a particular service thus not calling the MetadataResolver.Resolve each time. The DateTime key used in the dictionary value is the wsdl timestamp that is used to invalidate the cache entry if the timestamps do not match.

    Note: A simple way to save service resources once in production is to generate the wsdl once and save it as a static page and then let clients use this page as the service's metadata source. This avoids consuming service resources to generate the wsdl document over and over; however the drawback is the overhead to handle correctly the disposal of the static page in order to maintain metadata up to date.

    Transaction management

    WCF supports natively WS-AtomicTransaction (WS-AT) protocol through a couple of attributes at contract and service level (yet again) as well as binding configuration in order to allow transaction flow. By implementing the IEnlistmenentNotification interface, the SmartChannelFactory is able to participate actively to the transaction flow in order to take actions based, for instance, on CommunicationState or business logic. For example, if the client is closing the communication and there is an ambient transaction active then the transaction must be rolled back because the client won't be able to honor a commit order anyway, thus throwing an explicit exception will warn the user of the problem and trigger the transaction's rollback mechanism.

    Sample code

    Uri __baseAddress = new Uri(http://localhost:8081/service1?wsdl); 
    SmartChannelFactory<IService1> __channelFactory = new SmartChannelFactory<IService1>(__baseAddress)); 
    IService1 _IService1Proxy = __channelFactory.CreateChannel(); 
    String __result = _IService1Proxy.MyOperation1(DateTime.Now.ToString()); Console.WriteLine(__result);

    and the same call but with the wsdl address stored in the app.config file

    SmartChannelFactory<IService1> __channelFactory = new SmartChannelFactory<IService1>()); 
    IService1 _IService1Proxy = __channelFactory.CreateChannel(); 
    String __result = _IService1Proxy.MyOperation1(DateTime.Now.ToString()); Console.WriteLine(__result); 

    Going further

    With the Longhorn wave rolling-in and specially WCF, MS concretize widespread demand of dynamic proxy generation from wsdl, thus avoiding the necessary manual step of proxy generation in order to be able to communicate with any web service. Far from just solving this issue, MS leverages the .Net 2.0 generics to offer a strongly typed proxy factory. Can the old school react to this new breach? Anyway, we won't leave this flow unaddressed; check out the ProxyFactory project up on Codeplex.You'll probably see some similarities…

    Furthermore, with the proliferation announced by the simplicity of creating WCF services it will become crucial to provide a way to catalog and cartography existing services available in order to prop up service reusability. This can be achieved in numerous ways, the most widely known is to use a UDDI server to host the service description and categorization. The inquiring would apply to the SmartChannelFactory where the publishing would apply at service host level. So stay tuned to as this subject will be dealt with soon.

    A direction of improvement would be to provide a custom extended service host that would take care of the generation of each service's wsdl as well as the creation of the static page exposing the metadata for production environment and finally its disposal one required..

    Wrap-up

    Here is a small wrap-up of what has been covered here:

    • How to use server side configuration to infer client's using the MetadataResolver class,
    • How to select an endpoint based on the service address,
    • How to customize service wsdl,
    • How to execute custom actions on transaction events

    Downloads

    Requires NetFX3.0 RTM

    Pdf version is here

    Source code and sample application are here.

    Sample video

     

    Format: wmv
    Duration: 3:23

This Blog

Syndication

Powered by Community Server (Personal Edition), by Telligent Systems