• mwinkle.blog

    A Few Additional Treats @ PDC09

    • 1 Comments

    One fun thing we will be doing this year is using the theater in our lounge/booth area to have a few chalk style presentations.  These will be brief 30 minute demo/ q&a / conversation sessions with somebody from the product team.  These will dive a little deeper and be more interactive about topics we know you’re interested in.  We’d love you to come and just ask a ton of questions here:

    Thursday, 11:00-11:30 / Future Directions for State Machine Workflows  / Alan Ko

    Discuss options for using the State Machine modeling style on WF4, and see a demonstration of a WF4-based State Machine design experience.

    Thursday, 1-1:30 / Migrating WF 3.5 Workflows to WF 4 / Bob Schmidt

    Learn some tips and techniques for migrating your WF 3.5 workflows to the new WF4 runtime, and see a demonstration of a tool that helps automate this process.

    Thursday, 2:00-2:30 / WF Rehosting Deep Dive / Kushal Shah

    See how WF’s rehostable runtime, designer, and debugger can add powerful capabilities to your applications

    I’m excited because we’re going to use this as an opportunity to show some cool stuff that we’re thinking about.  Usual disclaimers apply, some of the stuff here we will show are prototypes, ideas we are looking for feedback, etc, etc.   These are things that we think are important, and your feedback helps us understand what you really need to make these things useful for you

    Stop on by the booth if you’re interested in any of the above topics!

  • mwinkle.blog

    Matt’s PDC Session List

    • 0 Comments

    I’m hoping that this PDC might be a little different and I may actually get to attend some sessions, rather than just prepping for mine (or the others from my team).  I’ve gone through all 22 pages of published talks, and I think there are some interesting ones.  So, without further ado, and with little, if any regard for actual scheduling of talks in relation to mine, here are the talks I would be interested in going to at PDC09.

    Hopefully I can get to 10 of them.

    Session Name

    Comment

    Building Data-Driven Applications Using Microsoft Project Code Name "Quadrant" and Microsoft Project Code Name "M" Doug’s had a few things to say recently, and I think this talk will be interesting.  My team was part of the Quadrant team for a while, and I’m curious to see what they’ve been up to.

    Windows 7 and Windows Server 2008 R2 Kernel Changes

    Anytime that you get to hear Mark talk about the kernel, it’s a great opportunity to learn a lot about a topic we don’t think every day.

    Code Visualization, UML, and DSLs

    The architecture tools team has done some really, really neat stuff to help you understand your code base better. I've found the tools to be really useful when looking at our code base, and I'd like to learn more here.

    Advanced Microsoft SQL Server 2008 R2 StreamInsight

    I've been watching StreamInsight since there was an internal talk on the technology. The capabilities here to do high capacity event stream queries is amazing. I think there are a number of interesting classes of problems where this can be useful and I'd like to find out more

    Introduction to Microsoft SQL Server 2008 R2 StreamInsight

     

    Dynamic Binding in C# 4

    This feature is one that I was excited to hear about in Anders' talk last year. A whole hour on how this works, that's just bliss

    Windows Error Reporting

    This kind of data is gold for folks who are building software.  You can’t catch every bug, or be aware of a video card incompatibility in a certain language on XP SP2, or always know why things might go wrong in your apps.  WER gives you a way to get that kind of data.

    How Microsoft Visual Studio 2010 Was Built with Windows Presentation Foundation 4

    This has been a huge undertaking, and hearing Paul talk about this will be insightful about the challenges faced, the way to integrate large, existing code bases with WPF, native WPF interop, etc.

    Windows Presentation Foundation 4 Plumbing and Internals

    I have a weak spot for deepdives into plumbing like this talk. This kind of knowledge is so useful for building apps on top of WPF to really understand how the pieces work together and what's happening at the low levels

    Future of Garbage Collection

    I got to sit next to Patrick at a dinner and had an amazing convesation that knocked my socks off. This is one of the guys who built the GC in .NET, and hearing the way he thinks will be interesting.

    Microsoft Perspectives on the Future of Programming

    Look at the list of this panel, how could you not want to hear this conversation? Just getting these folks together means there will be some interesting topics with lots of different backgrounds (from Jeffery Snover to Erik Meijer)

    REST Services Security Using the Access Control Service

    Justin is my former partner in crime from our days as technical evangelists, and I have lunch with him regularly and the stuff that he is working with is wicked cool. He's also one of the best presenters in the company, and there is always something I learn about presenting when I watch him talk.

    Data-Intensive Computing on Windows HPC Server with the DryadLINQ Framework

    I'd love to see anything that talks about "familiar declarative syntax of LINQ combined with the fault-tolerant distributed graph scheduling of the Dryad runtime"

    Building Sensor- and Location-Aware Applications with Windows 7 and the Microsoft .NET Framework 4.0

    The location API made me open up my first C++ project in a long time, the range of scenarios that this enables in Win7 is awesome

    Code Contracts and Pex: Power Charge Your Assertions and Unit Tests

    My first "from the labs" talk on the list, Pex and Code Contracts bring some really compelling capabilities to .NET development. This would be great to see.

    Microsoft Application Server Technologies: Present and Future

    This is from my team, and I'm excited to see the reaction to some of the cool stuff we will be talking about in the App Server space

    Rx: Reactive Extensions for .NET

    Erik Meijer is one of those guys I can't get enough of, I'd sign up for the fan club on channel9 if it were available.

    Building Amazing Business Applications with Microsoft Silverlight and Microsoft .NET RIA Services

    Brad is a really great presenter, and this is a whole space I have not had a chance to pay much attention to. I'd love to learn more about the ways to rapidly create business applications.

    SketchFlow: Prototyping to the Rescue

    Having just finished a project with a lot of designer/developer interaction, I have a lot of hope for things like SketchFlow.

    Developing REST Applications with the .NET Framework

    I like watching Don's talks, and REST is kind of a thing these days. Done deal.
  • mwinkle.blog

    Displaying Validation Errors in a Rehosted WF4 Designer

    • 1 Comments

    Here’s a quick one

    “I’d like to find out if the workflow in the designer is valid… is there someway to retrieve the validation errors?”

    Yes, the short answer is that you need to provide an implementation IValidationErrorService.  There is one important method to implement on this interface, ShowValidationErrors which will pass you a list of ValidationErrorInfo objects.

    The next thing that needs to happen is that you need to publish an instance of that new type to the editing context.

    Let’s look at a really simple implementation that will write out the validation errors to the debug log.

    using System.Activities.Presentation.Validation;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    
    namespace VariableFinderShell
    {
        class DebugValidationErrorService : IValidationErrorService
        {
            public void ShowValidationErrors(IList<ValidationErrorInfo> errors)
            {
                errors.ToList().ForEach(vei => Debug.WriteLine(string.Format("Error: {0} ", vei.Message)));
            }
        }
    }

    The final bit is to publish this to the editing context:

    wd.Context.Services.Publish<IValidationErrorService>(new DebugValidationErrorService());

    Now, when editing the workflow in the rehosted app, let’s introduce some errors and then look at the output window.  Note, most of the errors below come from incorrect expressions introduced in the expression editor (putting integers in the wrong place, wrong variable name, etc).

     

    image

  • mwinkle.blog

    Got a cool .NET app? Hang with Tortoises for 12 days

    • 0 Comments

    tortoise

    photo courtesy of flickr user mikeweston 

    This is a something that the .NET team is putting together that has a contest with super cool prizes (seriously, 12 day trip to the Galapagos, for real (as my 4 year old says)).  (obligatory legalese here). If you’ve built a sweet app with the .NET framework, we’d like to hear about it.   So check out http://www.myDotNetStory.com today and enter to win.

  • mwinkle.blog

    Finding the Variables in Scope Within the WF Designer

    • 0 Comments

    In this thread, one of our forum customers asked the question:

    “how do I find all of the variables that are in scope for a given activity in the designer?”

    We do not have a public API to do this.  Internally we have a helper type called VariableHelper that we use to display the list of variables in the variable designer, as well as passed into the expression text box intellisense control.

    image

    image

    One thing that I would like to point out is that if you are implementing your expression editor and want to be able to enumerate the variables (to highlight the tokens that are in scope), your implementation of IExpressionEditorService.CreateExpressionEditor() will get a list of model items that correspond to the inscope variables. 

    Now, if you are not building an expression editor, and want to be able to enumerate all of the in scope variables, you will need to do a little more work.  Here are the basic steps

    1. Go to parent container
    2. Does it contain variables*
    3. Does it have a parent?
    4. Go to parent’s parent
    5. Repeat from step 2

    * This is basically the tricky part, because it is not something we can cleanly determine (and it can also be altered at runtime by adding some additional ones via the CacheMetadata() method.  I’ll describe what the designer is currently doing to accumulate this list, and then talk about designs we could have in a future release.

    Current Approach

    Currently, there are two things that we look for when we navigate up the model item tree.

    1. Collections of Variables name Variables
    2. Variables injected by the use of ActivityAction

    As we walk up the tree, we need to find these.  Let’s first consider the following workflow we will use for our tests:

       1:  wd.Load(new Sequence
       2:  {
       3:      Activities =
       4:      {
       5:          new Sequence
       6:          {
       7:              Activities = 
       8:              {
       9:                  new ForEach<string>
      10:                  {
      11:                      Body = new ActivityAction<string>
      12:                      {
      13:                           Argument = new DelegateInArgument<string> { Name="foo" },
      14:                           Handler = 
      15:                              new Sequence
      16:                              {
      17:                                  Activities = 
      18:                                  {
      19:                                      new WriteLine()
      20:                                  }
      21:                              }
      22:                      }
      23:                  }
      24:              }
      25:          }
      26:      }
      27:  });

    Line 13 is the only one that might look a little different here, I’ll eventually have a post up about ActivityAction that talks in more detail what’s going on there.

    So, this loaded in a rehosted designer looks like the following:

    image  

    Now, let’s look at the code to figure out what is in scope of the selected element.  First, let’s get the selected element :-)

     Selection  sel = wd.Context.Items.GetValue<Selection>();
     if (sel != null)
     {
            ModelItem mi = sel.PrimarySelection;

    mi is now the model item of my selected item.  Let’s write some code to walk up the tree and add to a collection called variables

       1:  while (mi.Parent != null)
       2:  {
       3:      Type parentType = mi.Parent.ItemType;
       4:      if (typeof(Activity).IsAssignableFrom(parentType))
       5:      {
       6:          // we have encountered an activity derived type
       7:          // look for variable collection
       8:          ModelProperty mp = mi.Parent.Properties["Variables"];
       9:          if (null != mp && mp.PropertyType == typeof(Collection<Variable>))
      10:          {
      11:              mp.Collection.ToList().ForEach(item => variables.Add(item));
      12:          }
      13:      }
      14:      // now we need to look action handlers 
      15:      // this will ideally return a bunch of DelegateArguments
      16:      var dels = mi.Properties.Where(p => typeof(ActivityDelegate).IsAssignableFrom(p.PropertyType));
      17:      foreach (var actdel in dels)
      18:      {
      19:          if (actdel.Value != null)
      20:          {
      21:              foreach (var innerProp in actdel.Value.Properties)
      22:              {
      23:                  if (typeof(DelegateArgument).IsAssignableFrom(innerProp.PropertyType) && null != innerProp.Value)
      24:                  {
      25:                      variables.Add(innerProp.Value);
      26:                  }
      27:              }
      28:          }
      29:      }
      30:   
      31:   
      32:      mi = mi.Parent;
      33:  }

    Lines 4-13 handle the case where I just encounter an activity.  Lines 16-29 handle the slightly more tricky case where I need to handle ActivityAction (which ultimately derives from ActivityDelegate).  There are a few loops there but basically I look through all of the properties of the item which inherit from ActivityDelegate.  For each one of those, and for each property on that, I look for properties assignable from DelegateArgument.  As I find them I add them to my collection of model items for variables. 

    In my WPF app, I have a simple list box that shows all of these.  Because of the loosely typed data template I use in my WPF app, I get a pretty decent display since they share common things like ItemType from ModelItem, and a Name that routes through to the underlying Name property both elements share.

    image

    <ListBox Name="listBox1" Grid.Row="1" Grid.ColumnSpan="2" Grid.Column="0">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <TextBlock>Foo:</TextBlock>
                    <TextBlock FontWeight="Bold" Text="{Binding Path=ItemType}"/>
                    <TextBlock Text="{Binding Path=Name}"/>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

     

    Potential Future Approach

    Our approach outlined above is generally correct.  That said, you can imagine a deeply nested data structure on an activity that contain variables that get injected into the scope at runtime.  We need a way for an activity to provide a way to declare how to get all of its variables.  The current approach is one of inspection, which works for all of our activities. In the future we may need to add an extensibility point to allow an activity author to specify how to find the variables that are available to its children.  The way that we could do that is to introduce an “VariableResolver” attribute which points to a type (or maybe a Func<>) that operates on the activity and returns the list of variables.  Actually today you could likely introduce a custom type descriptor that lifted the variables out of the activity and surfaces them in such a way that they would be discovered by the inspection above. 

    Disclaimer: the Potential Future Approach is simply something that we’ve discussed that we could do, it does not represent something that we will do.  If you think that is an interesting extensibility point that you might want to take advantage of down the road, let me know. 

  • mwinkle.blog

    WF4 Beta1 => Beta2 Breaking Changes Document Published

    • 0 Comments

    Fresh on microsoft.com downloads, you can get the details of the major breaking changes that occurred for WF between Beta 1 and Beta 2.

    Get the document here.

    We will publish a similar document for any changes between Beta2 and RTM, although that list should be on the shorter side.  If you have feedback on the document, like the way something is presented or think we could have done a better job explaining it, please let me know.  Either comment here or use the “Email” link on the side.

  • mwinkle.blog

    Deep Dive into the WF4 Designer Data Model: ModelItem and ModelProperty

    • 0 Comments

    In this post I published an overview of the designer architecture.  I’ll copy the picture and the description of what I want to talk about today.

    image

    There are a few key components here

    • Source
      • In VS, this is xaml, but this represents the durable storage of the “thing” we are editing
    • Instance
      • This is the in memory representation of the item being edited.  In vs2010 for the WF designer, this is a hierarchy of System.Activities instances (an object tree)
    • Model Item Tree
      • This serves as an intermediary between the view and the instance, and is responsible for change notification and tracking things like undo state
    • Design View
      • This is the visual editing view that is surfaced to the user, the designers are written using WPF, which uses plain old data binding in order to wire up to the underlying model items (which represent the data being edited).
    • Metadata Store
      • This is a mapping between type and designers, an attribute table for lack of a better term.  This is how we know what designer to use for what type

    Motivating the ModelItem tree

    One observation that you could make when looking at the diagram above is “I should just be able to bind the view to the instance.”  This approach could work, but has a couple of implementation problems:

    • It is unrealistic to expect that instances of runtime types will all be built to fit perfectly into a design oriented world.  The most obvious problem is that our activity types aren’t going to inherit from DependencyObject, or implement INotifyPropertyChanged.  We can’t specify that all collections are ObservableCollection  [interesting trivia, ObservableCollection has moved from WindowsBase.dll into System.dll].  If we could, that would make life easy, but that’s not the case.  
      • Additionally, there are design time services that we need to think about supporting (such as Undo/Redo), and we need to make sure we can consistently handle this across many object types, including those that have not been written by us.
    • There may be a case for not actually operating on a live instance of the object graph.  Note, in VS2010, we do, but if we want to do work that would enable a design time XAML experience, we would need our instance to actually contain a lot of information about the source document. 
    • If we go directly from the view to the instance, we tightly couple the two together, and that makes doing more interesting things in the future tricky.  For instance, if we want to add refactoring support to update instances of objects, we need more than just the object graph to do that (the model item tree also keeps track of things like back pointers, so I know everybody that references the object). 

    These reasons cause us to think about an abstraction we can use to intermediate the implementation details of the instance and the view with a common layer.  If you have programmed at the WPF designer extensibility level, you will likely be familiar with the idea (and some of the types) here.

    The ModelItem tree

    The way that I think about the ModelItem/ModelProperty tree is that it forms a very thing proxy layer on top of the shape of the instance being edited. 

    Let’s start with a very simple type:

    public class Animal
    {
        // simple property
        public string Name { get; set; }
        // complex property 
        public Location Residence { get; set; } 
        // list 
        public List<Animal> CloseRelatives { get; set; }
        // dictionary
        public Dictionary<string, object> Features { get; set; } 
    }
    
    public class Location
    {
        public string StreetAddress { get; set; }
        public string City { get; set; }
        public string State { get; set; } 
    }

    Ignore for a moment that I just gave an animal Features, I’m a PM, it’s how we think :-)

    Now, let’s create some instances of that, and then actually create a ModelItem.

       1:  EditingContext ec = new EditingContext();
       2:  var companion1 = new Animal { Name = "Houdini the parakeet" };
       3:  var companion2 = new Animal { Name = "Groucho the fish" };
       4:  var animal = new Animal 
       5:                   {
       6:                       Name = "Sasha the pug",
       7:                       Residence = new Location 
       8:                       {
       9:                           StreetAddress = "123 Main Street",
      10:                           City = "AnyTown",
      11:                           State = "Washington"
      12:                       },
      13:                       Features = { 
      14:                          {"noise", "snort" },
      15:                          {"MeanTimeUntilNaps", TimeSpan.FromMinutes(15) }
      16:                       },
      17:                       CloseRelatives = { companion1, companion2 } 
      18:                   };
      19:  ModelTreeManager mtm = new ModelTreeManager(ec);  mtm.Load(animal);
      20:  ModelItem mi = mtm.Root;

    One thing to note here is that I am using ModelTreeManager and EditingContext outside the context (no pun intended) of the designer (see lines 1, 19, and 20 in the above snippet).  This isn’t the usual way we interact with these, but it’s for this sample so that we can focus just on the data structure itself. [as an aside, my brother did have a parakeet named Houdini]. 

    Let’s take a quick look at a visualization of what the data structure will look like.  Remember to think about the ModelItem tree as a thin proxy to the shape of the instance.

    Rather than spend an hour in powerpoint,  I’ll just include a sketch :-)

    IMAG0111

    On the left, you see the object itself.  For that object, there will be one ModelItem which “points” to that object.  You can call ModelItem.GetCurrentValue() and that will return the actual object. If you look at the ModelItem type, you will see some interesting properties which describe the object.

    •  ItemType is the type of the object pointed to (in this case, Animal)
    • Parent is the item in the tree which “owns” this model item (in the case of the root, this is null)
    • Source is the property that provided the value (in the case of the root, this is null)
    • Sources is a collection of all the backpointers to all of the properties which hold this value
      • Note, the distinction between Source and Sources and Parent and Parents is a topic worthy of another post
    • View is the DependencyObject that is the visual (in teh case above, this is null as there is no view service hooked into the editing context)
    • Properties is the collection of properties of this object

    Properties is the part where things get interesting.  There is a collection of ModelProperty objects which correspond to the shape of the underlying objects.  For the example above, let’s break in the debugger and see what there is to see.

    image

    As we might expect, there are 4 properties, and you will see all sorts of properties that describe the properties.  A few interesting ones:

    • ComputedValue  is a short circuit to the return the underlying object that is pointed to by the property.  This is equivalent to ModelProperty.Value.GetCurrentValue(), but has an interesting side effect that setting it is equivalent to SetValue().  
    • Name, not surprisingly, this is name of the property
    • PropertyType is the type of the property
    • Collection and Dictionary are interesting little shortcuts that we’ll learn about in a future blog post.
    • Value points to a model item that is in turn the pointer to the value
    • Parent points to the ModelItem which “owns” this property

    As Value points to another model item, you can see how this begins to wrap the object, and how this can be used to program against the data model.  Let’s look at a little bit of code. 

    root.Properties["Residence"].
                    Value.
                    Properties["StreetAddress"].
                    Value.GetCurrentValue()

    You might say “hey, that’s a little ugly”  and I have two bits of good news for you.

    1. ModelItem has a custom type descriptor, which means that in WPF XAML we can bind in the way we expect (that is, I can bind to ModelItem.Location.StreetAddress, and the WPF binding mechanism will route that to mi.Properties[“Location”].Value.Properties[“StreetAddress”].  So, if you don’t use C# (and just use XAML), you don’t worry about this
    2. In RTM, we will likely add support for the dynamic keyword in C# that will let you have a dynamic object and then program against it just like you would from WPF XAML.  It’s pretty cool and I hope we get to it, if we do I will blog about it.

    Here’s a set of tests which show the different things we’ve talked about:

     

       1:  ModelItem root = mtm.Root;
       2:  Assert.IsTrue(root.GetCurrentValue() == animal, "GetCurrentValue() returns same object");
       3:  Assert.IsTrue(root.ItemType == typeof(Animal),"ItemType describes the item");
       4:  Assert.IsTrue(root.Parent == null,"root parent is null");
       5:  Assert.IsTrue(root.Source == null, "root source is null");
       6:  Assert.IsTrue(((List<Animal>)root.Properties["CloseRelatives"].ComputedValue)[0] == companion1, 
       7:             "ComputedValue of prop == actual object");
       8:  Assert.IsFalse(((List<Animal>)root.Properties["CloseRelatives"].ComputedValue)[0] == companion2, 
       9:             "ComputedValue of prop == actual object");
      10:  Assert.AreEqual(root.Properties["Residence"].
      11:      Value.
      12:      Properties["StreetAddress"].
      13:      Value.GetCurrentValue(), "123 Main Street", "get actual value back out");
      14:  Assert.AreEqual(root, root.Properties["Residence"].Parent, "property points to owner");
      15:  ModelItem location = root.Properties["Residence"].Value;
      16:  Assert.AreEqual(root.Properties["Residence"], location.Source, "sources point to the right place");

    Oh my, won’t this get explosively large?

    Good question, and the truth is that yes, this could get large as you were to spelunk the object graph.  The good news is that we’re incredibly lazy about loading this, we will only flush out the properties collection on demand, and we won’t generate a ModelItem until it is requested.  When we combine this with the View virtualization work we have done, we will only ever load as much in the WF designer as you need.   This keeps the overhead minimal, and in does not represent a substantial memory overhead.

    Why should I be careful about ModelItem.GetCurrentValue()

    One might be tempted to just say “Hey, I’m in C#, I’ll just call GetCurrentValue() and party on that.  If you do that, you are entering dangerous waters where you can mess up the data model.  Since the underlying instance doesn’t likely support any change notification mechanism, the model item tree will get out of sync with the underlying instance description.  This will manifest itself in problems at the designer because our serialization is based off the instance, not the ModelItem tree (note, that’s a vs2010 implementation detail that could change in a subsequent release).  The net result though is that you will get your view out of sync with your instance and serialization and that’s generally considered problematic. 

    Summary

    Wow, that’s a longer post than I intended.  What have we covered:

    • The Modelitem tree and why we need it
    • The relationship between the underlying instance and ModelItem/ModelProperty
    • The shape and use of ModelItem / ModelProperty
    • Imperative programming against the tree

    What haven’t we covered yet

    • ModelItemCollection and ModelItemDictionary
    • How to use Sources and Parents to understand how the object sits in the graph and manipulation we can do for fun and profit

    I’ll get there.  In the meantime, if you have questions, let me know.

     

    **** Minor update on 10/29 to fix a bug in the code ****

    This is the way to use ModelTreeManager to generate ModelItems (Line 3 is the critical piece that was missing):

       1:  EditingContext ec = new EditingContext();
       2:  ModelTreeManager mtm = new ModelTreeManager(ec);
       3:  mtm.Load(new Sequence());
       4:  mtm.Root.Properties["Activities"].Collection.Add(new WriteLine());
  • mwinkle.blog

    WF4 Designer Bloggers

    • 2 Comments

    I wanted to put a quick note up as the team is starting to blog a little more frequently and putting some interesting content out there.  A few places to check out to hear from the folks on the team

     

    What blogs are you reading for WF4 content?  What kind of stuff would you like to hear more about?  Let us know!

  • mwinkle.blog

    Types, Metatypes and Bears, Redux

    • 1 Comments

    I made a quick post a few months back where I tried to talk about the way the designer works and lets us design types, as well as simply configure instances of types.

    There were a couple of key points that I wanted to make there in that post:

    • The workflow designer can configure instances of activity graphs, and create entire types as well
    • Types are designed by editing an instance of a type that represents the type being designed, the metatype
    • There is some XAML trickery required to serialize and deserialize this
    • This same type of work is done to enable the DynamicActivity capability

    A few folks have noticed (and sent me mail), that things look a little different in Beta2.  While we are going to have a more thorough, “here’s everything that changed” doc, I want to go ahead and update at least some of the things that I’ve been talking about here.

    What’s New

    In reality, very little is new, we’ve primarily moved stuff around now.  One thing that you may remember is that the DesignTimeXamlReader was not public in beta1, and if you are looking around, you may not find it.  We have made this functionality public however.  Thus, see the “what’s changed" bit.

    What’s Changed

    We took a long look at things and realized we had a bunch of XAML stuff all over the place.  We felt it would be a good idea to try to consolidate that into one place in WF, so System.Activities.XamlIntegration.ActivityXamlServices becomes your one stop shop for most things Activity and XAML related.  Let’s take a quick look and see what’s in there:

    ActivityXamlServices Members

    [This documentation is for preview only, and is subject to change in later releases. Blank topics are included as placeholders.]

    Creates an instance of an activity tree described in XAML.

    The ActivityXamlServices type exposes the following members.

    clip_image001[4] Methods

    Name

    Description

    clip_image002[10]clip_image003[10]

    CreateBuilderReader

    Overloaded. Maps an x:Class activity tree to an ActivityBuilder or ActivityBuilder<(Of <(TResult>)>).

    clip_image002[11]clip_image003[11]

    CreateBuilderWriter

    Maps an ActivityBuilder or ActivityBuilder<(Of <(TResult>)>) from the specified writer to an x:Class activity tree.

    clip_image002[12]clip_image003[12]

    CreateReader

    Overloaded. Maps an x:Class activity tree to an DynamicActivity or DynamicActivity<(Of <(TResult>)>).

    clip_image002[13]clip_image003[13]

    Load

    Overloaded. Creates an instance of a declarative workflow.

    Top

     

    Load is used to generally take some XAML and return an Activity which you can then use to execute.  If Load encounters a XAML stream for <Activity x:Class, it will subsequently generate a DynamicActivity.  This functions basically the same way WorkflowXamlServices.Load() did in beta1. 

    You also see CreateBuilderReader, and CreateBuilderWriter, which are used to surface the DesignTimeXaml capabilities that we used in beta1.  These will return an instance of a XamlReader/Writer that handles the transformation between the metatype and the <Activity x:Class XAML.   The metatype has changed names from ActivitySchemaType to ActivityBuilder.

    The table below should help summarize the uses and changes between beta1 and beta2.  In this area, I don’t expect any changes between what you see now, and what you will see in RTM.

    Task

    Beta1

    Beta2

    metatype (type to build types) ActivitySchemaType ActivityBuilder
    Mechanism to load DynamicActivity WorkflowXamlServices.Load() ActivityXamlServices.Load()
    Mechanism to load ActivityBuilder use WorkflowDesigner.Load() to get an ActivitySchemaType Use the reader from CreateBuilderReader() to pass into XamlServices.Load()
    Mechanism to save ActivityBuilder to XAML Create a new DesignTimeXamlWriter, pass that to XamlServices.Save() Use the writer returned from CreateBuilderWriter() to pass into XamlServices.Save()
         

    To explore this, use CreateBuilderReader() and XamlServices.Load() on a workflow that you’ve built in the designer and poke around a bit to see what’s going on.

    Here is some sample code that walks through this:

       1:  ActivityBuilder ab1 = new ActivityBuilder();
       2:  ab1.Name = "helloWorld.Foo";
       3:  ab1.Properties.Add(new DynamicActivityProperty { Name = "input1", Type = typeof(InArgument<string>) });
       4:  ab1.Properties.Add(new DynamicActivityProperty { Name = "input2", Type = typeof(InArgument<string>) });
       5:  ab1.Properties.Add(new DynamicActivityProperty { Name = "output", Type = typeof(OutArgument<string>) });
       6:  ab1.Implementation = new Sequence
       7:  {
       8:      Activities =
       9:      {
      10:          new WriteLine { Text = "Getting Started " },
      11:          new Delay { Duration = TimeSpan.FromSeconds(4) },
      12:          new WriteLine { Text = new VisualBasicValue<string> { ExpressionText= "input1 + input2" }},
      13:          new Assign<string> { To = new VisualBasicReference<string> { ExpressionText = "output" },
      14:                       Value = new VisualBasicValue<string> {ExpressionText= "input1 + input2 + \"that's it folks\"" } }
      15:      }
      16:   
      17:  };
      18:  StringBuilder sb = new StringBuilder();
      19:  StringWriter tw = new StringWriter(sb);
      20:  XamlWriter xw = ActivityXamlServices.CreateBuilderWriter(
      21:      new XamlXmlWriter(tw, new XamlSchemaContext()));
      22:  XamlServices.Save(xw , ab1);
      23:  string serializedAB = sb.ToString();
      24:   
      25:  DynamicActivity da2 = ActivityXamlServices.Load(new StringReader(serializedAB)) as DynamicActivity;
      26:  var result = WorkflowInvoker.Invoke(da2, new Dictionary<string,object> { {"input1","hello"}, {"input2", "world" }});
      27:  Console.WriteLine("result text is {0}", result["output"]);
      28:   
      29:   
      30:  ActivityBuilder ab = XamlServices.Load(
      31:      ActivityXamlServices.CreateBuilderReader(
      32:          new XamlXmlReader(new StringReader(serializedAB)))) as ActivityBuilder;
      33:   
      34:  Console.WriteLine("there are {0} arguments in the activity builder", ab.Properties.Count);
      35:  Console.WriteLine("Press enter to exit");
      36:  Console.ReadLine();

     

    Good luck, and happy metatyping!

  • mwinkle.blog

    Navigating the WF4 Beta 2 Samples

    • 2 Comments

    Hot off the presses (and the download center) come the WF4 Beta 2 samples here.  The team has invested a lot of time into these samples and they provide a good way to get up to speed on the way a particular feature or group of features work together.

    Note, there are 2300 files to be unzipped, so hopefully there is a sample in here for everyone.

    At a high level, we work down the directory structure from technology, sample type, and then some functional grouping of samples. 

    image

     

    Within the “Sample Type” we have a few different categories we use.

    • Basic
      • These are “one feature” samples that are used to illustrate how to use a given sample.  Often times they are hosted in the most basic wrapper necessary in order to get the feature to a point where it can be shown. 
      • These are grouped within feature level areas, a few  examples from the samples are:
        • \WF\Basic
          • \BuiltInActivities – how to configure and use the activities that are in the box
          • \CustomActivities
            • \CodeBodied\ – writing activities in code, including async, composite, and leveraging ActivityAction’s
            • \Composite – writing composite activities
            • \CustomActivityDesigners – writing activity designers
          • \Designer – programming against the infrastructure, a tree view designer, and a XamlWriter which will remove view state.
          • \Persistance
          • \Tracking
    • Scenario
      • These are higher level samples that require pulling together a number of features in order to highlight how the features can be combined to enable an application of the technology that might be of interest.  In the WF bucket, you will see things like Compensation and Services, which pull together a number of individual features
      • The ActivityLibrary folder is chock full of interesting sample activities that are useful for seeing how to write activities, as well as code that might be useful in your application.  Some of these are items which we aren’t shipping in the framework but may in the future.   Many of these also include interesting designers as well.  Some of the interesting sample activities in here:
    • Application
      • These samples are used to show how to pull everything together within the context of an application.  For instance, the WorkflowSimulator is described this way:
        • This sample demonstrates how to write a workflow simulator that displays the running workflow graphically. The application executes a simple flowchart workflow (defined in Workflow.xaml) and re-hosts the workflow designer to display the currently executing workflow. As the workflow is executed, the currently executing activity is shown with a yellow outline and debug arrow. In addition tracking records generated by the workflow are also displayed in the application window. For more information about workflow tracking, see Workflow Tracking and Tracing. For more information about re-hosting the workflow designer, see Rehosting the Workflow Designer.

          The workflow simulator works by keeping two dictionaries. One contains a mapping between the currently executing activity object and the XAML line number in which the activity is instantiated. The other contains a mapping between the activity instance ID and the activity object. When tracking records are emitted using a custom tracking profile, the application determines the instance ID of the currently executing activity and maps it back to the XAML file that instantiated it. The re-hosted workflow designer is then instructed to highlight the activity on the designer surface and use the same method as the workflow debugger, specifically drawing a yellow border around the activity and displaying a yellow arrow along the left side of the designer

    • Extensibility
      • This is a section inside the WCF samples that focuses on the various mechanisms and levels of extensibility

     

    I will be blogging more on some of the interesting (to me) individual samples.  What do you think?  Are there samples you’d like to see?  How are you using these, is there anything we can do to make these more useful?

  • mwinkle.blog

    WF4 Designer Enhancements in VS 2010 Beta 2

    • 2 Comments

    So, you might have heard that Beta2 has shipped.  I wanted to give a brief overview of the changes that have been made to the designer since beta1

    So, without further ado, here are my top 10 new designer features in Beta 2

    1. New UI
    2. Expand in place
    3. Choose Toolbox Items
    4. Imports designer
    5. Text on Flowchart labels
    6. IActivityTemplateFactory is public
    7. Namespace change
    8. WriteLine designer
    9. UI Virtualization
    10. Information about arguments in the property grid

    New UI

    We made a decision in Beta2 to do a bit of an overhaul on the designers general theme and feel in order to feel more like part of VS.  The UI we had in Beta1 was still the first pass at the designer surface that we showed last year in the first CTP at PDC.  Here are some screenshots of that new UI.  Our UX PM, Cathy, has started a blog, and I expect she will have some really interesting discussions about the design decisions that we made.

    image

    We’ve rounded out the set of icons as well

     image

    image

     

    We’ve also created focus around the header, and surface information within that header (like validation errors)

    image

    Expand in Place

    One of the biggest pieces of feedback that we got from the Beta1 release was the need to see what’s going on in my workflow.  The model we had in Beta1 was optimized around a few levels of composition, and required the user to double-click to drill into the activity.  This model works well to focus on an activity, but comes at the expense of the broader view.  We’ve gone ahead and changed that in beta2 that allows the workflow items to expand in place.

    I am jazzed that we did this, and there is no good way to put it in a picture.  Open up the designer and start playing around, and I think you’ll like what you’ll see.  The other nice part of this is that I can still choose to drill into if I want to have that focus.  This should also support fairly deep nesting of activities, meaning you can visualize large, complex workflows on the canvas. 

    Additionally, you can choose to expand or collapse individual nodes, or go to the “Expand All” command in order to expand out the tree.  One thing to note is that we don’t allow expansion inside of a flowchart because we don’t yet have a mechanism that lets me rearrange all of the items around that newly expanded activity. 

    Choose Toolbox Items

    If you have an assembly on disk somewhere that contains some activities, you can now add it into the toolbox through the Choose Items dialog.

    image

    image

    When you drop these new items on the canvas, the appropriate reference should be wired up for you.

    Imports Designer

    One thing that we heard from folks is that they like the vb expressions (well, they like the expressions, they aren’t super excited that we only have one language choice at this point), but they really didn’t like fully qualifying the type names.  To help out with this, we’ve introduced the Imports designer which lets you pick out the namespaces you want to import that will be used to make it easier to resolve types.

    Before:

    image

    Go to the Imports designer down at the bottom (next to Arguments and Variables) and add System.Xml

    image

    This will now bring in System.Xml into the expression text box:

    image

     

    Text on Flowchart Labels

    People seem to like the flowchart model, but one request we heard was that it was tough to see what all the lines meant, especially coming out of the branching constructs. I’ll show a few other interesting flowchart things here.

    First, hovering over a switch/decision will show the expression

    image

    Clicking on the small triangle will “pin” this expression so that I can see it while working on the flowchart.  Now, hook up some of the lines:

     

    image

    We also heard that the labels True and False are descriptive for the decision, they may not capture the intent.  So, you can click on the decision, open the property grid and set these to some custom value.

    image

    Switch will display the switch values as well.

    IActivityTemplateFactory is Public

    Towards the end of Beta1, we slipped a little feature in called IActivityTemplateFactory as an internal API to generate a configured set of activities (like what happens when I drop the messaging activity pairs).  We found a number of folks really liked this idea, so we have made this public.  This lets you add these factories to the toolbox to drop a templatized, configured set of activities onto the canvas.  A real simple one would look like [this drops a Pick with two branches, like what happens today when you drop a Pick]:

     

       1:  public sealed class PickWithTwoBranchesFactory : IActivityTemplateFactory
       2:  {
       3:      public Activity Create(DependencyObject target)
       4:      {
       5:          return new Pick
       6:          {
       7:              Branches =
       8:              {
       9:                  new PickBranch
      10:                  {
      11:                      DisplayName = "Branch1"
      12:                  },
      13:                  new PickBranch
      14:                  {
      15:                      DisplayName = "Branch2"
      16:                  }
      17:              }
      18:          };
      19:      }
      20:  }

     

    Namespace change

    We’ve made some OM changes (there have been some I’ll blog about later). One important one is that we needed to move out of the *.Design namespaces, primarily because *.Design is meant to specifically imply VS designer specific stuff, and our designer stuff is not VS specific.  So, we’ve moved from .Design to .Presentation as our namespace and assembly names of choice. There has also been some reduction in the number of namespaces, and moving some types out of the root namespace (System.Activities.Presentation) into more specialized namespaces (System.Activities.Presentation.Converters). 

    We’ve also reduced the number of assemblies from 3 to 2, and here is the way to think about them.

    • System.Activities.Presentation.dll => this is the core extensibility mechanism and the bulk of the “guts” of the designer
    • System.Activities.Core.Presentation.dll => this is primarily the assembly that contains the designers for the out of box activity designers.  There are some, but not too many extensibility / programmability points.
      • One thing to note is that the out of box designers have been made internal as there isn’t any extensibility built into those.

    Within System.Activities.Presentation.dll, here are the public namespaces:

    • System.Activities.Presentation
    • .Converters
    • .Debug
    • .Hosting
    • .Metadata
    • .Model
    • .PropertyEditing
    • .Services
    • .Toolbox
    • .Validation
    • .View

     

    Writeline Designer

    We had not originally planned for a designer for WriteLine, we had felt that the default designer was good enough for this.  We heard some feedback and one of the testers decided to put one together.  This makes it easy to configure the WriteLine on the canvas:

    image

    UI Virtualization

    This is a feature you may not see, or ever need to think about, but it’s one that I think is pretty neat, and helps us increase performance for large workflows.  We’ve built up some infrastructure inside the designer that supports virtualization of the UI surface.  The way to think about this is lazy loading the visuals of the designer until they are actually needed.  While this isn’t that useful for a small workflow, as a workflow starts to span multiple screens, it makes sense to only load the visual elements that are required for the workflow that are actually in view.  You may see artifacts of this if you open a large workflow as you see some of the workflow “draw on the fly.”  The advantages of doing this are that bigger workflows will open faster with fewer visual elements on the screen.  The information used by virtualization is coupled with the viewstate of the workflow in order to cache the size of the elements in order to reduce the need to do complex computations about size, position and overlap.

    Information About Arguments in the Property Grid

    Now, on hover we will display the information about the arguments and properties of the selected item, and additionally, we will pick up the System.ComponentModel.DesscriptionAttribute data as well.  The following code:

       1:  public sealed class MySimpleActivity : CodeActivity
       2:  {
       3:      [Description("This is a basic argument which will be used to compute the end value")]
       4:      public InArgument<string> Text { get; set; }
       5:   
       6:      protected override void Execute(CodeActivityContext context)
       7:      { ...

    will show up like this in the property grid:

    image

    Bonus Feature: Additional Toolbox Organization

    Rather than go with the “big flat list” approach, we’ve made the organization of items in the toolbox a little more granular.  This also shows some of the updated icons for the activities :-)

    image

    I’m really excited that beta2 is out the door, and I can’t wait to start hearing about folks using it.  I’ll start blogging a little more, and there are a few folks on my team that will start talking about some other interesting designer topics.

  • mwinkle.blog

    Introduction to WF Designer Rehosting (Part 2)

    • 3 Comments

    standard beta disclaimer.  This is written against the beta1 API’s.  If this is 2014, the bits will look different.  When the bits update, I will make sure to have a new post that updates these (or points to SDK samples that do)

    In yesterday’s post, we went over the core components of the designer.  Let’s now take that and build that rehosts the designer, and then we’ll circle back around and talk about what we did and what comes next.

    Start VS, Create a new project, and select a WPF project

    image

    Inside the VS project add references to the System.Activities.* assemblies.  For now, that list looks like

    • System.Activities.dll
    • System.Activities.Design.Base.dll
    • System.Activities.Design.dll
    • System.Activities.Core.Design.dll

    image

    You might think the list of design assemblies is excessive.  We’ll be collapsing probably into two design assemblies, one with the designer infrastructure and one with the activity designers in subsequent milestones.

    Create some layout in the WPF window to hold the various designer elements.  I usually do a three column grid for toolbox, property grid and designer canvas.

    The XAML for this looks roughly like this:

    <Window x:Class="BlogPostRehosting.Window1"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="Window1" Height="664" Width="831">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="1*"/>
                <ColumnDefinition Width="3*"/>
                <ColumnDefinition Width="2*"/>
            </Grid.ColumnDefinitions>
        </Grid>
    </Window>

    Now that we’ve got the layout down, let’s get down to business.  First let’s just get an app that displays the workflow designer and then we will add some other interesting features. We wanted to make it easy to get a canvas onto your host application, and to program against it.  The key type that we use is WorkflowDesigner, this encapsulates all of the functionality, and operating context, required.  Let’s take a quick look at the type definition

     

    Name Description
    Context Gets or sets an EditingContext object that is a collection of services shared between all elements contained in the designer and used to interact between the host and the designer. Services are published and requested through the EditingContext.
    ContextMenu Gets the context menu for this designer.
    DebugManagerView Provides a DebuggerServicethat is used for runtime debugging.
    PropertyGridFontAndColorData Sets the property grid font and color data.
    PropertyInspectorView Returns a UI element that allows the user to view and edit properties of the workflow.
    Text Gets or sets the XAML string representation of the workflow.
    View Returns a UI element that allows the user to view and edit the workflow visually.

     

    The editing context is where we will spend more time in the future, for now the View is probably what’s most interesting, as this is the primary designer canvas.  There are also some useful methods to load and persist the workflow as well.

    Let’s start off real simple, and write some code that will display a basic sequence, and we’ll get more sophisticated as we go along.

       1:  using System.Windows;
       2:  using System.Windows.Controls;
       3:  using System.Activities.Design;
       4:  using System.Activities.Core.Design;
       5:  using System.Activities.Statements;
       6:   
       7:  namespace BlogPostRehosting
       8:  {
       9:      /// <summary>
      10:      /// Interaction logic for Window1.xaml
      11:      /// </summary>
      12:      public partial class Window1 : Window
      13:      {
      14:          public Window1()
      15:          {
      16:              InitializeComponent();
      17:              LoadWorkflowDesigner();
      18:          }
      19:   
      20:          private void LoadWorkflowDesigner()
      21:          {
      22:              WorkflowDesigner wd = new WorkflowDesigner();
      23:              (new DesignerMetadata()).Register();
      24:              wd.Load(new Sequence 
      25:                              { 
      26:                                  Activities = 
      27:                                  {
      28:                                      new Persist(), 
      29:                                      new WriteLine()
      30:                                  }
      31:                              });
      32:              Grid.SetColumn(wd.View, 1);
      33:              grid1.Children.Add(wd.View);
      34:          }
      35:      }
      36:  }

    Let’s walk through this line by line:

    • Line 22, construct the workflow designer
    • Line 23, Call Register on the DesignerMetadata class.  Note that this associates all of the out of the box activities with their out of the box designers.  This is optional as a host may wish to provide custom editors for all or some of the out of box activities, or may not be using the out of box activities.
    • Line 24-31, Call Load, passing in an instance of an object graph to display.  This gives the host some flexibility, as this instance could come from XAML, a database, JSON, user input, etc.  We simply create a basic sequence with two activities
    • Line 32, set the column for the view
    • Line 33, add the view to the display

    This gives us the following application:

    image

    Now, that was pretty simple, but we’re also missing some key things, namely, the property grid.  It’s important to note however that this has all of the functionality of the designer (the variables designer, the overview map, etc.  This will react just the same as if you were building the workflow in VS. 

    Let’s add the property grid by adding the following two lines:

    Grid.SetColumn(wd.PropertyInspectorView, 2);
    grid1.Children.Add(wd.PropertyInspectorView);

    This will let us see the property grid (so things get a little more interesting).

    image

    So, we’re able to display the workflow and interact with it, but we probably also want to have a constrained authoring experience (not just editing), so that comes in the form of the ToolboxControl.  For the sake of this blog post, we’ll use this in XAML, but we certainly can code against it imperatively as well. 

    <Window x:Class="BlogPostRehosting.Window1"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:sad="clr-namespace:System.Activities.Design;assembly=System.Activities.Design"
            xmlns:sys="clr-namespace:System;assembly=mscorlib"
            Title="Window1" Height="664" Width="831">
        <Window.Resources>
            <sys:String x:Key="AssemblyName">System.Activities, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35</sys:String>
        </Window.Resources>
        <Grid Name="grid1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="1*"/>
                <ColumnDefinition Width="3*"/>
                <ColumnDefinition Width="2*"/>
            </Grid.ColumnDefinitions>
            <sad:ToolboxControl>
                <sad:ToolboxControl.Categories>
                    <sad:ToolboxCategoryItemsCollection CategoryName="Basic">
                        <sad:ToolboxItemWrapper AssemblyName="{StaticResource AssemblyName}" ToolName="System.Activities.Statements.Sequence"/>
                        <sad:ToolboxItemWrapper AssemblyName="{StaticResource AssemblyName}" ToolName="System.Activities.Statements.If"/>
                        <sad:ToolboxItemWrapper AssemblyName="{StaticResource AssemblyName}" ToolName="System.Activities.Statements.Parallel"/>
                        <sad:ToolboxItemWrapper AssemblyName="{StaticResource AssemblyName}" ToolName="System.Activities.Statements.WriteLine"/>
                        <sad:ToolboxItemWrapper AssemblyName="{StaticResource AssemblyName}" ToolName="System.Activities.Statements.Persist"/>
                    </sad:ToolboxCategoryItemsCollection>
                </sad:ToolboxControl.Categories>
            </sad:ToolboxControl>
        </Grid>
    </Window>

     

     

    This lets me specify the items I want to allow a user to drop.

    image

    The thing that is interesting to point out here is that we’ve built a full featured, constrained editor (with things like copy paste, undo/redo, etc) with not too much code.

    Next time, we’ll get into to doing some more interesting bits as well to interact with the item being edited, serialize to XAML, and explore the editing context some more.  Let me know what you think!

  • mwinkle.blog

    Introduction to WF Designer Rehosting (Part 1)

    • 1 Comments

    standard beta disclaimer.  This is written against the beta1 API’s.  If this is 2014, the bits will look different.  When the bits update, I will make sure to have a new post that updates these (or points to SDK samples that do)

     

    In WF3, we allowed our customers to rehost the WF designer inside their own applications.  This has many reasons, usually about monitoring a workflow or allowing an end user to customize a constrained workflow (visual construction of an approval process, for instance). This article became the gold standard for writing rehosted applications.  As we were planning our work for the WF4 designer, this was certainly a scenario we considered, and one we wanted to make easier. 

    This post consists of a few parts

    • Designer architecture – introduce the pieces, parts and terms we’ll use throughout
    • Simple rehosting – getting it up and running
    • What to do next

     

    Designer Architecture

    image

    There are a few key components here

    • Source
      • In VS, this is xaml, but this represents the durable storage of the “thing” we are editing
    • Instance
      • This is the in memory representation of the item being edited.  In vs2010 for the WF designer, this is a hierarchy of System.Activities instances (an object tree)
    • Model Item Tree
      • This serves as an intermediary between the view and the instance, and is responsible for change notification and tracking things like undo state
    • Design View
      • This is the visual editing view that is surfaced to the user, the designers are written using WPF, which uses plain old data binding in order to wire up to the underlying model items (which represent the data being edited).
    • Metadata Store
      • This is a mapping between type and designers, an attribute table for lack of a better term.  This is how we know what designer to use for what type

    I’ll go into more detail about these pieces and parts in future posts as well, but this is the mental model of the things I will be talking about as we go through the designer.

     

    Stay tuned, part 2 will come tomorrow!

  • mwinkle.blog

    Types, Metatypes and Bears, Oh my!

    • 1 Comments

    ***** UPDATE: Please see this post for how these features and functionality work in Beta2 *****

     

    Polar Bear

    image courtesy of flickr user chodhound

    This post comes about after a little conversation on the forums where I was talking about using the xaml stack save and load objects.

    Here’s what I said:

    Bob  (it's a little late in Seattle, so I don't have a code editor handy, so there may be some minor errors below),
    If you want to serialize any object graph to xaml, simply look at the XamlServices.Save() api that's in System.Xaml.  I'm sure there are a few SDK samples around that as well.  It takes in a file name to output to, a text writer or a stream, so you get to pick your poison for destination.  Similarly, if you want to get an object graph deserialized from Xaml, you can just use XamlServices.Load() again, with overloads for files, streams, text, xml, and xaml readers.
    To see this API, just do something like

    Sequence s = new Sequence { Activities = { new Persist(), new Persist() } };
    XamlServices.Save(fileNameVar, s);

    If you want to read, basically do the reverse.
    Save and Load are convinient helper functions to operate on the whole of the doc, there Xaml stack surfaces much more programmability and has a nice node stream style API that lets you plug in while nodes are being read and written.
    Now, if you want to deserialize a Xaml file that contains an x:Class directive, you are going to need to do a bit more work (and it depends what you want to serialize to or from).  I'll try to blog about that in the next week or so.

    Now, I want to take a little bit of time to explain the last part.

    A convenient way to think about XAML is a way to write down objects, really, instances of objects.  That said I am not limited to writing down just instances, I can actually write down type definitions as well.  I do this using the x:Class attribute. 

    Consider the following XAML snippet

    <Activity x:Class="foo" ...

    This is roughly equivalent to the following C#

    public class foo : Activity
    {
    ...

    Now, “normally” what happens with XAML like this in .xaml files is that it is used in a VS project and it is set up so that there is a build task who has the job of generating a type from that XAML so that you can use that type subsequently in your application.  This works basically the same way for WPF as well as WF.

    However, if you think about trying simply deserialize this, it’s a little confusing what this will actually deserialize to.  This is a type definition, so if you simply try to pass it to XamlServices.Load(), you will encounter an exception:

    Error System.Xaml.XamlObjectWriterException: No matching constructor found on type System.Activities.Activity. You can use the Arguments or FactoryMethod direct ives to construct this type. ---> System.MissingMethodException: No default constructor found for type System.Activities.Activity. You can use the Arguments or FactoryMethod directives to construct this type.
       at System.Xaml.Runtime.ClrObjectRuntime.DefaultCtorXamlActivator.EnsureConstructorDelegate(XamlType xamlType)
       at System.Xaml.Runtime.ClrObjectRuntime.DefaultCtorXamlActivator.CreateInstance(XamlType xamlType)
       at System.Xaml.Runtime.ClrObjectRuntime.CreateInstanceWithCtor(XamlType xamlType, Object[] args)
       at System.Xaml.Runtime.ClrObjectRuntime.CreateInstance(XamlType xamlType, Object[] args)
       --- End of inner exception stack trace ---

    So, if we want to deserialize that, we need to ask the question first: “Why do we want to deserialize it?”

    Deserialize to Execute

    If we want to simply use the activity we’ve created and have it execute, we have a special type, DynamicActivity.  DynamicActivity lets you execute the activity, and rather than creating a type for it, will allow you to pass in the arguments in the typical Dictionary<string,object> way that we are used to. 

    Imagine a xaml file, just sitting on disk somewhere that looks like this (a workflow that concats two strings): 

    <p:Activity mc:Ignorable=""
        x:Class="WorkflowConsoleApplication1.Sequence1" 
         xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities/design"
         xmlns:__Sequence1="clr-namespace:WorkflowConsoleApplication1;" 
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:p="http://schemas.microsoft.com/netfx/2009/xaml/activities" 
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
      <x:Members>
        <x:Property Name="argument1" Type="p:InArgument(x:String)" />
        <x:Property Name="argument2" Type="p:InArgument(x:String)" />
      </x:Members>
      <p:Sequence>
        <p:WriteLine>[argument1 + " " + argument2]</p:WriteLine>
      </p:Sequence>
    </p:Activity>

    The code for this is the following:

    object o = WorkflowXamlServices.Load(File.OpenRead("Sequence1.xaml"));
    Console.WriteLine("Success {0}", o.GetType());
    DynamicActivity da = o as DynamicActivity;da.Properties.ToList().ForEach(ap => Console.WriteLine("argument: {0}", ap.Name));
    WorkflowInvoker.Invoke(da, new Dictionary<string, object> { { "argument1", "foo" }, { "argument2", "bar" } });

    Deserialize to “Design”

    At design time, we have an even more interesting problem.  Our designer is an instance editor, and, as such, it must always edit an instance of “something”.  In our case, we actually do some work in our design time xaml reader and writer to deserialize into a metatype, an instance of a type whose sole purpose in life is to describe the activity.  In Beta1, this is called ActivitySchemaType.  ActivitySchemaType simply models the type structure of an activity, complete with properties, etc  If you want to construct an instance of an ActivitySchemaType in code, you can, and then you could use the DesignTimeXamlWriter in order to properly serialize out to an Activity x:class xaml file.  The following code works on an ActivitySchemaType in memory and then serializes it:

    XamlSchemaContext xsc = new XamlSchemaContext();
    ActivitySchemaType ast = new ActivitySchemaType()
    {
        Name = "foo",
        Members = 
         {
            new Property { Name="argument1", Type = xsc.GetXamlType(typeof(InArgument<string>)) }
    
         },
        Body = new Sequence { Activities = { new Persist(), new Persist() } }
    };
    StringBuilder sb = new StringBuilder();
    
    DesignTimeXamlWriter dtxw = new DesignTimeXamlWriter(
        new StringWriter(sb),
        xsc, "foo", "bar.rock");
    
    XamlServices.Save(dtxw, ast);
    Console.WriteLine("you wrote:");
    ConsoleColor old = Console.ForegroundColor;
    Console.ForegroundColor = ConsoleColor.DarkGray;
    Console.WriteLine(sb.ToString());
    Console.ForegroundColor = old;

    This is what the output looks like:

    <p:Activity mc:Ignorable=""
         x:Class="foo" 
         xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities/design" 
         xmlns:__foo="clr-namespace:bar.rock;" 
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:p="http://schemas.microsoft.com/netfx/2009/xaml/activities" 
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
      <x:Members>
        <x:Property Name="argument1" Type="p:InArgument(x:String)" />
      </x:Members>
      <p:Sequence>
        <p:Persist />
        <p:Persist />
      </p:Sequence>
    </p:Activity>

    If you want to read in from this, you have to do a little bit of trickery with the designer as DesignTimeXamlReader is not a public type in beta1. 

    WorkflowDesigner wd = new WorkflowDesigner();
    wd.Load("Sequence1.xaml");
    object obj = wd.Context.Services.GetService<ModelService>().Root.GetCurrentValue();
    Console.WriteLine("object read type: {0}", obj.GetType());
    ActivitySchemaType schemaType = obj as ActivitySchemaType;
    Console.WriteLine("schema type name: {0}", schemaType.Name);
    schemaType.Members.ToList().ForEach(p => Console.WriteLine("argument: {0}, type: {1}", p.Name, p.Type));

    That wraps up our tour of the ways to read (and write)  <Activity x:Class xaml

    Here’s the full text of the little program I put together to execute this, also make sure to drop a sequence1.xaml into your execution directory if you want this to not throw :-)


    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Xaml;
    using System.Activities.Statements;
    using System.Activities;
    using System.IO;
    using System.Activities.Design.Xaml;
    using System.Windows.Markup;
    using System.Xml;
    using System.Activities.Design;
    using System.Activities.Design.Services;
     
    namespace ConsoleApplication2
    {
        class Program
        {
            [STAThread()]
            static void Main(string[] args)
            {
                bool doStuff = true;
                Guid g = Guid.NewGuid();
                while (doStuff)
                {
                    Console.WriteLine();
                    Console.WriteLine("What do you want to do?");
                    Console.WriteLine("    read [x]:class xaml with XamlServices");
                    Console.WriteLine("    read x:class xaml with W[o]rkflowXamlServices");
                    Console.WriteLine("    [w]rite an ActivitySchemaType");
                    Console.WriteLine("    r[e]ad an ActivitySchemaType");
                    Console.WriteLine("    [q]uit");
                    Console.WriteLine();
                    char c = Console.ReadKey(true).KeyChar;
                    switch (c)
                    {
                        case 'w':
                            XamlSchemaContext xsc = new XamlSchemaContext();
                            ActivitySchemaType ast = new ActivitySchemaType()
                            {
                                Name = "foo",
                                Members = 
                                 {
                                    new Property { Name="argument1", Type = xsc.GetXamlType(typeof(InArgument<string>)) }
     
                                 },
                                Body = new Sequence { Activities = { new Persist(), new Persist() } }
                            };
                            StringBuilder sb = new StringBuilder();
                            
                            DesignTimeXamlWriter dtxw = new DesignTimeXamlWriter(
                                new StringWriter(sb),
                                xsc, "foo", "bar.rock");
                            
                            XamlServices.Save(dtxw, ast);
                            Console.WriteLine("you wrote:");
                            ConsoleColor old = Console.ForegroundColor;
                            Console.ForegroundColor = ConsoleColor.DarkGray;
                            Console.WriteLine(sb.ToString());
                            Console.ForegroundColor = old;
                            break;
     
                        case 'x':
                            try
                            {
                                object o = XamlServices.Load(File.OpenRead("Sequence1.xaml"));
                                Console.WriteLine("Success{0}", o.GetType());
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine("Error {0}", ex);
                            }
                            break;
                        case 'o':
                            try
                            {
                                object o = WorkflowXamlServices.Load(File.OpenRead("Sequence1.xaml"));
                                Console.WriteLine("Success {0}", o.GetType());
                                DynamicActivity da = o as DynamicActivity;
                                da.Properties.ToList().ForEach(ap => Console.WriteLine("argument: {0}", ap.Name));
                                WorkflowInvoker.Invoke(da, new Dictionary<string, object> { { "argument1", "foo" }, { "argument2", "bar" } });
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine("Error {0}", ex);
                            }
                            break;
                        case 'e':
                            WorkflowDesigner wd = new WorkflowDesigner();
                            wd.Load("Sequence1.xaml");
                            object obj = wd.Context.Services.GetService<ModelService>().Root.GetCurrentValue();
                            Console.WriteLine("object read type: {0}", obj.GetType());
                            ActivitySchemaType schemaType = obj as ActivitySchemaType;
                            Console.WriteLine("schema type name: {0}", schemaType.Name);
                            schemaType.Members.ToList().ForEach(p => Console.WriteLine("argument: {0}, type: {1}", p.Name, p.Type));
                            break;
                        case 'q':
                            doStuff = false;
                            break;
     
                        default:
                            break;
                    }
     
                }
                Console.WriteLine("All done");
                Console.ReadLine();
            }
     
            private static object CreateWfObject()
            {
                return new Sequence { Activities = { new Persist(), new Persist() } };
            }
        }
    }
  • mwinkle.blog

    Introducing the WF4 Designer

    • 6 Comments

    // standard disclaimer applies, this is based on the released Beta 1 bits, things are subject to change, if you are reading this in 2012, things may be, look, smell, work differently.  That said, if it’s 2012 and you’re reading this, drop me a line and let me know how you found this!

     

    As you might have heard, Beta1 of VS is out the door, and available to the public sometime today.  As you may know we’ve done a bunch of work for WF4, and I wanted to give a quick, high level overview of the designer.  Here’s a good overview for the new WF bits all up.

    First, let’s start with your existing WF projects.  What happens if I want to create a 3.5 workflow?  We’re still shipping that designer, in fact, let’s start there on our tour.  This shows of a feature of VS that’s  pretty cool, multitargeting. 

    Click New Project

    image

    Notice the “Framework Version” dropdown in the upper right hand corner.

    image

    This tells VS which version of the framework you would like the project you are creating to target.  This means you can still work on your existing projects in VS 2010 without upgrading your app to the new framework.  Let’s pick something that’s not 4.0, namely 3.5.  You’ll note that the templates may have updated a bit, select Workflow from the left hand tree view and see what shows up.

    image

    There isn’t anything magical about what happens next, you will now see the 3.5 designer inside of VS2010.  You’re able to build, create, edit and update your existing WF applications. 

    image

    Let’s move on and switch over to a 4.0 workflow.

    Create a new project and select 4.0

    image

    Create a new WF Sequential Console application and name it “SampleProject”.  Click Ok.

    We’ll do a little bit of work here, but you will shortly see the WF 4.0 designer.  It looks a little different from the 3.x days, we’ve taken this time to update the designer pretty substantially.  We’ve built it on top of WPF, which opens up the doors for us to do a lot of interesting things.  If you were at PDC and saw any Quadrant demos, you might think that these look similar. We haven’t locked on the final look and feel yet, so expect to see some additional changes there, but submit your feedback early and often, we want to know what you think. 

    image

    Let’s drop some activities into our sequence and see what’s there to be seen.

    image

    We’ve categorized the toolbox into functional groupings for the key activities.  We heard a lot of feedback that it was tough to know what to use when, so we wanted to provide a little more help with some richer default categories.  Add an Assign activity, a WriteLine activity and a Delay activity to the canvas by clicking and dragging over the to the sequence designer.

    image

    You’ll note that we’ve now got some icons on each activity indicating something is not correct.  This is a result of the validation executing and returning details about what is wrong.  Think of these as the little red squiggles that show up when you spell something wrong.  You can hover over the icon to see what’s wrong

    image

    You can also see that errors will bubble up to their container, so hovering over sequence will tell you that there is a problem with the child activities.

    image

    What if I have a big workflow, and what if I want to see a more detailed listing of errors?  Open up the Error View and you will see the validation results are also displayed here.  You’ll note there is some minor formatting weirdness.  This is a bug that we fixed but not in time for the Beta1 release.

    image

    Now, let’s actually wire up some data to this workflow.  WF4 has done a lot of work to be much more crisp about the way we think about data within the execution environment of a workflow. We divide the world into two types of data, Arguments, and Variables.  If you mentally map these to the way you write a method in code (parameters, and state internal to the method), you are one the right track.  Arguments determine the shape of an activity, what goes in, what goes out.  Variables allocate storage within the context of an activities execution.  The neat thing about variables, once the containing activity is done, we can get rid of the variables, as our workflow no longer needs them (note, we pass the important data in and out through the arguments).  To do this, we have two special designers on the canvas that contain information about the arguments and variables in your workflow

    image

    First, let’s click on the Argument designer and pass in some data. 

    Arguments consist of a few important elements

    • Name
    • Type
    • Direction
    • Default Value (Optional)

    image

    Most of these are self explanatory, with the one exception being the Direction.  You’ll note that this has In, Out and Property.  Now, when you are editing the arguments, you are actually editing the properties of the underlying type you are creating (I’ll explain more about this in a future post).  A more appropriate name might be “Property Editor” but the vast majority of what you’ll be creating with it is arguments.  Anyway, If you select In or Out, this basically wraps the type T in an InArgument, so it becomes a property of type InArgument<T>.  We just provide a bit of a shorthand so you don’t always have to pick InArgument as the type.  The default value takes an expression, but in this case, we won’t be using it.

    Let’s go ahead and add an argument of type TimeSpan named DelayTime.  You’ll need to select browse for types and then search for the TimeSpan

    image

    Variables are similar, but slightly different, variables have a few important elements:

    • Name
    • Type
    • Scope
    • Default Value (Optional)

     

    Remember earlier, I mentioned that variable is part of an activity, this is what Scope refers to.  Variables will only show up to be the scope of the selected activity, so if you don’t see any, make sure to select the Sequence, and then you will be able to add a variable.  Let’s add new variable, named StringToPrint, of type String. 

    image

    Now let’s do something with these in the workflow.  One thing I’m particularly happy with that we’ve done on the designer side of things is to enable people to build activity designers more easily.  There are lots of times where you have activities that have just a few key properties that need to be set, and you’d like to be able to see that “at a glance”  The assign designer is like that.

    image

    Now, let’s dig into expressions.  One big piece of feedback from 3.0 was that people really wanted richer binding experiences.  You see this as well with the WPF data binding .  We’ve taken it to the next level, and allow any expression to be expressed as a set of activities.  What this means is that we do have to “compile” textual expressions into a tree of activities, and this is one of the reasons we use VB to build expressions.  In the fullness of time, other languages will come on board. But how to use it, let’s see.  Click on the “To” text box on the Assign activity.  You will see a brief change of the text box, and then you will be in a VB Expression Editor, or what we’ve come to refer to as “The Expression Text Box” or ETB.  Start typing S, and already you will see intellisense begin to scope down the choices.  This will pick up all of the variables and arguments in scope. 

    image

    On the right side, we won’t use any of the passed in arguments, we’ll show off a richer expression.   Now, the space on the right side of the designer is kind of tight for something lengthy, so go to the property grid and click on the “…” button for the Value property

    image

    String.Format("Someone wants to wait {0} seconds", TimeToWait.TotalSeconds)

    This just touches the surface of what is possible with expressions in WF4, we can really get  much richer expressions (3.x expressions are similar to WPF data binding, they are really an “object” + “target” structure).

    Not everything makes it to the canvas of the designer surface, and for that, we have the property grid.  If you’ve used the WPF designer in VS2008, this should look pretty familiar to you.  Select the delay activity, and use the property grid to set the duration property to the InArgument you created above.  This experience is similar, with the ETB embedded into the property grid for arguments.

    Finally, repeat with the WriteLine and bind to the StringToPrint variable.

    Navigating the Workflow

    There are two different things that we have to help navigating the workflow, our breadcrumb bar at the top and the overview map (which appears as the “Mini Map” in the beta).  Let’s look at the overview map.  This gives you a view of the entire workflow and the ability to quickly scrub and scroll across it.

     

    image

    Finally, across the top we display our breadcrumbs which are useful when you have a highly nested workflow.  Double click on one of the activities, and you should see the designer “drill into” that activity.  Now notice the breadcrumb bar, it displays where you have been, and by clicking you can navigate back up the hierarchy.  In beta1, we have a pretty aggressive breadcrumb behavior, and so you see “collapsed in place” as the default for many of our designers.  We’re probably going to relax that a bit in upcoming milestones to reduce clicking and provide a better overview of the workflow.

    image

    Finally, there may be times where we don’t want to have a designer view, but would rather see the XAML.  To get there, just right click on the file and ask to “View Code”

    image

    This will currently ask you if you are sure that you want to close the designer, and you will then see the XAML displayed in the XML editor.  For the workflow we just created, this is what it looks like:

     

       1:  <p:Activity mc:Ignorable=""
       2:       x:Class="WorkflowConsoleApplication1.Sequence1" 
       3:       xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities/design"
       4:       xmlns:__Sequence1="clr-namespace:WorkflowConsoleApplication1;" 
       5:       xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
       6:       xmlns:p="http://schemas.microsoft.com/netfx/2009/xaml/activities"
       7:       xmlns:s="clr-namespace:System;assembly=mscorlib"
       8:       xmlns:sad="clr-namespace:System.Activities.Debugger;assembly=System.Activities" 
       9:       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
      10:    <x:Members>
      11:      <x:Property Name="TimeToWait" Type="p:InArgument(s:TimeSpan)" />
      12:    </x:Members>
      13:    <__Sequence1:Sequence1.TimeToWait>
      14:      <p:InArgument x:TypeArguments="s:TimeSpan">[TimeSpan.FromSeconds(10)]</p:InArgument>
      15:    </__Sequence1:Sequence1.TimeToWait>
      16:    <p:Sequence>
      17:      <p:Sequence.Variables>
      18:        <p:Variable x:TypeArguments="x:String" Name="StringToPrint" />
      19:      </p:Sequence.Variables>
      20:      <p:Assign>
      21:        <p:Assign.To>
      22:          <p:OutArgument x:TypeArguments="x:String">
      23:              [StringToPrint]
      24:          </p:OutArgument>
      25:        </p:Assign.To>
      26:        <p:Assign.Value>
      27:          <p:InArgument x:TypeArguments="x:String">
      28:                    [String.Format("Someone wants to wait {0} seconds", TimeToWait.TotalSeconds)]
      29:           </p:InArgument>
      30:        </p:Assign.Value>
      31:      </p:Assign>
      32:      <p:Delay>[TimeToWait]</p:Delay>
      33:      <p:WriteLine>[StringToPrint]</p:WriteLine>
      34:    </p:Sequence>
      35:  </p:Activity>

     

    Executing the Workflow

    Inside the project you will see the Program.cs to execute the workflow, let’s take a look at that.

     

       1:  namespace WorkflowConsoleApplication1
       2:  {
       3:      using System;
       4:      using System.Linq;
       5:      using System.Threading;
       6:      using System.Activities;
       7:      using System.Activities.Statements;
       8:      using System.Collections.Generic;
       9:   
      10:      class Program
      11:      {
      12:          static void Main(string[] args)
      13:          {
      14:              AutoResetEvent syncEvent = new AutoResetEvent(false);
      15:   
      16:              WorkflowInstance myInstance =
      17:                  new WorkflowInstance(new Sequence1(),
      18:                      new Dictionary<string, object>
      19:                      {
      20:                          {"TimeToWait", TimeSpan.FromSeconds(3.5) }
      21:                      }
      22:   
      23:   
      24:   
      25:                      );
      26:              myInstance.OnCompleted = delegate(WorkflowCompletedEventArgs e) { syncEvent.Set(); };
      27:              myInstance.OnUnhandledException = delegate(WorkflowUnhandledExceptionEventArgs e)
      28:              {
      29:                  Console.WriteLine(e.UnhandledException.ToString());
      30:                  return UnhandledExceptionAction.Terminate;
      31:              };
      32:              myInstance.OnAborted = delegate(WorkflowAbortedEventArgs e)
      33:              {
      34:                  Console.WriteLine(e.Reason);
      35:                  syncEvent.Set();
      36:              };
      37:   
      38:              myInstance.Run();
      39:   
      40:              syncEvent.WaitOne();
      41:              Console.WriteLine("Press <Enter> to exit");
      42:              Console.ReadLine();
      43:   
      44:          }
      45:      }
      46:  }

     

    This is the standard program.cs template with two modifications.  The first is passing data into the workflow, indicated by the dictionary we create to pass into the WorkflowInstance.  This should look familiar if you have used WF in the past.

     WorkflowInstance myInstance =
                    new WorkflowInstance(new Sequence1(),
                        new Dictionary<string, object>
                        {
                            {"TimeToWait", TimeSpan.FromSeconds(3.5) }
                        }
                     );

     

    The second is a break at the end to keep our console window open (lines 41 and 42).  Hitting F5 from our project results in the following output (as expected).

    image

    This concludes our brief tour through the new WF designer.  I’ll be talking a lot more in upcoming days about some of the more programmatic aspects of it and how it’s put together.

  • mwinkle.blog

    10-4 Good Buddy, First Look at WF 4

    • 1 Comments

    Our WF evangelist, Ron Jacobs, just posted a 10-4 screencast that walks through WF4 as it's looking in VS 2010.  This is a good first look at our bits since PDC (we've changed things a little bit).  I really like that he takes it from a unit testing perspective.  There are certainly things that have been added (like WorkflowInvoker.Invoke) which will make testing activities a lot easier than the 3.0 days.

    Check it out, let me know if you've got feedback. 

    Get Microsoft Silverlight

  • mwinkle.blog

    WF 4.0 -- Designer Type Visibility

    • 3 Comments

    One of the exercises I'm going through now is scrubbing our API and taking a hard look at types marked as public and trying to decide "does it really need to be public."  While on first blush, you can

    What I'd love to get from folks are answers to the following questions:

    • Were there designer types in 3.0/3.5 that were not public that caused you problems?
      • If so, why?  What and how did you want to use that type?
    • Were there scenarios where you found our decisions on type visibility odd, inconsistent or painful?  Let me know what those were.

     

    Looking forward to your thoughts!

  • mwinkle.blog

    Endpoint.tv Talks Tracking

    • 1 Comments

    I heard that the folks that bring you endpoint.tv are doing a two part series on Workflow Tracking in 3.0/3.5.  This is one of my favorite feature areas in 3.0/3.5, and one that I think is not talked about too often.  The team built a lot of functionality in there besides the very basic "emit tracking info" one might expect with the feature description.

    Check it out, and if you want some other background, check out these blog posts I made a few years back!

  • mwinkle.blog

    Thoughts on Waiting until 20xx for WF

    • 10 Comments

    Usual msblog disclaimer applies, this represents my opinion!

    While I was on break, a number of folks pinged me asking me about this blog post by Tad Anderson.

    I find the investment in time to learn how to use 3.0/3.5 has been a complete waste time. So we have release 1.0 and 1.5 of WWF becoming obsolete in favor of version 2.0. These are the real release numbers on these libraries, and that is how they should have been labeled. They are not release 3.0 and 3.5.

    First, your investment in the existing technologies is not a "waste of time."  The idea of modeling your app logic declaratively, via workflows doesn't change, nor do the ideas surrounding how one builds an application with workflows.  What we are fixing is that we are making it substantially easier to use, and enabling more advanced scenarios (like implicit message correlation). What you will not be able to re-use is some of the things you did the first time and thought, "hmmm, I wonder why I have to do that [activity execution context cloning, handleExternalEvent, I'm looking at you]."  From a designer perspective, your not going to have to keep remembering the quirks of the v1 designer.  I think about this similarly to the way we went from ASMX web services to WCF.  The API's changed, but the underlying thinking of building an app on services did not.  Regarding version numbers, all of our libraries are versioned to the version of the framework we ship with (see WPF, WCF, etc).  Internally we struggled with what we call the thing we're working on now and decided to stick with framework version (so WF 4.0, rather than WF 2.0). 

    Secondly, it's important to note, we're not getting rid of the 3.0, 3.5 technologies.  We're investing to port them to the new CLR, and work to make the designers operate in VS 2010.  If you get sufficient return by using WF in your apps today, use WF today.  If WF doesn't meet your needs today, and if we're fixing that by something that we're doing in 4.0, then it makes sense to wait.  Note, I'm not defining "return" for you.  Depending upon how you define that, you may reach a different conclusion that someone in a similar setting. 

    Thirdly, activities you write today on 3.0/3.5 will continue to work, even inside a 4.0 workflow by way of the interop activity.  Much as WPF has the ability to host existing WinForms content, we have the ability to execute 3.0-based activities. 

    There is a larger issue of how we (the big, Microsoft "we") handle a combination of innovation, existing application compatibility, and packaging of features.  I'm not sure how we avoid the fact that inevitably, any framework in version n+1 will introduce new features, some of which will not be compatible with framework version n, some of which may do similar things to features in framework version n.  Folks didn't stop writing WinFroms apps when WPF was announced (they still write WinForms apps).  As I mentioned, this is a big issue, but not one I intend to tackle in this post :-)

    The feedback we got from customers around WF was centered around the need for a few things:

    • Activities and Workflows
      • A fully declarative experience (declare everything in XAML)
      • Make it easier to write complex activities (see my talk for the discussion on writing Parallel or NofM)
      • Make data binding easier
    • Runtime
      • Better control over persistence
      • Flow-in transactions
      • Support partial trust
      • Increase perf
    • Tooling
      • Fix Perf and usability
      • Make rehosting and extensibility easier

    Most of these would require changes to the existing code base, and breaking changes would become unavoidable.  The combination of doing all of these things makes the idea of breaking all existing customers absolutely untenable.  We're doing the work to make sure that your WF apps you write today will keep on working, and with services as the mechanisms to communicate between them, one can gradually introduce 4.0 apps as well.  Given the commitment we have to our v1  (or netfx3) customers, we don't want to introduce those kinds of breaking changes.

    Kathleen's article summarizes this very nicely, and rather than be accused of cherry-picking quotes, I encourage you to read the whole article.

    Questions, comments, violent flames, post 'em here or mwinkle_at_largeredmondbasedsoftwarecompany_dot_com

  • mwinkle.blog

    How I Spent My Winter Vacation

    • 1 Comments

    I haven't posted since prior to PDC, and since then it's been a whirl wind.  You see, in the middle of September, my wife and I had a healthy baby boy, so I've been a little busy since then with things like diapers, visiting family, etc.  Given the huge time crunch of PDC, I decided to cash in on Microsoft's awesome parental leave benefit and I have been away from work for a little over a month. 

    I started working for my dad's company during the summer when I was still in high school, in college I worked summers to pay tuition, and following graduation went right to work two weeks later.  I've never taken this much time away from work, brendan with grinand I sorely regret not having done it sooner.  When my daughter was born, I was unable to take much more than a week or so off, and I miss having been able to spend this kind of time with her. My boss and grand-boss were very supportive, including a few gentle slaps on the wrist when I did log in to check on a few emails.

    This gave me a great opportunity to really unplug after a crazy year, and an even crazier PDC.  Most importantly though, it let me really spend a lot of quality time with my new son while he's at a pretty fun age (as you can see from the grin)

    I stayed pretty true to my commitment to avoid work stuff, but I did keep up on tech news (including fun rumors) and started to lurk through some ASP.NET MVC stuff earlier in the break.  I've begun to become much more addicted to google reader, friendfeed and twitter than is probably healthy

    I also got to do a fair amount of reflection on work, what we're doing, what I'm doing, and how excited I am about the work that my team is doing.

    I'll be getting back to my more regular series of postings, first on some WF things, and then I'll start doing more posts on Oslo and more specifically, Quadrant.

  • mwinkle.blog

    Which WF/WCF Talks Should You Attend at PDC?

    • 1 Comments

    Just got the email the other day that PDC is less than 4 weeks away, and it got me thinking a bit about how I would think about these sessions as an attendee.  Searching on the PDC site will yield 8 talks tagged with WF.   Here's how I break some of these these down, the first few are about using WF, and the last 3 are about WF itself:

    Hosting Workflows and Services

    Dan Eshner

    Hear about extensions being made to Windows Server to provide a feature-rich middle-tier execution and deployment environment for Windows Workflow Foundation (WF) and Windows Communication Foundation (WCF) applications. Learn about the architecture of this new extension, how it works, how to take advantage of it, and the features it provides that simplify deployment, management, and troubleshooting of workflows and services.

    This talk is all about the host we're building for WF and WCF, which I mentioned earlier, we're calling "Dublin".  If you're familiar with either technology, and have built a host of your own, this will be interesting both from the perspective of what is coming, as well as how we are thinking about solving some of the hosting problems.

    A Lap around "Oslo"

    Presenters: Douglas Purdy, Vijaye Raji

    "Oslo" is the family of new technologies that enable data-driven development and execution of services and applications. Come and learn how to capture all aspects of an application schematized in the "Oslo" repository and use "Oslo" directly to drive the execution of deployed applications.

    Building declarative or data driven apps is a "thing" in the Oslo world.  This talk will give the big picture of all of the various pieces of Oslo, and how existing declarative technologies, like WF and WCF fit into it.  Note, this talk is not primarily about WF or WCF, rather it is about Oslo, which you can read about in more detail here and here.

    What about all of the things we are doing to WCF and WF in .NET 4?  That's the remaining three talks:

    Windows Workflow Foundation 4.0: A First Look

    Presenter: Kenny Wolf

    Programs coordinate work. The code for coordination and state management often obscures a program's purpose. Learn how programming with Windows Workflow Foundation (WF) 4.0 provides clarity of intent while preserving the functional richness of the .NET framework. See how easy it is to build workflows with the new Visual Studio workflow designer. Learn about text-based authoring options for WF. Hear how WF integrates well with other Microsoft technologies (WCF, WPF, ASP.NET). If you've looked at WF before, come and see the changes to data flow, composition, and new control flow styles. Significant improvements to usability, composability, and performance make Workflow a great fit for a broad range of solutions on both the client and the server.

    Windows Workflow Foundation 4.0: Extending with Custom Activities

    Presenter: Matt Winkler

    Windows Workflow Foundation (WF) coordinates and manages individual units of work, encapsulated into activities. WF comes with a rich library of activities. Learn how to extend this library by encapsulating your own APIs with custom activities. See how to compose those basic activities into higher level units using rules, flowchart, and state machine control flow styles. Learn how to build your own WF control styles. Learn how to customize and re-host the workflow authoring experience using the new WF designer framework.

    Windows Communication Foundation 4.0: Building WCF Services with Windows Workflow Foundation in Microsoft .NET 4.0

    Presenter: Ed Pinto

    Eliminate the tradeoff between ease of service authoring and performant, scalable services. Hear about significant enhancements in WCF and WF to deal with the ever increasing complexity of communication. Learn how to use WCF to correlate messages to service instances using transport, context, and application payloads. See how the new WF messaging activities enable the modeling of rich protocols. Learn how WCF provides a default host for workflows exposing features such as distributed compensation and discovery. See how service definition in XAML completes the union of WF and WCF with a unified authoring experience that simplifies configuration and is fully integrated with IIS activation and deployment.

    Kenny's talk will be an introduction to all of the new features in WF.  If you haven't used WF, or if you looked at WF before and decided it wasn't right for your solution, come to this talk to see how WF makes writing programs easier.  If you are using WF today, and want to see what has changed, this will be a good talk for you.

    My talk will be a very hands on, write some code, style talk focused on building activities and all of the aspects of WF that impact activity development.  If you are using WF today, and want to see what the changes mean for the code you'll write, this is the talk for you.  Also, if you attend Kenny's talk and think, "hey, I want to learn more" this will also be the talk for you.  My talk won't focus on the "why" or the "where" of workflow, but more the "how to build" parts.

    Finally, Ed's talk is the talk to go to if you are a WCF  developer. If you are building programs that consume services, where service is very loosely defined integrating external information into your app, you should also make sure to go to this talk.  This talk will highlight a number of the enhancements that have been made both to WCF and to the integration between WF and WCF.  We believe very strongly that WF and WCF are tremendously complementary technologies.

    To help, I've put together the following decision table to help you decide.  There are three possible actions, "Must Attend" "Should attend" and "Would Enjoy".  I think they are fairly explanatory actions, but if you have questions, let me know.

     

      Kenny's talk: A First look Matt's Talk : Building Activities Ed's talk: Building WCF Services with WF
    Building WF today Must attend Must attend Must attend
    Building WCF today Should attend Should attend Must attend
    Looked at WF, but didn't use it Must attend Must attend Should attend
    Looked at WCF, but didn't use it Should attend Would enjoy Must attend
    Interested in Oslo Must attend Should attend Should attend
    Interested in the problem of coordination Should attend Should attend Should attend
    Building services, or apps that consume services Would enjoy Would enjoy Must attend

     

    Can't wait to see you in LA!

  • mwinkle.blog

    WCF Perf Talk @ PDC (or, the Doctor Teaches Fishing)

    • 1 Comments

    I'm probably not going to have too much time to attend talks at PDC, but one talk that would be high on my list is the one that Dr. Allen blogs about here, where he talks about the Zen of WCF Performance and Scale. 

    I like "Zen" style talks, especially for topics like performance and scale.  Sure, I could go and listen for 75 minutes for tips and tricks that may be applicable to my scenario, but this is giving you a fish.  Having a 75 minute conversation about how to think about perf and scale, how to think about achieving that in a distributed messaging system teaches you how to fish.  This is going to equip you with a lot more knowledge about how to plan for and solve performance and scale issues down the road.

    This is one lunch session, I wouldn't want to miss.  As a plus, he's taking questions and suggestions on his blog, so fire away!

  • mwinkle.blog

    More Details On WF/WCF in .NET 4.0

    • 1 Comments

    Steve Martin, a director of product management for CSD, has a blog post containing more information on the work that we are doing for the next versions of WF and WCF that we will release as a CTP at PDC.  He also introduces "Dublin," the name for our efforts around creating a manageable and scalable host for WF and WCF applications, something that I know a few customers would be interested in. 

    For you WF and WCF fans, there some more information about some of the features that you'll hear more about at PDC.  I think for customers who are using either today, you'll see something on the list below that gets you interested.  And, if you're not using WF or WCF today, I think there are a few things that might make you interested. We think that the features we're introducing (especially in WF, which is close to my heart) will make it easier to use WF, in more places, and by more people.  Let us know what you think.  What's exciting in the list below, what do you want to hear more about, is there something else you'd like to see on the list?

     

    WF Features

    Significant improvements in performance and scalability

    · Ten-fold improvement in performance

    New workflow flow-control models and pre-built activities

    · Flowcharts, rules

    · Expanded built-in activities – PowerShell, database, messaging, etc.

    Enhancements in workflow modeling

    · Persistence control, transaction flow, compensation support, data binding and scoping

    · Rules composable and seamlessly integrated with workflow engine

    Updated visual designer

    · Easier to use by end-users

    · Easier to rehost by ISVs

    Ability to debug XAML

     

    WCF Features

    RESTful enhancements

    · Simplifying the building of REST Singleton & Collection Services, ATOM Feed and Publishing Protocol Services, and HTTP Plain XML Services using WCF

    · WCF REST Starter Kit to be released on Codeplex to get early feedback

    Messaging enhancements

    · Transports - UDP, MQ, Local in-process

    · Protocols - SOAP over UDP, WS-Discovery, WS-BusinessActivity, WS-I BP 1.2

    · Duplex durable messaging

    Correlation enhancements

    · Content and context driven, One-way support

    Declarative Workflow Services

    · Seamless integration between WF and WCF and unified XAML model

    · Build entire application in XAML, from presentation to data to services to workflow

  • mwinkle.blog

    More Workflow Service Questions

    • 1 Comments

    In response to this post, Anderson raised the following question.

    Definitely too hard and not service-oriented.  I like the idea of sending it via the headers or some other such implementation, but that would also require implementation on the other side in the form of an agreed-upon place and format of the context information or a custom binding implementation that you either distribute or have the other side of the fence reimplement.

    The first time I did this I spent days working on it saying to myself "surely you don't have to do this... surely you don't have to tell the target service which *activity* you want it to talk to next".  But you do.  It makes me sad inside.

    matt:  Our general goal is not to introduce more misery into the world.  At this point in time, doing duplex requires the explicit management of the context token.  We're working to make it better, so hopefully around PDC time, you will no longer be sad inside. 

    One thing I've not seen implemented yet in a demo.  I really like the feature of State Machine super states and substates.  The ability to take a group of 3 states and put them in another state called "Cancellable" or some other such interrupty business function is really kickass.

    Let's say you are in state "Processing" which has an event-driven that listens for the service method "FinishedProcessing".  "Processing" is also in a superstate called "Cancellable" with an event-driven that listens for "CancelProcess".  How could you implement this when you have to tell the other service about the context identifier of the target receive activity?  Do you have to send both contexts?  I fear your answer will be yes.

    matt:  In that case, provided you are not listening for the same operation in two places, you can get away with grabbing the context token from either FinishedProcessing or CancelProcess and send that along.  The context token will be the same fore either of them, it will only consist of the Workflow Instance Id.  The Instance Id + the OperationName is the combination used to enqueue an inbound message, unless you've gotten clever as in the Conversations sample and told the Receive activities to create queues based on an additional conversation id. 

     

    Also, it sounds like Anderson is delivering a talk tomorrow night in Dallas about WCF /WF integration.  If you're in the area, head on over and check it out.

  • mwinkle.blog

    Advanced Workflow Service Talk (Demo 4 of 4)

    • 3 Comments

    When we start doing this two way style of messaging, we now open up to start modeling some interesting business problems.  In the previous post, you'll note that I did not include the code, because I mentioned we needed to be more clever in scenarios where we listen in parallel. 

    First, a brief diversion into how the Receive activity works.  Everybody remembers the workflow queues, the technology that underlies all communication between a host and a workflow instance.  The Receive activity works by creating a queue that the WorkflowServiceHost (specifically the WorkflowOperationInvoker) will use to send the message received off the wire into the workflow.  Now, the Receive activity normally just creates a queue that is named the same as the operation the Receive activity is bound to.  However, if we have two Receive activities listening for the same operation at the same time, no longer is a single queue useful to route responses back as we want to route to the correct Receive activity instance. 

    There is property on the Receive activity called ContextToken.  Normally this is null in the simple case.  However, when we want our Receive activity to operate in parallel, we need to indicate that it needs to be smarter when it creates a queue. 

    image

    By setting this property (you can just type in a name, and then select the common owner all of the parallel receive's share.  This will cause the Receive activity to create a queue named [OperationName] +[ConversationId], the conversation ID takes the form of a GUID, and is the second element inside a context token. 

    The sample that I show for this talk is simply the conversations sample inside the SDK.  This is the sample to check out to understand all sorts of interesting ways to use the context tokens to model your processes.

     

    Conversations Sample Architecture

     

    Now, there are two conversation patterns here.  One is the one shown above, which I refer to as an n-party conversation where n is fixed at design time.  We can accomplish this with the parallel activity.  The other is where n is arbitrary (imagine you send out to business partners stored in the database).  The way to do this is to use the Replicator activity.  The Replicator is a little known gem shipped in 3.0 that essentially gives you "ForEach" semantics.  But, by flipping the ExecutionType switch to parallel, I now get the behavior of a parallel, but operating with an arbitrary n branches.

    So, in order to enable conversations, we need to tell our receive activity to be a little smarter about how it generates its queue name, and then we simply follow the duplex pattern we discussed in the last two posts.  Once we do that, we're in good shape to start modeling some more interesting communication patterns between multiple parties. 

    Where can we go from here?


    We can just make the patterns more interesting.  One interesting one would be the combination of the long running work with cancellation and a Voting activity in order to coordinate the responses and allow for progress to be made when some of the branches complete (if I have 3 yes votes, I can proceed).  The power of building composite activities is that it gives me a uniform programming model (and a single threaded one to boot) in order to handle the coordination of different units of work.  Get out there and write some workflows :-)

Page 2 of 6 (148 items) 12345»