Stumbling Through

Join me as I stumble, bumble and fumble my way through some new developer technologies. We'll laugh, we'll cry, there may be a mouse tossed through a monitor, but in the end we will all hopefully learn something.
in
Stumbling Through: SharePoint 2010 - Visual Web Parts

There are plenty of great blog posts out there on how to get started with Visual Web Parts on SharePoint 2010 using Visual Studio 2010, but when I started to go beyond the basics I found that I had to piece together information from various sources to get the complete picture.  As such, my goal in this post is to bring all of that information together into one place.

To begin with, I have a user control built for SharePoint 2007 that takes advantage of the ‘SmartPart’ project to turn it into a web part.  This user control has custom images, references other project assemblies, uses JQuery libraries and has configurable web part properties.  These are all things I wanted to replicate in my SharePoint 2010 Visual Web Part with as little re-coding as possible, so we’ll stumble through whatever hurdles present themselves in this process.

I am not going to rehash the basics of creating a Visual Web Part project, other blogs have covered that far better than I could, but there was one interesting stumbling block I hit during the create visual web part wizard.  When I told it the SharePoint site I wanted to use for debugging, I received the following error:

image

Well, what the heck?  That was the only site that showed up in the dropdown and I can navigate to it just fine so I know it is correct.  What is the deal?  Searching on this error uncovered a ton of different information on the various security rights that the user opening Visual Studio must have, but not all of these recommendations seemed to be necessary.  In fact, I was able to get away with only making the user db_owner of the SharePoint_AdminContent, SharePoint_Config and application content databases.  Still seems like a lot of rights to grant a developer, but it is necessary to allow the feature to be deployed to and retracted from the farm (and it should be a development server any way, so no big deal). 

Despite making this change, I was still receiving the error above.  The final piece of this puzzle was that even though the local site name is in fact appsrv2, when the web application was created in Central Administration it was given a different URL.  Apparently, both URLs still work when browsing via IE, but Visual Studio required the URL given in Central Administration in order to connect correctly.  This pain could’ve been avoided if Visual Studio showed the URL from Central Admin in the dropdown instead of the IIS site name but oh well, we figured it out.

Now that our SharePoint debugging server is set up, every time we run our project it will deploy the web part and open up our SharePoint page where we can add the web part and see its behavior.  Then, when we are done debugging, it retracts the web part.  This is all well and good if you are in pure development mode and don’t have any pages that have that web part used in them for any other reason – for example, I had the web part on a page that I was using for a proof of concept to show end users, and every time I ended a debugging session, that page would blow up because the web part was no longer available!  Thankfully, there is an easy way around this auto-retract feature.  Right click your visual web part project and open its properties.  There should be a SharePoint tab within there, and tucked way at the bottom of that panel is an option to Auto-retract after debugging.  This is checked by default, uncheck it and it will no longer retract your web part for you (leaving any proof of concept pages functional after a debugging session):

image

Ok, the basics are out of the way – we have a Visual Web Part project that we can debug and deploy to SharePoint so let’s get into the guts of the functionality of this web part.  I already have the user control from the SharePoint 2007 ‘SmartPart’ approach, so I’m going to copy the entire layout and code behind into the user control created as a part of our Visual Web Part project and see what it doesn’t like.  Aside adding my references to the new project that were in the old project, everything seemed to copy over just fine.  Building the project, however, revealed that a utility project I was referencing within the web part solution was not getting picked up.  To include project references in a Visual Web Part project, they need to be added to the package.  Each Visual Web Part project has a ‘Package’ node underneath it in the Solution Explorer that has its own set of properties.  Right click it to bring up the properties window and you’ll see by default that the only item in the package is the web part itself.  Clicking the ‘Advanced’ button will allow you to add other references to the package, including project references.  Additionally, it will allow you to specify how this reference will be deployed with the web part, either into the GAC or into the web project.  In my case, I selected ‘Add’ and ‘Add Assembly from Project Output’.  This presented me with a screen to select which project I would like to include out of all the projects in my solution:

image

I chose to add this assembly into the GAC as that is the simplest way to make it available, though this process does seem to streamline adding the control to safe controls for you (kind of a pain to do by hand in the past).

Ok, great, with the addition of the ‘K2 Utility’ project into the web part package (something I happened to be referencing in the original web part), we can now build and deploy the visual web part.  This makes it available to add to any page just like any other web part.  Doing so, however, revealed that there are two other things that failed to come across in the deployment:  my images and scripts!  I had them included in the web part in the typical manner of an ‘Images’ and ‘Scripts’ folder right in the root of the project, but that didn’t seem to do the trick in the SharePoint 2010 world.  What we actually need to do is add a reference to SharePoint’s ‘Layouts’ folder, which automatically will include a folder for this web part to put referenced files into.  To do this, right click the Visual Web Parts project and select ‘Add’ –> ‘SharePoint “Layouts” Mapped Folder’.  Within this folder, you can then move your Images and Scripts folder from the original web part.  Unfortunately, any reference to said scripts and images need to be changed to look like this:  /_layouts/TaskList2010/Images/ or /_layouts/TaskList2010/Scripts/.  After these quick changes, images and scripts were being referenced correctly in the web part, making it look like its old self again.

Happy once again with the look of my web part, I went into the web part settings screen in SharePoint to set up the configuration values that make it actually function, and was saddened to see that there were no custom properties available in the properties page.  In the SmartPart world, all I had to do was decorate a public property of the user control with ‘System.ComponentModel.Browsable(true)’ to make it appear in the properties page, not so in SharePoint 2010.  To accomplish this, we need to add the properties to the code behind of the Visual Web Part (not the user control) and decorate the properties with this: 

System.Web.UI.WebControls.WebParts.WebBrowsable(true),
System.Web.UI.WebControls.WebParts.WebDisplayName("Property Name"),
System.Web.UI.WebControls.WebParts.WebDescription(""),
System.Web.UI.WebControls.WebParts.Personalizable(System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared),
System.ComponentModel.Category("Custom Settings"),
System.ComponentModel.DefaultValue("")

This will make the property appear under a ‘Custom Settings’ section, but then you have to tie it into your actual user control property – kind of a tedious duplication of work but it is a one-time thing.  This tie-in will occur in the ‘CreateChildControls’ event within the Visual Web Part code behind, setting each property value of the control to its corresponding value in the Visual Web Part.

Well there you have it – it wasn’t totally a copy+paste+celebrate affair to get a SmartPart from SharePoint 2007 working as a Visual Web Part in SharePoint 2010, but there weren’t too many changes necessary.  Hopefully this collection of hurdles I encountered during the conversion will be useful to you as you move into SharePoint 2010.

Stumbling Through: Visual Studio 2010 (Part IV)

So finally we get to the fun part – the fruits of all of our middle-tier/back end labors of generating classes to interface with an XML data source that the previous posts were about can now be presented quickly and easily to an end user.  I think.  We’ll see.  We’ll be using a WPF window to display all of our various MFL information that we’ve collected in the two XML files, and we’ll provide a means of adding, updating and deleting each of these entities using as little code as possible.  Additionally, I would like to dig into the performance of this solution as well as the flexibility of it if were were to modify the underlying XML schema.  So first things first, let’s create a WPF project and include our xml data in a ‘data’ folder within.  On the main window, we’ll drag out the following controls:

  • A combo box to contain all of the teams
  • A list box to show the players of the selected team, along with add/delete player buttons
  • A text box tied to the selected player’s name, with a save button to save any changes made to the player name
  • A combo box of all the available positions, tied to the currently selected player’s position
  • A data grid tied to the statistics of the currently selected player, with add/delete statistic buttons

This monstrosity of a form and its associated project will look like this (don’t forget to reference the ‘DataFoundation’ project from the ‘Presentation’ project):

image

To get to the visual data binding, as we learned in a previous post, you have to first make sure the project containing your bindable classes is compiled.  Do so, and then open the Data Sources pane to add a reference to the Teams and Positions classes in the DataFoundation project:

image

Why only Team and Position?  Well, we will get to Players from Teams, and Statistics from Players so no need to make an interface for them as we’ll see in a second.  As for Positions, we’ll need a way to bind the dropdown to ALL positions – they don’t appear underneath any of the other classes so we need to reference it directly.  After adding these guys, expand every node in your Data Sources pane and see how the Team node allows you to drill into Players and then Statistics.  This is why there was no need to bring in a reference to those classes for the UI we are designing:

image

Now for the seriously hard work of binding all of our controls to the correct data sources.  Drag the following items from the Data Sources pane to the specified control on the window design canvas:

  • Team.Name –> Teams combo box
  • Team.Players.Name –> Players list box
  • Team.Players.Name –> Player name text box
  • Team.Players.Statistics –> Statistics data grid
  • Position.Name –> Positions combo box

That is it!  Really?  Well, no, not really… there is one caveat here in that the Positions combo box is not bound the selected player’s position.  To do so, we will apply a binding to the position combo box’s ‘SelectedValue’ to point to the current player’s PositionId value:

image

That should do the trick… now, all we need to worry about is loading the actual data.  Sadly, it appears as if we will need to drop to code in order to invoke our IO methods to load all teams and positions.  At least Visual Studio kindly created the stubs for us to do so, ultimately the code should look like this:

image

Note the weirdness with the ‘InitializeDataFiles’ call – that is my current means of telling an IO where to load the data for each of the entities.  I haven’t thought of a more intuitive way than that yet, but do note that all data is loaded from Teams.xml besides for positions, which is loaded from Lookups.xml.   I think that may be all we need to do to at least load all of the data, let’s run it and see:

image

Yay!  All of our glorious data is being displayed!  Er, wait, what’s up with the position dropdown?  Why is it red?  Let’s select the RB and see if everything updates:

image

Crap, the position didn’t update to reflect the selected player, but everything else did.  Where did we go wrong in binding the position to the selected player?  Thinking about it a bit and comparing it to how traditional data binding works, I realize that we never set the ‘value member’ (or some similar property) to tell the control to join the Id of the source (positions) to the position Id of the player.  I don’t see a similar property to that on the combo box control, but I do see a property named ‘SelectedValuePath’… that might be it, so I set it to Id and run the app again:

image

Hey, all right!  No red box around the positions combo box.  Unfortunately, selecting the RB does not update the dropdown to point to Runningback.  Hmmm.  Now what could it be?  Maybe the problem is that we are loading teams before we are loading positions, so when it binds position Id, all of the positions aren’t loaded yet.  I went to the code behind and switched things so position loads first and… no dice.  Same result when I run.  Why?  WHY?  Ok, ok, calm down, take a deep breath.  Get something with caffeine or sugar (preferably both) and think rationally.

Ok, gigantic chocolate chip cookie and a mountain dew chaser have never let me down in the past, so don’t fail me now!  Ah ha!  of course!  I didn’t even have to finish the mountain dew and I think I’ve got it:  Data Context.  By default, when setting on the selected value binding for the dropdown, the data context was list_team.  I don’t even know what the heck list_team is, we want it to be bound to our team players view source resource instead, like this:

image

image

Running it now and selecting the various players:

image

image

Done and done.  Everything read and bound, thank you caffeine and sugar!  Oh, and thank you Visual Studio 2010.  Let’s wire up some of those buttons now… There has got to be a better way to do this, but it works for now.  What the add player button does is add a new player object to the currently selected team.  Unfortunately, I couldn’t get the new object to automatically show up in the players list (something about not using an ‘observable collection’ – gotta look into this) so I just save the change immediately and reload the screen.  Terrible, but it works:

image

Let’s go after something easier:  The save button.  By default, as we type in new text for the players name, it is showing up in the list box as updated.  Cool!  Why couldn’t my add new player logic do that?  Anyway, the save button should be as simple as invoking MFL.IO.Save for the selected player, like this:

MFL.IO.Save((MFL.Player)lbTeamPlayers.SelectedItem, true);

Surprisingly, that worked on the first try.  Let’s see if we get as lucky with the Delete player button:

MFL.IO.Delete((MFL.Player)lbTeamPlayers.SelectedItem);
Refresh();

Note the use of the ‘Refresh’ method again – I can’t seem to figure out why updates to the underlying data source are immediately reflected, but adds and deletes are not.  That is a problem for another day, and again my hunch is that I should be binding to something more complex than IEnumerable (like observable collection).

Now that an example of the basic CRUD methods are wired up, I want to quickly investigate the performance of this beast.  I’m going to make a special button to add 30 teams, each with 50 players and 10 seasons worth of stats.  If my math is right, that will end up with 15000 rows of data, a pretty hefty amount for an XML file.  The save of all this new data took a little over a minute, but that is acceptable because we wouldn’t typically be saving batches of 15k records, and the resulting XML file size is a little over a megabyte.  Not huge, but big enough to see some read performance numbers… or so I thought.  It reads this file and renders the first team in under a second.  That is unbelievable, but we are lazy loading and the file really wasn’t that big.  I will increase it to 50 teams with 100 players and 20 seasons each - 100,000 rows.  It took a year and a half to save all of that data, and resulted in an 8 megabyte file.  Seriously, if you are loading XML files this large, get a freaking database!  Despite this, it STILL takes under a second to load and render the first team, which is interesting mostly because I thought that it was loading that entire 8 MB XML file behind the scenes. 

I have to say that I am quite impressed with the performance of the LINQ to XML approach, particularly since I took no efforts to optimize any of this code and was fairly new to the concept from the start.  There might be some merit to this little project after all… Look out SQL Server and Oracle, use XML files instead!  Next up, I am going to completely pull the rug out from under the UI and change a number of entities in our model.  How well will the code be regenerated?  How much effort will be required to tie things back together in the UI?

Stumbling Through: Visual Studio 2010 (Part III)

The last post ended with us just getting started on stumbling into text template file customization, a task that required a Visual Studio extension (Tangible T4 Editor) to even have a chance at completing.  Despite the benefits of the Tangible T4 Editor, I still had a hard time putting together a solid text template that would be easy to explain.  This is mostly due to the way the files allow you to mix code (encapsulated in <# #>) with straight-up text to generate.  It is effective to be sure, but not very readable.  Nevertheless, I will try and explain what was accomplished in my custom tt file, though the details of which are not really the point of this article (my way of saying ‘don’t criticize my crappy code, and certainly don’t use it in any somewhat real application.  You may become dumber just by looking at this code.  You have been warned’ – really the footnote I should put at the end of all of my blog posts).

To begin with, there were two basic requirements that I needed the code generator to satisfy:  Reading one to many entity framework files, and using the entities that were found to write one to many class files.  Thankfully, using the Entity Object Generator as a starting point gave us an example on how to do exactly that by using the ‘MetadataLoader’ and ‘EntityFrameworkTemplateFileManager’ – you include references to these items and use them like so:

// Instantiate an entity framework file reader and file writer
MetadataLoader loader = new MetadataLoader(this);
EntityFrameworkTemplateFileManager fileManager = EntityFrameworkTemplateFileManager.Create(this);

// Load the entity model metadata workspace
MetadataWorkspace metadataWorkspace = null;
bool allMetadataLoaded =loader.TryLoadAllMetadata("MFL.tt", out metadataWorkspace);
EdmItemCollection ItemCollection = (EdmItemCollection)metadataWorkspace.GetItemCollection(DataSpace.CSpace);

// Create an IO class to contain the 'get' methods for all entities in the model
fileManager.StartNewFile("MFL.IO.gen.cs");

Next, we want to be able to loop through all of the entities found in the model, and then each property for each entity so we can generate classes and methods for each.  The code for that is blissfully simple:

// Iterate through each entity in the model
foreach (EntityType entity in ItemCollection.GetItems<EntityType>().OrderBy(e => e.Name))
{
    // Iterate through each primitive property of the entity
    foreach (EdmProperty edmProperty in entity.Properties.Where(p => p.TypeUsage.EdmType is PrimitiveType && p.DeclaringType == entity))
    {
        // TODO:  Create properties
    }
    // Iterate through each relationship of the entity
    foreach (NavigationProperty navProperty in entity.NavigationProperties.Where(np => np.DeclaringType == entity))
    {
        // TODO:  Create associations
    }
}

There really isn’t anything more advanced than that going on in the text template – the only thing I had to blunder through was realizing that if you want the generator to interpret a line of code (such as our iterations above), you need to enclose the code in <# and #> while if you want the generator to interpret the VALUE of code, such as putting the entity name into the class name, you need to enclose the code in <#= and #> like so:

public partial class <#=entity.Name#>

To make a long story short, I did a lot of repetition of the above to come up with a text template that generates a class for each entity based on its properties, and a set of IO methods for each entity based on its relationships.  The two work together to provide lazy-loading for hierarchical data (such getting Team.Players) so it should be pretty intuitive to use on a front-end.  This text template is available here – you can tweak the ‘inputFiles’ array to load one or many different edmx models and generate the basic xml IO and class files, though it will probably only work correctly in the simplest of cases, like our ‘MFL’ model described in the previous post.  Additionally, there is no validation, logging or error handling which is something I want to handle later by stumbling through the enterprise library 5.0.

The code that gets generated isn’t anything special, though using the LINQ to XML feature was something very new and exciting for me – I had only worked with XML in the past using the DOM or XML Reader objects along with XPath, and the LINQ to XML model is just so much more elegant and supposedly efficient (something to test later).  For example, the following code was generated to create a ‘Player’ object for each ‘Player’ node in the XML:

        return from element in GetXmlData(_PlayerDataFile).Descendants("Player")
            select new Player
            {
                Id = int.Parse(element.Attribute("Id").Value)
                ,ParentName = element.Parent.Name.LocalName
                ,ParentId = long.Parse(element.Parent.Attribute("Id").Value)
                ,Name = element.Attribute("Name").Value
                ,PositionId = int.Parse(element.Attribute("PositionId").Value)
            };

It is all done in one line of code, no looping needed.  Even though ‘GetXmlData’ loads the entire xml file just like the old XML DOM approach would have, it is supposed to be much less resource intensive.  I will definitely put that to the test after we develop a user interface for getting at this data.  Speaking of the data… where IS the data?  We’ve put together a pretty model and a bunch of code around it, but we don’t have any data to speak of.  We can certainly drop to our favorite XML editor and crank out some data, but if it doesn’t totally match our model, it will not load correctly.  To help with this, I’ve built in a method to generate xml at any given layer in the hierarchy.  So for us to get the closest possible thing to real data, we’d need to invoke MFL.IO.GenerateTeamXML and save the results to file.  Doing so should get us something that looks like this:

<Team Id="0" Name="0">
  <Player Id="0" Name="0" PositionId="0">
    <Statistic Id="0" PassYards="0" RushYards="0" Year="0" />
  </Player>
</Team>

Sadly, it is missing the Positions node (haven’t thought of a way to generate lookup xml yet) and the data itself isn’t quite realistic (well, as realistic as MFL data can be anyway…).  Let’s manually remedy that for now to give us a decent starter set of data.  Note that this is TWO xml files – Lookups.xml and Teams.xml:

<Lookups Id=”0”>
  <Position Id="0" Name="Quarterback"/>
  <Position Id="1" Name="Runningback"/>
</Lookups>

<Teams Id=”0”>
  <Team Id="0" Name="Chicago">
    <Player Id="0" Name="QB Bears" PositionId="0">
      <Statistic Id="0" PassYards="4000" RushYards="120" Year="2008" />
      <Statistic Id="1" PassYards="4200" RushYards="180" Year="2009" />
    </Player>
    <Player Id="1" Name="RB Bears" PositionId="1">
      <Statistic Id="2" PassYards="0" RushYards="800" Year="2007" />
      <Statistic Id="3" PassYards="0" RushYards="1200" Year="2008" />
      <Statistic Id="4" PassYards="3" RushYards="1450" Year="2009" />
    </Player>
  </Team>
</Teams>

Ok, so we have some data, we have a way to read/write that data and we have a friendly way of representing that data.  Now, what remains is the part that I have been looking forward to the most: present the data to the user and give them the ability to add/update/delete, and doing so in a way that is very intuitive (easy) from a development standpoint.

Stumbling Through: Visual Studio 2010 (Part II)

I would now like to expand a little on what I stumbled through in part I of my Visual Studio 2010 post and touch on a few other features of VS 2010.  Specifically, I want to generate some code based off of an Entity Framework model and tie it up to an actual data source.  I’m not going to take the easy way and tie to a SQL Server data source, though, I will tie it to an XML data file instead.  Why?  Well, why not?  This is purely for learning, there are probably much better ways to get strongly-typed classes around XML but it will force us to go down a path less travelled and maybe learn a few things along the way.  Once we get this XML data and the means to interact with it, I will revisit data binding to this data in a WPF form and see if I can’t get reading, adding, deleting, and updating working smoothly with minimal code. 

To begin, I will use what was learned in the first part of this blog topic and draw out a data model for the ‘MFL’ (My Football League) - I don’t want the NFL to come down and sue me for using their name in this totally football-related article.  The data model looks as follows, with Teams having Players, and Players having a position and statistics for each season they played:

image

Note that when making the associations between these entities, I was given the option to create the foreign key but I only chose to select this option for the association between Player and Position.  The reason for this is that I am picturing the XML that will contain this data to look somewhat like this:

<MFL>

<Position/>

<Position/>

<Position/>

<Team>

    <Player>

        <Statistic/>

    </Player>

</Team>

</MFL>

Statistic will be under its associated Player node, and Player will be under its associated Team node – no need to have an Id to reference it if we know it will always fall under its parent.  Position, however, is more of a lookup value that will not have any hierarchical relationship to the player.  In fact, the Position data itself may be in a completely different xml file (something I’d like to play around with), so in any case, a player will need to reference the position by its Id.

So now that we have a simple data model laid out, I would like to generate two things based on it: 

  • A class for each entity with properties corresponding to each entity property
  • An IO class with methods to get data for each entity, either all instances, by Id or by parent.

Now my experience with code generation in the past has consisted of writing up little apps that use the code dom directly to regenerate code on demand (or using tools like CodeSmith).  Surely, there has got to be a more fun way to do this given that we are using the Entity Framework which already has built-in code generation for SQL Server support.  Let’s start with that built-in stuff to give us a base to work off of.  Right click anywhere in the canvas of our model and select ‘Add Code Generation Item’:

image

So just adding that code item seemed to do quite a bit towards what I was intending:

image

It apparently generated a class for each entity, but also a whole ton more.  I mean a TON more.  Way too much complicated code was generated – now that code is likely to be a black box anyway so it shouldn’t matter, but we need to understand how to make this work the way we want it to work, so let’s get ready to do some stumbling through that text template (tt) file.

When I open the .tt file that was generated, right off the bat I realize there is going to be trouble… there is no color coding, no intellisense no nothing!  That is going to make ‘stumbling through’ more like ‘groping blindly in the dark while handcuffed and hopping on one foot’, which was one of the alternate titles I was considering for this blog.  Thankfully, the community comes to my rescue and I won’t have to cast my mind back to the glory days of coding in VI (look it up, kids).  Using the ‘Extension Manager’ (Available under the ‘Tools’ menu), I did a quick search for ‘tt editor’ in the Online Gallery and quickly found the ‘Tangible T4 Editor’:

image

Downloading and installing this was a breeze, and after doing so I got some color coding and intellisense while editing the tt files.  If you will be doing any customizing of tt files, I highly recommend installing this extension.  Next, we’ll see if that is enough help for us to tweak that tt file to do the kind of code generation that we want

Stumbling Through: Making a case for the K2 Case Management Framework

I have recently attended a three-day training session on K2’s Case Management Framework (CMF), a free framework built on top of K2’s blackpearl workflow product, and I have come away with several different impressions for some of the different aspects of the framework. 

Before we get into the details, what is the Case Management Framework?  It is essentially a suite of tools that, when used together, solve many common workflow scenarios.  The tool has been developed over time by K2 consultants that have realized they tend to solve the same problems over and over for various clients, so they attempted to package all of those common solutions into one framework.  Most of these common problems involve workflow process that aren’t necessarily direct and would tend to be difficult to model.  Such solutions could be achieved in blackpearl alone, but the workflows would be complex and difficult to follow and maintain over time.  CMF attempts to simplify such scenarios not so much by black-boxing the workflow processes, but by providing different points of entry to the processes allowing them to be simpler, moving the complexity to a middle layer.  It is not a solution in and of itself, development is still required to tie the pieces together.

CMF is under continuous development, both a plus and a minus in that bugs are fixed quickly and features added regularly, but it may be difficult to know which versions are the most stable.  CMF is not an officially supported K2 product, which means you will not get technical support but you will get access to the source code.

The example given of a business process that would fit well into CMF is that of a file cabinet, where each folder in said file cabinet is a ‘case’ that contains all of the data associated with one complaint/customer/incident/etc. and various users can access that case at any time and take one of a set of pre-determined actions on it.  When I was given that example, my first thought was that any workflow I have ever developed in the past could be made to fit this model – there must be more than just this model to help decide if CMF is the right solution.  As the training went on, we learned that one of the key features of CMF is SharePoint integration as each ‘case’ gets a SharePoint site created for it, and there are a number of excellent web parts that can be used to design a portal for users to get at all the information on their cases.  While CMF does not require SharePoint, without it you will be missing out on a huge portion of functionality that CMF offers.  My opinion is that without SharePoint integration, you may as well write your workflows and other components the old fashioned way.

When I heard that each case gets its own SharePoint site created for it, warning bells immediately went off in my head as I felt that depending on the data load, a CMF enabled solution could quickly overwhelm SharePoint with thousands of sites – so we have yet another deciding factor for CMF:  Just how many cases will your solution be creating?  While it is not necessary to use the site-per-case model, it is one of the more useful parts of the framework.  Without it, you are losing a big chunk of what CMF has to offer.

When it comes to developing on top of the Case Management Framework, it becomes a matter of configuring what makes up a case, what can be done to a case, where each action on a case should take the user, and then typing up actions to case statuses.  This last step is one that I immediately warmed up to, as just about every workflow I’ve designed in the past needed some sort of mapping table to set the status of a work item based on the action being taken – definitely one of those common solutions that it is good to see rolled up into a re-useable entity (and it gets a nice configuration UI to boot!).  This concept is a little different than traditional workflow design, in that you don’t have to think of an end-to-end process around passing a case along a path, rather, you must envision the case as central object with workflow threads branching off of it and doing their own thing with the case data.  Certainly there can be certain workflow threads that get rather complex, but the idea is that they RELATE to the case, they don’t BECOME the case (though it is still possible with action->status mappings to prevent certain actions in certain cases, so it isn’t always a wide-open free for all of actions on a case).

I realize that this description of the Case Management Framework merely scratches the surface on what the product actually can do, and I don’t think I’ve conclusively defined for what sort of business scenario you can make a case for Case Management Framework.  What I do hope to have accomplished with this post is to raise awareness of CMF – there is a (free!) product out there that could potentially simplify a tangled workflow process and give (for free!) a very useful set of SharePoint web parts and a nice set of (free!) reports.  The best way to see if it will truly fit your needs is to give it a try – did I mention it is FREE?  Er, ok, so it is free, but only obtainable at this time for K2 partners…

Stumbling Through: Visual Studio 2010 (Part I)

I’ve spent the better part of the last two years doing nothing but K2 workflow development, which until very recently could only be done in Visual Studio 2005 so I am a bit behind the times. I seem to have skipped over using Visual Studio 2008 entirely, and I am now ready to stumble through all that I’ve missed. Not that I will abandon my K2 ramblings, but I need to get back to some of the other technologies I am passionate about but haven’t had the option of working with them on a day-to-day basis as I have with K2 blackpearl. Specifically, I am going to be focusing my efforts on what is new in the Entity Framework and WPF in Visual Studio 2010, though you have to keep in mind that since I have skipped VS 2008, I may be giving VS 2010 credit for things that really have been around for a while (hey, if I haven’t seen it, it is new to me!).

I have the following simple goals in mind for this exercise:

· Entity Framework – Model an inherited class

· Entity Framework – Model a lookup entity

· WPF – Bind a list of entities

· WPF - on selection of an entity in the bound list, display values of the selected entity

· WPF – For the lookup field, provide a dropdown of potential values to lookup

All of these goals must be accomplished using as little code as possible, relying on the features we get out of the box in Visual Studio 2010. This isn’t going to be rocket science here, I’m not even looking to get or save this data from/to a data source, but I gotta start somewhere and hopefully it will grow into something more interesting.

For this exercise, I am going to try to model some fictional data about football players and personnel (maybe turning this into some sort of NFL simulation game if I lose my job and can play with this all day), so I’ll start with a ‘Person’ class that has a name property, and extend that with a ‘Player’ class to include a Position lookup property. The idea is that a Person can be a Player, Coach or whatever other personnel type may be associated with a football team but we’ll only flesh out the ‘Player’ aspect of a person for this.

So to get started, I fired up Visual Studio 2010 and created a new WPF Application:

clip_image002

To this project, I added a new ADO.NET Entity Data Model named ‘PlayerModel’ (for now, not sure what will be an appropriate name so this may be revisited):

clip_image004

I chose for it to be an empty model, as I don’t have a database designed for this yet:

clip_image005

Using the toolbox, I dragged out an entity for each of the items we identified earlier: Person, Player and Position, and gave them some simple properties (note that I kept the default ‘Id’ property for each of them):

clip_image007

Now to figure out how to link these things together the way I want to – first, let’s try to tell it that ‘Player’ extends ‘Person’. I see that ‘Inheritance’ is one of the items in the toolbox, but I can’t seem to drag it out anywhere onto the canvas. However, when I right-click an element, I get the option to Add Inheritance to it, which gives us exactly what we want:

clip_image008

Ok, now that we have that, how do we tell it that each player has a position? Well, despite ‘association’ being in the toolbox, I have learned that you can’t just drag and drop those elements so I right click ‘Player’ and select ‘Add -> Association’ to get the following dialog:

clip_image009

I see the option here to ‘Add foreign key properties’ to my entities – I’ve read somewhere this this is a new and highly-sought after feature so I’ll see what it does. Selecting it includes a ‘PositionId’ on the Player element for me, which seems pretty database-centric and I would like to see if I can live without it for now given that we also got the ‘Position’ property out of this association. I’ll bring it back into the fold if it ends up being useful later. Here is what we end up with now:

clip_image011

Trying to compile this resulted in an error stating that the ‘Player’ entity cannot have an Id, because the ‘Person’ element it extends already has a property named Id. Makes sense, so I remove it and compile again. Success, but with a warning… but success is a good thing so I’ll pretend I didn’t see that warning for now. It probably has to do with the fact that my Player entity is now pretty useless as it doesn’t have any non-navigation properties.

So things seem to match what we are going for, great… now what the heck do we do with this? Let’s switch gears and see what we get for free dealing with this model from the UI. Let’s open up the MainWindow.xaml and see if we can connect to our entities as a data source. Hey, what’s this? Have you read my mind, Visual Studio? Our entities are already listed in the Data Sources panel:

clip_image013

I do notice, however, that our ‘Player’ entity is missing. Is this due to that compilation warning? I’ll add a bogus property to our player entity just to see if that is the case… no, still no love. The warning reads: “Error 2062: No mapping specified for instances of the EntitySet and AssociationSet in the EntityContainer PlayerModelContainer. ” Well if everything worked without any issues, then I wouldn’t be stumbling through at all, so let’s get to the bottom of this. My good friend google indicates that the warning is due to the model not being tied up to a database. Hmmm, so why don’t ‘Players’ show up in my data sources? A little bit of drill-down shows that they are, in fact, exposed under ‘Positions’:

clip_image015

Well now that isn’t quite what I want. While you could get to players through a position, it shouldn’t be that way exclusively. Oh well, I can ignore that for now – let’s drag ‘Players’ out onto the canvas after selecting ‘List’ from the dropdown:

clip_image017

Hey, what the heck? I wanted a list not a listview. Get rid of that list view that was just dropped, drop in a listbox and then drop the ‘Players’ entity into it. That will bind it for us. Of course, there isn’t any data to show, which brings us to the really hacky part of all this and that is to stuff some test data into our view source without actually getting it from any data source. To do this through code, we need to grab a reference to the ‘positionsPlayersViewSource’ resource that was created for us when we dragged out our Players entity. We then set the source of that reference equal to a populated list of Players.  We’ll add a couple of players that way as well as a few positions via the positionsViewSource resource, and I’ll ensure that each player has a position specified.  Ultimately, the code looks like this:

System.Windows.Data.CollectionViewSource positionViewSource = ((System.Windows.Data.CollectionViewSource)(this.FindResource("positionsViewSource")));

            List<Position> positions = new List<Position>();

            Position newPosition = new Position();
            newPosition.Id = 0;
            newPosition.Name = "WR";
            positions.Add(newPosition);
            newPosition = new Position();
            newPosition.Id = 1;
            newPosition.Name = "RB";
            positions.Add(newPosition);
            newPosition = new Position();
            newPosition.Id = 2;
            newPosition.Name = "QB";
            positions.Add(newPosition);

            positionViewSource.Source = positions;

            System.Windows.Data.CollectionViewSource playerViewSource = ((System.Windows.Data.CollectionViewSource)(this.FindResource("positionsPlayersViewSource")));

            List<Player> players = new List<Player>();

            Player newPlayer = new Player();
            newPlayer.Id = 0;
            newPlayer.Name = "Test Dude";
            newPlayer.Position = positions[0];
            players.Add(newPlayer);
            newPlayer = new Player();
            newPlayer.Id = 1;
            newPlayer.Name = "Test Dude II";
            newPlayer.Position = positions[1];
            players.Add(newPlayer);
            newPlayer = new Player();
            newPlayer.Id = 2;
            newPlayer.Name = "Test Dude III";
            newPlayer.Position = positions[2];
            players.Add(newPlayer);

            playerViewSource.Source = players;

Now that our views are being loaded with data, we can go about tying things together visually. Drop a text box (to show the selected player’s name) and a combo box (to show the selected player’s position). Drag the ‘Positions’ entity from the data sources panel to the combo box to wire it up to the positions view source. Click the text box that was dragged, and find its ‘Text’ property in the properties pane. There is a little glyph next to it that displays ‘Advanced Properties’ when hovered over – click this and then select ‘Apply Data Binding’. In the dialog that appears, we can select the current player’s name as the value to bind to:

clip_image019

Similarly, we can wire up the combo box’s ‘SelectedItem’ value to the current player’s position:

clip_image021

When the application is executed and we navigate through the various players, we automatically get their name and position bound to the appropriate fields:

clip_image022

clip_image023

clip_image024

All of this was accomplished with no code save for loading the test data, and I might add, it was pretty intuitive to do so via the drag and drop of entities straight from the data sources panel. So maybe all of this was old hat to you, but I was very impressed with this experience and I look forward to stumbling through the caveats of doing more complex data modeling and binding in this fashion. Next up, I suppose, will be figuring out how to get the entities to get real data from a data source instead of stuffing it with test data as well as trying to figure out why Players ended up being under Positions in the data sources panel.

Stumbling Through – Clearing Test K2 Data

In many K2 projects I've worked on, we end up having to tie existing business data into the various workflow processes.  While k2 provides a means to do this via process/activity data fields, they tend to be difficult to query against and can be inefficient compared to an existing schema that has been optimized specifically for the business data involved.  That, and why store the same data twice if it already exists in a database?  That discussion is not the point of this blog entry, though it is an interesting topic that I may revisit later. For now, lets roll with the assumption that we have data in a custom database that is related to K2 process instances. 


During development and testing cycles, one of the tedious activities for me has been trying to clean up old test data, particularly when there are thousands of process instances involved.  The reason for this is because cleaning data in the scenario described above is a two-step process:  First delete the custom database data and then delete the K2 process instances.  Deleting the custom database data is no problem - write a quick SQL script and run it as needed but cleaning the K2 process instances is a different story...


There are two main ways to clear K2 process instances:  via API or via Workspace.  While we could write a program that would execute our database clean up scripts and then iterate through and delete our K2 process instances via API, but that is too much work for this lazy busy developer.  The workspace approach can be a real hassle when there are thousands of process instances because let's face it - deleting more than 100 process instances at a time is not a snappy experience through today's workspace. 


After wrestling with clearing test data repeatedly through the workspace, I finally posed myself the question:  Why is it that we can't we delete process instances through the same SQL script we use to clear the custom data?  Surely, the workspace uses some sort of stored procedure(s) when it deletes a process instance, right?  I started digging around through the K2 databases to see if any stored procedures jumped out at me as obvious choices and while there were a few likely candidates, none of them fit the bill.  Frustrated, I posed the question on the K2Underground and was quickly directed to an article that came close to what I was trying to accomplish.  Using the code in this article as a base, I tweaked it until it was capable of clearing all process instances for a given folder (could easily work for a given process name as well) and it has been so useful to me that I figured I'd share it here.  Be warned, this is slapped together for development assistance, and should by no means be used on a production box!  Happy data clearing...

*** UPDATE 3/15/2010 – I’ve gone through and verified that all tables were being cleared, and updated the script accordingly ***

/*
    Delete all workflow process instances
*/
Use K2Server
DECLARE @FOLDERNAME NVARCHAR(1024)
SET @FOLDERNAME = '[Folder name]'

PRINT 'Starting to remove Process Instances for ' + @FOLDERNAME

PRINT 'Getting List of Process Instances'
SELECT inst.ID
INTO #TMP
FROM dbo.[_ProcInst] AS inst
INNER JOIN dbo.[_Proc] AS prc
       ON (inst.ProcID = prc.ID)
INNER JOIN dbo.[_ProcSet] AS pset
       ON (prc.ProcSetID = pset.ID)
WHERE pset.[Folder] = @FOLDERNAME;

PRINT 'Removing Process Instances from _IPC'
DELETE _IPC
FROM _IPC
INNER JOIN #TMP
ON SrcProcInstID = #TMP.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _IPC'

PRINT 'Removing Process Instances from _Async'
DELETE _Async
FROM _Async
INNER JOIN #TMP
ON ProcInstID = #TMP.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _Async'

PRINT 'Removing Process Instances from _ErrorLog'
DELETE _ErrorLog
FROM _ErrorLog
INNER JOIN #TMP
ON ProcInstID = #TMP.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ErrorLog'

PRINT 'Removing Process Instances from _FieldProcInst'
DELETE _FieldProcInst
FROM _FieldProcInst
INNER JOIN #TMP
ON ProcInstID = #TMP.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _FieldProcInst'

PRINT 'Removing Process Instances from _FieldActInst'
DELETE _FieldActInst
FROM _FieldActInst
INNER JOIN #TMP
ON ProcInstID = #TMP.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _FieldActInst'

PRINT 'Removing Process Instances from _FieldActInstDest'
DELETE _FieldActInstDest
FROM _FieldActInstDest
INNER JOIN #TMP
ON ProcInstID = #TMP.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _FieldActInstDest'

PRINT 'Removing Process Instances from _FieldSlot'
DELETE _FieldSlot
FROM _FieldSlot
INNER JOIN #TMP
ON ProcInstID = #TMP.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _FieldSlot'

PRINT 'Removing Process Instances from _FieldOnDemand'
DELETE _FieldOnDemand
FROM _FieldOnDemand
INNER JOIN #TMP
ON ProcInstID = #TMP.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _FieldOnDemand'

PRINT 'Removing Process Instances from _IPCReturn'
DELETE _IPCReturn
FROM _IPCReturn
INNER JOIN #TMP
ON SrcProcInstID = #TMP.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _IPCReturn'

PRINT 'Removing Process Instances from _Log'
DELETE _Log
FROM _Log
INNER JOIN #TMP
ON ProcInstID = #TMP.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _Log'

PRINT 'Removing Process Instances from _LogProcInst'
DELETE _LogProcInst
FROM _LogProcInst
INNER JOIN #TMP
ON ProcInstID = #TMP.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _LogProcInst'

PRINT 'Removing Process Instances from _ProcInstDestQueue'
DELETE _ProcInstDestQueue
FROM _ProcInstDestQueue
INNER JOIN #TMP
ON ProcInstID = #TMP.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ProcInstDestQueue'

PRINT 'Removing Process Instances from _ProcInstDestQueue'
DELETE _ProcInstDestQueue
FROM _ProcInstDestQueue
INNER JOIN #TMP
ON ProcInstID = #TMP.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ProcInstDestQueue'

PRINT 'Removing Process Instances from _ServerList'
DELETE _ServerList
FROM _ServerList
INNER JOIN #TMP
ON ProcInstID = #TMP.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ServerList'

PRINT 'Removing Process Instances from _WorklistSlot'
DELETE _WorklistSlot
FROM _WorklistSlot
INNER JOIN #TMP
ON ProcInstID = #TMP.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _WorklistSlot'

PRINT 'Removing Process Instances from _WorklistHeader'
DELETE _WorklistHeader
FROM _WorklistHeader
INNER JOIN #TMP
ON ProcInstID = #TMP.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _WorklistHeader'

PRINT 'Removing Process Instances from _ActionActInstShared'
DELETE _ActionActInstShared
FROM _ActionActInstShared
INNER JOIN #TMP
ON ProcInstID = #TMP.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ActionActInstShared'

PRINT 'Removing Process Instances from _ActionActInstRights'
DELETE _ActionActInstRights
FROM _ActionActInstRights
INNER JOIN #TMP
ON ProcInstID = #TMP.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ActionActInstRights'

PRINT 'Removing Process Instances from _ProcInst'
DELETE _ProcInst
FROM _ProcInst
INNER JOIN #TMP
ON _ProcInst.ID = #TMP.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ProcInst'

PRINT 'Completed removing completed process instances from K2ServerLog for folder ' + @FOLDERNAME

DROP TABLE #TMP

Use K2ServerLog

PRINT 'Starting to remove Process Instances for ' + @FOLDERNAME

PRINT 'Getting List of Process Instances'
SELECT ID INTO #TMP2 FROM _ProcInst
WHERE PROCID IN
(
    SELECT ID
    FROM _PROC
    WHERE PROCSETID IN
    (
        SELECT ID
        FROM _PROCSET
        WHERE FOLDER =  @FOLDERNAME
    )
)

PRINT 'Removing Process Instances from _ActInst'
DELETE _ActInst
FROM _ActInst INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ActInst'

PRINT 'Removing Process Instances from _ActInstAudit'
DELETE _ActInstAudit
FROM _ActInstAudit
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ActInstAudit'

PRINT 'Removing Process Instances from _ActInstData'
DELETE _ActInstData
FROM _ActInstData
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ActInstData'

PRINT 'Removing Process Instances from _ActInstDataAudit'
DELETE _ActInstDataAudit
FROM _ActInstDataAudit
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ActInstDataAudit'

PRINT 'Removing Process Instances from _ActInstDest'
DELETE _ActInstDest
FROM _ActInstDest
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ActInstDest'

PRINT 'Removing Process Instances from _ActInstDestData'
DELETE _ActInstDestData
FROM _ActInstDestData
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ActInstDestData'

PRINT 'Removing Process Instances from _ActInstDestDataAudit'
DELETE _ActInstDestDataAudit
FROM _ActInstDestDataAudit
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ActInstDestDataAudit'

PRINT 'Removing Process Instances from _ActInstDestXml'
DELETE _ActInstDestXml
FROM _ActInstDestXml
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ActInstDestXml'

PRINT 'Removing Process Instances from _ActInstDestXmlAudit'
DELETE _ActInstDestXmlAudit
FROM _ActInstDestXmlAudit
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ActInstDestXmlAudit'

PRINT 'Removing Process Instances from _ActInstSlotData'
DELETE _ActInstSlotData
FROM _ActInstSlotData
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ActInstSlotData'

PRINT 'Removing Process Instances from _ActInstSlotDataAudit'
DELETE _ActInstSlotDataAudit
FROM _ActInstSlotDataAudit
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ActInstSlotDataAudit'

PRINT 'Removing Process Instances from _ActInstSlotXml'
DELETE _ActInstSlotXml
FROM _ActInstSlotXml
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ActInstSlotXml'

PRINT 'Removing Process Instances from _ActInstSlotXmlAudit'
DELETE _ActInstSlotXmlAudit
FROM _ActInstSlotXmlAudit
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ActInstSlotXmlAudit'

PRINT 'Removing Process Instances from _ActInstXml'
DELETE _ActInstXml
FROM _ActInstXml
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ActInstXml'

PRINT 'Removing Process Instances from _ActInstXmlAudit'
DELETE _ActInstXmlAudit
FROM _ActInstXmlAudit
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ActInstXmlAudit'

PRINT 'Removing Process Instances from _EscInst'
DELETE _EscInst
FROM _EscInst
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _EscInst'

PRINT 'Removing Process Instances from _EventInst'
DELETE _EventInst
FROM _EventInst
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _EventInst'

PRINT 'Removing Process Instances from _IPC'
DELETE _IPC
FROM _IPC
INNER JOIN #TMP2
ON SrcProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _IPC'

PRINT 'Removing Process Instances from _LogBatch'
DELETE _LogBatch
FROM _LogBatch
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _LogBatch'

PRINT 'Removing Process Instances from _LineInst'
DELETE _LineInst
FROM _LineInst
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _LineInst'

PRINT 'Removing Process Instances from _ProcEscInst'
DELETE _ProcEscInst
FROM _ProcEscInst
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ProcEscInst'

PRINT 'Removing Process Instances from _ProcInstAudit'
DELETE _ProcInstAudit
FROM _ProcInstAudit
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ProcInstAudit'

PRINT 'Removing Process Instances from _ProcInstData'
DELETE _ProcInstData
FROM _ProcInstData
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ProcInstData'

PRINT 'Removing Process Instances from _ProcInstDataAudit'
DELETE _ProcInstDataAudit
FROM _ProcInstDataAudit
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ProcInstDataAudit'

PRINT 'Removing Process Instances from _ProcInstRevision'
DELETE _ProcInstRevision
FROM _ProcInstRevision
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ProcInstRevision'

PRINT 'Removing Process Instances from _ProcInstXml'
DELETE _ProcInstXml
FROM _ProcInstXml
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ActInst'

PRINT 'Removing Process Instances from _ProcInstXmlAudit'
DELETE _ProcInstXmlAudit
FROM _ProcInstXmlAudit
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ProcInstXmlAudit'

PRINT 'Removing Process Instances from _Worklist'
DELETE _Worklist
FROM _Worklist
INNER JOIN #TMP2
ON ProcInstID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _Worklist'

PRINT 'Removing Process Instances from _ProcInst'
DELETE _ProcInst
FROM _ProcInst
INNER JOIN #TMP2
ON _ProcInst.ID = #TMP2.ID
PRINT CAST(@@ROWCOUNT AS VARCHAR(10)) + ' records removed from _ProcInst'

PRINT 'Completed removing completed process instances from K2ServerLog for folder ' + @FOLDERNAME

DROP TABLE #TMP2

Stumbling through: K2 blackpearl 090x (Part II)

So we left off last time with the new ‘Configuration Analyzer’ tool (part of the K2 blackpearl 090x installation) telling me that I am an idiot – I tried to install the product without installing or configuring any of the server dependencies.  I see this nice, shiny ‘Repair All’ button sitting prominently on the wizard results.  Just how much can this actually repair for me?  Sadly, at least in this beta phase, it did nothing to satisfy my lazy ways.  Another downside (again, perhaps due to a beta) is that clicking the ‘help’ link for any of the configuration errors gives only Windows Server 2003 help on how to address the issue – not very helpful to me since I am on Windows Server 2008 and not bright enough to figure it out for myself.  As always, though, the k2undeground came to my rescue (thank you, Vernon): 

tbyrne Posted: 10-27-2009 1:57 PM

I am trying to install the 090x beta on a new farm running windows server 2008 and SQL Server 2008.  The installation seemed to be successful, but the configuration analysis has a few issues that I can't figure out how to address (likely due to my inexperience with windows server 2008 and the fact that the help is specifically for windows server 2003)

First off, is 090x supposed to support windows server 2008?

Some of the issues I get are around Message Queuing not being enabled (it is, but I'm not sure if I did it right as the instructions within the installer do not match windows server 2008) and MSDTC Network Access which doesn't seem to have the configuration options available that the help is pointing me to.  Finally, there is a big nasty error when I select the 'Set Site Negotiation' node under the K2 workspace analysis.

Does anybody have any insight into these issues?

Tim Byrne Clarity Consulting http://claritycon.com

Vernon replied on 10-27-2009 6:27 PM

Yes Server 2008 is supported in 090x. As for the setting up of the DTC. In the administrator tools > Component Services > expand till you see the DTC Folder and in it the Local DTC. Right click > Properties and then under security you will find the settings you need.

For MSMQ you can check under Service if the service is running. To install it please see the following link: http://msdn.microsoft.com/en-us/library/aa967729.aspx

What is the big nasty error for the Set site Negotiation?

Vernon

tbyrne replied on 10-28-2009 9:26 AM

Thank you, Vernon, your tips pointed me into the right direction in regards to DTC and MSMQ.  As for the big nasty error (what, that isn't descriptive enough for you?) it seems to have resolved itself once I had the other components set up.  Thanks!

Tim Byrne Clarity Consulting http://claritycon.com

After following the helpful advice given to me in this thread, I was able to eliminate all of the analyzer errors save for one – it was telling me that my workspace web site was set up to use NTLM authentication and it needed to be negotiate for Kerberos.  On a whim, I clicked this error node and then clicked the ‘Repair’ button and just like that, my problem was solved!

So it appears as if I have successfully stumbled through my first installation of K2 blackpearl 090x, and I have to say it went far more smoothly than any installation of their previous versions, due mostly to the new configuration analyzer.  While it didn’t simply resolve all of my issues without my involvement, it did manage to address one and at least provide feedback on what else needs my attention.  This is much better than in the previous versions of the product where you didn’t know something wasn’t configured correctly until you tried to build/deploy/run your first workflow, and then it was off to the forums or K2 support to figure out what exactly is wrong.

Things are going far too well with this, I haven’t muttered a single curse or thrown anything through my monitor yet.  Will things continue to go on this way?  We’ll see, as I start digging in to the details of some of the new features that 090x is bringing to the table.  They all looked good on paper and in the limited demos I’ve seen, but my next few posts will prove if things really are as rosy as they seem.

Stumbling Through: K2 blackpearl 090x (Part I)

Wow, it has been a while since I've posted anything.  That isn't to say that I haven't been stumbling through things, quite the contrary, but I haven't been doing anything lately that hasn't been covered pretty extensively elsewhere... until now.

K2 has recently released a beta for its next major revision to the blackpearl product line, currently codenamed '090x'.  This release adds tons of new features, many which will completely revolutionize the way that I have been designing workflows with the product.

One new feature that has already had a profound impact on my blackpearl experience is the ‘Configuration Analyzer’.  This little tool runs after installing the product and looks for common configuration issues and even has an option to automatically repair the issue.  The reason that this tool is so helpful is because there are a lot of other components involved with a complete K2 blackpearl installation, and each of these components has a boatload of configuration options that you, as the K2 installer, may not have configured yourself.  It makes it very difficult at times to determine why a feature of the product isn’t working without going through reams of documentation and retracing all of your steps.  Why not have the product itself tell you why it isn’t working?  Sounds good in theory, let’s take a look at it in practice.

So in my particular scenario, I was taking advantage of the fact that K2 blackpearl 090x now supports Windows Server 2008 and SQL Server 2008, two products that I have had no previous experience with.  Many of the items that K2 depends on weren’t in the same places I was used to finding them in Windows Server 2003 so I just installed K2 blackpearl 090x without setting up any dependencies and let it tell me what was wrong:

ConfigurationAnalyzer

Ouch, that is a lot of big red ‘X’s, and there are a couple more when I scroll down.  That can’t be good for any installation, though what can I expect without doing any of the leg work up front?  So, how will I fix them?  Will this analyzer fix them form me?  We’ll tackle that in part II.

Stumbling Through: Getting to the Point

To raise awareness of its new product, blackpoint, K2 is running a game that is open to anybody and includes such prizes as a 30" flat panel monitor, a Canon PowerShot Pro digital camera and a Nintendo WII.  Playing the game is simple - register here and after doing so, you will see links on the right hand menu pertaining to clues.  These clues are hints as to which sites may have a points card on them, a points card being an orange bull's-eye with a code inside.  Use that code to redeem the points on the K2site.  Happy hunting!

Stumbling Through: K2 blackpearl - InfoPath Integration (Part III)
Technorati Tags: ,,,

Well, it has certainly been a while since I revisited my InfoPath Integration thread, and unfortunately, so much has changed in the direction of my 'real world' scenario that I am no longer attempting to tackle the same issues that I presented at the beginning of the thread.  Not that I'm not tackling the same issues, I guess, more like I've done a complete 180 on how I had originally planned to solve the problems.  So I will be abandoning the InfoPath Integration thread and hopefully starting a new one shortly that is centered around smart object implementation and maybe even an interesting new product by K2 called 'blackpoint'. 

There isn't much I can say about blackpoint that hasn't aleady been said, as I have not gotten my hands dirty with the product yet other than reading up on its spec sheet, watching a couple of demos and learning the target audience of the product.  blackpoint attempts to find the middle ground between SharePoint workflow and full-blown blackpearl - it is between the two products in terms of capabilities but is designed to be more user-friendly (non-developer user, that is) than blackpearl.  So gone are smart objects and code-behind, but new is a more user-friendly design surface.  I hope stumble through this new product a bit as time goes on, so more posts on this topic to follow.

Stumbling Through: K2 [blackpearl] For-Each Loop Simulation
Technorati Tags: ,,,

I know I know, I am way behind on my InfoPath Integration series that I started several weeks back, but still, I'd like to go off on a tangent here on a somewhat tricky methodology for accomplishing something that is probably pretty common in a lot of workflow scenarios.  That scenario is this:  Lets say we have a list of zero to many items, where it is unknown how many items may be in said list but we need to do some sort of workflow process on each item in the list.  Specifically, we will use an InfoPath template with a repeating table as our example.  In this InfoPath template, the user may add as many rows as they like to the repeating table which we will call 'Child' for simplicity sake.  Each 'Child' row has a 'Name' and a 'Description' field.  In our workflow process, we need to loop through each 'Child' row and create a smart object for it, so it is persisted to a data store.

I am going to make a couple of assumptions here before I get started with my example.  I am assuming there is an InfoPath form with the 'Child' repeating table present as defined above, and there is a simple smart object defined with a 'Name' and 'Description' property.  I am also assuming that the InfoPath form is integrated with the process which we will be using as an example, with the process being started on submission of the Infopath form.

Given our assumptions, the first step to simulating a For-Each loop is to drag out a Smart Object event which is going to be our action taken on each iteration of the loop.  Map this Smart Object event to our 'Child' smart object's 'Create' method.  Leave the input and output parameters blank for now, we'll need to extract them in our loop.  What we are doing here is providing the action to take place on every iteration of the loop, which in this case is to create a 'Child' smart object:

image

Now we need to go about telling the activity that it is to be executed for each child row in the InfoPath form.  It is not very intuitive to do this, which is why I'm blogging about it in great detail.  I'll try and explain each step as best I can.  The first step is to bring up the activity's 'Destination Rule' wizard:

image

Once the rule wizard starts, we actually need to GO BACK a screen (Click the 'Back' button), so we can tell the wizard we want to run in advanced mode:

image

image

Now that we are in advanced mode, click 'Next' and we are presented with a list of options for specifying when instances of this activity should be created.  We want to specify 'Plan per slot (no destinations)', which means that this activity is not going to an actual destination and that we will be creating x number of instances of the activity:

image

Click 'Next' and we are presented with a special screen based on our previous selection of 'no destinations'.  In this screen, we are being asked to specify how many instances of the activity we will be creating.  The first option assumes we know the exact number of slots to create, which we don't since we are relying on the InfoPath repeating table to tell us this.  The second option allows us to specify any repeating field (be it a repeating xml node or a smart object list) to iterate through, creating an instance of the activity for each iteration.  This is exactly what we want, so select this option.  In the field entry, specify the repeating 'Child' node from our InfoPath form which instructs it to create an activity instance for each row in this repeating table:

image

Click 'Finish' to finalize the destination rule wizard.  What would happen now if we ran this process is that for each 'Child' row inserted into the InfoPath form, there will be a blank smart object record inserted into the Smart Box database.  Why will the smart object values be blank?  We never mapped the 'Create' method's input parameters to the values being extracted from the child node iteration.  Now there is good news, bad news and worse news here:  The good news is that all of the data from the child node of the current iteration is available in the 'ActivityDestinationInstance.InstanceData' field:

image

The bad news is that, as you can see, there is no design-time context for the underlying data in this field so we can't use the interface to map values to the Create method.  No problem, we can always create our data fields manually in server code, parsing the individual data fields from the InstanceData into our own data fields.  Here is the worse news.  The InstanceData field concatenates the data of its repeating node into one giant non-delimited string, making it very difficult to extract individual values.  I am currently working with the product support team to see if there is a better way to get at our iteration data, but for now this is only a viable solution if you either have just one child value or if you can make assumptions on data locations and sizes in your InstanceData.  I'll post an update here if and when I learn a more effective way to get at the iteration data.

UPDATE - forgot to come back and revisit this (among other things), but the best approach I found to getting the appropriate data for your current iteration is to keep a process instance data field as an index, incrementing it in a server code event.  Within that same event (or another one, I guess), you can use that index to get the node directly from the xml data field for that index and stick it into an activity instance xml node to use in your smart object's 'Create' method.  Here is a quick example of what my server code event within the for-each activity looks like ('Policy' is the name of the repeating node):

System.Xml.XmlDocument docSource = new System.Xml.XmlDocument();

docSource.LoadXml(K2.ProcessInstance.XmlFields["Client Services Request Form"].Value);

System.Xml.XmlNamespaceManager nsMgr = new System.Xml.XmlNamespaceManager(docSource.NameTable);

nsMgr.AddNamespace("my", docSource.DocumentElement.GetNamespaceOfPrefix("my"));

int policyIteration = ((int)K2.ProcessInstance.DataFields["Policy Iteration Index"].Value);

System.Xml.XmlNode ndePolicy = docSource.SelectNodes("//my:Policy", nsMgr)[policyIteration];

K2.ActivityInstanceDestination.XmlFields["Policy"].Value = ndePolicy.OuterXml;

 

K2.ProcessInstance.DataFields["Policy Iteration Index"].Value = policyIteration + 1;

Posted: Apr 17 2008, 09:30 AM by tbyrne | with 4 comment(s)
Filed under:
Stumbling Through: K2 [blackpearl] - Infopath Integration (Part II)

Previously, I identified a couple of specific technical problems that I'd like to stumble through solutions for.  One of the technical problems I described like this:

'Submitting one order form may spawn multiple processes that can be acted on simultaneously by different users to accomplish one final result.  How will we have one process split into many, passing along the common information necessary to the child processes?'

I suppose I will clarify this description a little bit before I jump into the solution.  One InfoPath form containing some common user-entered data starts the master process.  Submitting this form starts three other processes that have their own InfoPath forms that need the common information pre-populated from the first InfoPath form.  So, to do a proof of concept for this issue, we'll need a total of four InfoPath forms and four processes defined in a K2 workflow project (Master, Child1, Child2, Child3).  Let's start out with the InfoPath forms:

Create an InfoPath form named 'Master', and add two text fields that will represent the common data.  We can just call them 'Id' and 'Name':

image

Create the next three InfoPath forms named 'Child1', 'Child2' and 'Child3'.  You can put any other information you want in them, as long as you include the 'CommonFields' section as it was defined in the Master form:

image

Now that we have our InfoPath templates defined, we need to consume them in K2 Processes.  Start Visual Studio and create a new empty K2 project:

image

Add four processes to the project named 'Master', 'Child1', 'Child2' and 'Child3':

image

image

Let's start with defining each child process.  They will simply create a task list item pointing to their corresponding InfoPath form which will have the header somehow pre-populated with data entered in the Master form.  First things first, though, we need to integrate the process with its InfoPath form.  Drag out an InfoPath Integration process wizard and follow the steps necessary to integrate the InfoPath form and create a simple client event (I've blogged this process before if you get stuck, you can read it here, though that was blogged long before I semi-knew what I was doing).  Duplicate the process for all three child processes so that they look as simple as this:

image

Note that we did nothing with pre-populating the data in the InfoPath form yet, we'll tackle that later.  For now, we can move on to our Master process.  We'll need to integrate the Master InfoPath form with this process similarly to how we did it with the Child processes, the only difference being that we need to ensure that the Master form starts the process, by filling in this portion of the integration wizard:

image

This will make it so that any time this specific form is submitted, it will automatically start this Master process.  So now we come to the heart of the matter - how do we invoke the child processes all at once?  Thankfully, the developers of this product anticipated this very need and supplies a wizard that will make this challenge a breeze.  Drag out the 'IPC Event' wizard:

image

In the wizard, we'll need to specify which process to invoke.  Let's start out invoking our Child1 process.  It also asks us if it is Synchronous or Asynchronous - basically whether or not we need a response from this child process before we can continue.  In our case, we just want to kick off the process and we don't care what it does so we will chose Asynchronous:

image

We also have the ability here to specify a new folio, if we want the child folio to match something that we are gathering in the parent process.  Be warned, though, that this folio definition will be overridden by any folio definition in the Child process (defined in the InfoPath Integration wizard).  Clicking next brings us to a simple security question - who to call the process as.  Now normally, I would expect to use 'Integrated Windows' here so that the process is invoked by whomever invoked the Master process.  However, for reasons I don't yet understand, this never worked successfully for me and I've had to use the 'Impersonate Originator' option:

image

Clicking next again brings us to a screen that is very interesting to us, given the challenge we are trying to overcome.  It is asking us what data we want to pass to the child from the parent!  That is perfect!  Or is it... Let's see how it handles our specific request of passing the 'CommonFields' data from the Master form to the 'CommonFields' data of the child.  Add a new process field mapping:

image

Use the object browser to drill into our Master infopath form XML representation and select the 'CommonFields' section:

image

And drag it onto the Field Mapping form:

image

Now use the object browser via ellipsis next to the Child process field name to browse to the 'CommonFields' data in the Child InfoPath form:

image

And drag it onto the Field Mapping form... uh oh, it won't let us!  Looks like we are asking it to do something a little more complex than what it was designed for, which is understandable as moving one piece of xml between documents might not always be very straight forward, particularly when namespaces are involved.  Looks like we'll have to do some of this ourselves via code.  Bring up the Child Process object browser again, and add a new xml field called 'ParentCommonFields'.  We'll use that as the temporary container for the common field data:

image

Drag it onto the Field Mapping form:

image

Something else I learned the hard way about this process is that if you specify a portion of an xml document, as we did when we specified 'CommonFields' in the Master document, it will send the data as a list of field values as opposed to the XML.  As such, we'll just send the whole XML over and parse out the CommonFields section in the Child process logic.  So revisit the Parent field value and browse to the Master xml document:

image

Drag it onto the Field Mapping form:

image

Repeat this IPC event process for all three Child processes so your Master process looks like this in the end:

image

Now we need to revisit each Child process and add some logic to copy the parent common fields into the child common fields InfoPath xml.  The only way to do this is with some server side code, so drag out a new 'Default Server Event (Code)' wizard:

image

And connect it up before the InfoPath client event:

image

View the code of this Server Event (Right-Click - View Code - Event Item) and add a 'using System.Xml' to the top to make our XML references cleaner.  Now, what we need to do is load up a DOM with the Child InfoPath form XML so we can navigate to its 'CommonFields' section and replace it with the 'ParentCommonFields' value.  The caveat here is that the Parent Common Fields XML may have a different namespace associated with it, so we'll need to load that up in a DOM too so we can identify and replace any occurrences of an incorrect namespace:

XmlDocument docChild = new XmlDocument();

docChild.LoadXml(K2.ProcessInstance.XmlFields["Child1"].Value);

XmlNamespaceManager nsMgrChild = new XmlNamespaceManager(docChild.NameTable);

nsMgrChild.AddNamespace("my", docChild.DocumentElement.GetNamespaceOfPrefix("my"));

XmlDocument docParent = new XmlDocument();

docParent.LoadXml(K2.ProcessInstance.XmlFields["ParentCommonFields"].Value);

XmlNamespaceManager nsMgrParent = new XmlNamespaceManager(docParent.NameTable);

nsMgrParent.AddNamespace("my", docParent.DocumentElement.GetNamespaceOfPrefix("my"));

docChild.SelectSingleNode("//my:CommonFields", nsMgrChild).InnerXml = docParent.SelectSingleNode("//my:CommonFields", nsMgrParent).InnerXml;

K2.ProcessInstance.XmlFields["Child1"].Value = docChild.InnerXml.Replace(nsMgrParent.LookupNamespace("my"), nsMgrChild.LookupNamespace("my"));

Let's deploy this thing, and when it is done, we will visit the SharePoint list associated with our 'Master' form, add an item, and see if we get tasks generated for each 'Child' form.  Opening up those tasks should show the Master Common Field data in the header.  Don't forget to give yourself process start rights for the master and child processes via the Workspace!  If it doesn't work as expected or I glossed over an important detail, please leave me a comment and I'll try to help as best i can.

Posted: Feb 29 2008, 10:12 AM by tbyrne | with 2 comment(s)
Filed under:
Stumbling Through: K2 [blackpearl] - InfoPath Integration (Part I)

The K2 [blackpearl] product integrates very well with InfoPath, as any sales-pitch and pre-made tutorial will inform you.  I was very impressed with how easily I was able to use InfoPath as the user interface to my simple workflow processes, but I feel that every new technology looks good when you take it through its expected scenarios.  As such, what I am going to try and do for you in the next series of posts is describe my experiences implementing K2 [blackpear] InfoPath integration in the real-world, where the business requirements don't always exactly match the InfoPath integration tutorials.  What I hope for you to get out of this series is the scenarios where InfoPath integration may be more complex than it would seem on the surface, where it will be a quick-win, and some tricks I stumble through along the way to get over some of the hurdles that present themselves.

Here is some background on the project:

Users will be interfacing with SharePoint 2007 to create service request orders, which are sent to a service provider team for processing.  There are four distinct services with their own order forms, and each of these order forms can be either filled out from scratch and submitted ad-hoc, or submitted through an automated process where most of the order form data is pre-filled.

Here are some of the key business requirements that I am going to cover in detail:

There are multiple ways to start the same process.  That is, it is possible for a user to submit an order at any time (ad-hoc) or to approve and submit an order that is automatically generated for them (outside of this system).  This might be tricky because from what I've seen, a process can be tied to only one view of one InfoPath form for process initiation.  The ad-hoc and automated order forms will be different views at the least, maybe even completely different InfoPath forms... how will we get one process to start off of the submission of either one of these forms?

Submitting one order form may spawn multiple processes that can be acted on simultaneously by different users to accomplish one final result.  How will we have one process split into many, passing along the common information necessary to the child processes?

Since there are a number of activities related to one request, the requestor would like to see the progress being made on each activity.  The default task list webpart in SharePoint only shows tasks assigned to the user that is currently logged in.  How will we change this so that it shows all tasks associated with the process that they initiated?

All of the various order forms share a certain subset of data, lending themselves to a hierarchical use of content types to represent a base order form and then each specific order form derived from it.  I did not see any options in my InfoPath integration experimentation to indicate that the integrated form can be turned into a content type that is based on another content type.  How will we get our InfoPath form data to be tied to a base content type?

The task list needs to show various information that exists within the InfoPath form data (such as client name, due date, etc.).  The default task list can only show a static set of data, and the InfoPath integration wizard didn't seem to have a screen to promote fields to columns (a feature available when publishing InfoPath forms via InfoPath 2007).  How (and where) will I be able to show InfoPath form data as columns associated with a task?

Users want to have buttons or links to click in the InfoPath forms to submit their actions with one click.  The InfoPath Integration tutorials all use a drop down list of actions that the user selects from and then clicks a submit button.  How will I get the actions related to the task into buttons or links that know what to do when they are clicked?

If you are interested in seeing the solutions to any of the above problems, keep an eye on this blog as I will be posting solutions as I stumble through them over the next few days.

Posted: Feb 18 2008, 12:48 PM by tbyrne | with 1 comment(s)
Filed under: ,
Stumbling Through - Coding Challenges

While this is a bit of a departure from the theme of my blog, the Coding Challenge recently held by Clarity Consulting for its employees is an event that I feel is worth blogging about.  The reason it is of particular interest to me is that behind my calm, mild-mannered demeanor I am a fiercely competitive person - not from the standpoint that I must win everything I compete in but in that I simply love the thrill of the competition itself.  There is also the fact that on any given work day, I could go home thinking I must be the greatest developer in the world or go home thinking that I must have chosen the wrong profession; outside of my grades in college (which mean absolutely nothing in the real world) I have no way to judge or rank my developer skills.  Thusly, I was very excited to hear about an opportunity to test my abilities against a number of other Clarity consultants, and proceeded to completely ignore preparing in any way, preferring to 'stumble through' it as I do everything in my life (not that there was much to prepare for, really, as you'll see).

Come time for the challenge, I learned that there were going to be just three questions - an 'easy' one, a 'medium' one and a 'hard' one and we would gain points according to how quickly we submitted a working solution to the problem.  Nobody knew what the questions would be until the challenge started, and the timer for the question would begin right when you read it.  The solution would consist of only one class file (either VB or C#) with a pre-determined public method signature that can accept a number of different parameters and return results in a pre-determined format for testing.  After submission, the class would be subjected to a number of tests consisting of various parameter values and the results would be validated (I am assuming an exception would count as failing the test as well).  If any one test failed, you would score a big fat zero for that solution.  This brings me to my one gripe with the challenge:  you could have written the greatest algorithm in the world faster than anybody else but forgot to handle one outside test case (such as null parameter) and you'd get no credit at all!  I had hoped you'd get points per test case that succeeded, but such was not the case.  Turns out, though, that the test cases weren't as hard-core as I thought, as I built my method without any error handling, bounds checking or null handling and manage to score full points so I guess I shouldn't gripe too much.  I would, however, still submit that the test cases should've been tougher as a 'good' developer wouldn't have forgotten to check for null parameter values like I forgot to do.

While I won't post my results here, I will say that I was both humbled by what the other Clarity consultants were capable of and emboldened by my own moderate success.  My experience was somewhat tainted by the fact that I had a specific time I needed to leave by and since we started the challenge very late I was severely handicapped by time.  Not that I am making excuses here, mind you, I have no confidence that I would've done any more had I another 50 hours much less another 50 minutes, but I was very disappointed to leave the challenge in progress.  I am very much looking forward to stumbling through the next tech challenge, I'll make sure I have no responsibilities afterwards this time and therefore, no more pathetic excuses!

All in all, it was a very fun event and is a good example of one of the many things Clarity does to keep a level of excitement and interest in the workplace.

Posted: Feb 15 2008, 12:39 PM by tbyrne | with 3 comment(s)
Filed under:
More Posts Next page »