The Patterns and Practices group have just finished and shipped the Web Client Software Factory. If you haven't looked into software factories before this is a great place to get started, and even if you don't use it in your daily work just studying it will be a great experience.
This software factory make good use of several design pattern. Among the more interesting to be aware of are the View-Presenter and the Service Locator. If nothing else learning these will improve your software design skills
And they have incorporated Windows Workflow Foundation as a possible engine for page navigation. Just take a look at the PageFlow or the PageFlowWithShoppingCart samples to see how it is done.
One negative note though, this Software Factory only generates C#
If you are using a WCF client make sure you either Close() or Dispose() the client when you are done. I failed to do so and got bitten by a big memory leak
I was using a NetMsmqBinding and forgot to close the client when done. By itself the code worked just fine, no leaks or nothing. But then the same piece of code got called from inside another NetMsmqBinding server during the reception of a message and it generated a memory leak big time. Of course I should have Disposed() the client but the IDispose implementation is private so without casting it doesn't show up making it easy to forget, the Close() was public so no excuses there
. The best solution is to use the Using keyword (using in C#) and it will make sure Dispose() is called if it exists.
Another thing that would have saved the day was if the ClientBase(Of TChannel As Class) implemented the IDisposable design pattern instead of just implementing the IDisposable interface. Using the design pattern ensures that the resources are disposed regardless of Dispose() being called. See http://msdn2.microsoft.com/en-us/library/fs2xkftw.aspx
for more information about the IDisposable design pattern.
Is your solution plagued by a large number of assemblies that need to be loaded? Well ILMerge will merge them together into one single assembly. A single assembly is so much easier to distribute after all
Download it here
. Or read more about it over here
One interesting thing is that the readme says "It does not yet support Rotor or Mono". Now how often does Microsoft use the not yet in combination with Mono? Interesting thought!
I am probably the last person to discover this but only today I discovered the Stopwatch type in the System.Diagnostics namespace. Real nice when you want to determine how long a certain action took
Stopwatch timer = Stopwatch.StartNew();
// Do some stuff you want to time.
Use the Stopwatch.IsHighResolution to check if the timer is using high resolution of not.
It has taken a while but it's finally there. And that would be SQL Server Compact Edition, the great little in process database. Great is you need a small database in lots of applications, not just offline smart client/ Admittedly that is one of the places where it shines
Turns out I forgot to include one of the utility functions use in my previous two blog posts at
. The code is the GetAllActivities() function in the WorkflowUtils class.
This is the missing method:
PublicSharedFunction GetAllActivities(ByVal root As Activity) As List(Of Activity)
Dim result AsNew List(Of Activity)
Dim i AsInteger = 0
Dim current As Activity
While i < result.Count
current = result(i)
Dim ca As CompositeActivity = TryCast(current, CompositeActivity)
If ca IsNotNothingThen
i += 1
Another thing I failed to mention and got a few questions about is how the code finds the workflow type. The serialized type is contained in the SqlTrackingWorkflowInstance, or to be more specific in the WorkflowDefinition property. This is deserialized and during the process the related assemblies will be loaded. This means these assemblies, and the correct version if they are signed, must reachable by the process. Basically this means either deploying them to the GAC or copying them to the same directory or one of the directories mentioned in the private probing path. For my example I just copied the workflow assembly to the same directory as the assembly generating the image. Nice and quick for a demo but in a real live environment not quite good enough as, in all likelihood, you will contend with multiple versions of a workflow.
Yesterday I showed in my previous blog
post the minimal code required to create an image from a workflow definition. As I mentioned the most interesting reason for doing so is showing the status of an executing workflow in a sort of monitoring web site. So the next step is to add the ExecutionStatus of each activity to the image. The following image is an example of the result.
This can be done using an IDesignerGlyphProvider in the WorkflowView type. The WorkflowView is already equipped with a IDesignerGlyphProviderService service to with we need to add our IDesignerGlyphProvider implementations. As we need to use the protected GetService to get a reference to the IDesignerGlyphProviderService we need to create a subclass of WorkflowView and add some code.
SubNew(ByVal serviceProvider As IServiceProvider)
PublicSub AddGlyphProvider(ByVal provider As IDesignerGlyphProvider)
Dim service As IDesignerGlyphProviderService
service = GetService(GetType(IDesignerGlyphProviderService))
The next step is to create the IDesignerGlyphProvider implementation itself. Again no big deal. In this example I decided to use the Workflow SqlTrackingQuery to retrieve an actual SqlTrackingWorkflowInstanceinstead instead of a dummy workflow created on the spot. Using this SqlTrackingWorkflowInstance gives me the history of the activity events to display. After all that is wat we want in a monitoring web site. The class I created is DesignerGlyphProvider and it contains a function to add the activity event and the GetGlyphs as gefined by the IDesignerGlyphProvider interface. Again the code is no big deal
Private _activityExecutionStatus AsNew Dictionary(OfString, ActivityExecutionStatus)
PublicFunction GetGlyphs(ByVal activityDesigner As ActivityDesigner) As ActivityDesignerGlyphCollection Implements IDesignerGlyphProvider.GetGlyphs
Dim result AsNew ActivityDesignerGlyphCollection()
If _activityExecutionStatus.ContainsKey(activityDesigner.Activity.QualifiedName) Then
Dim status As ActivityExecutionStatus = _activityExecutionStatus(activityDesigner.Activity.QualifiedName)
PublicSub AddActivityEvents(ByVal trackingRecords As IList(Of ActivityTrackingRecord))
ForEach record As ActivityTrackingRecord In trackingRecords
If _activityExecutionStatus.ContainsKey(record.QualifiedName) Then
_activityExecutionStatus(record.QualifiedName) = record.ExecutionStatus
Of course we need the glyphs to draw on the design surface. I kept things really simple and just used the Winding font to write an appropriate character for each status.
Protected _text AsString = ""
Protected _brush As Brush = Brushes.Black
ProtectedOverridesSub OnPaint(ByVal graphics As Graphics, ByVal activated AsBoolean, ByVal ambientTheme As AmbientTheme, ByVal designer As ActivityDesigner)
Using font AsNew Font("Wingdings", 12)
graphics.DrawString(_text, font, _brush, designer.Bounds)
_text = "J"
_brush = Brushes.Green
_text = "F"
_text = "N"
_brush = Brushes.Red
_text = "I"
_brush = Brushes.Red
That only leaves us with the main function that was slightly updated from yesterday. Of course I added the SqlTrackingQuery to load an actual SqlTrackingWorkflowInstance object. Next I changed the standard WorkflowView used to the custom GlyphProvidedWorkflowView type. The final addition is adding the DesignerGlyphProvider to the GlyphProvidedWorkflowView.
Dim query AsNew SqlTrackingQuery("Data Source=.\sqlexpress;Initial Catalog=WF_Tracking;Integrated Security=True")
Dim options AsNew SqlTrackingQueryOptions
Dim instances As IList(Of SqlTrackingWorkflowInstance) = query.GetWorkflows(options)
Dim instance As SqlTrackingWorkflowInstance = instances(instances.Count - 1)
Dim workflow As Activity = instance.WorkflowDefinition
Dim loader AsNew WorkflowLoader(workflow)
Dim surface AsNew DesignSurface
Dim view AsNew GlyphProvidedWorkflowView(CType(surface, IServiceProvider))
Dim glyphProvider AsNew DesignerGlyphProvider()
I have been experimenting a bit with hosting the workflow designer. Unfortunately the sample provided, while it works is kind of hard to get started with if you are not already familiar with the way designers work in Visual Studio. So I decided to create the minimal code to create an image of a workflow, something quite useful if you are going to with an ASP.NET site to monitor you workflows. One caveat to be aware of is that the code needs to run under a thread with an STA ApartmentState. If not you will receive errors messages that the DragDrop registration did not succeed while creating the WorkflowView.
The code is pretty simple. Basically it uses a standard System.ComponentModel.Design.DesignSurface and System.Workflow.ComponentModel.Design.WorkflowView type to work with the workflow. To load the workflow a custom WorkflowDesignerLoader subclass is required to do the actual loading. No big deal. If you are using a State Workflow you need to override the OnEndLoad function to load the .layout file and use the LoadDesignerLayout function to add it to the image generated.
Dim workflow AsNew SequentialWorkflowActivity
Dim loader AsNew WorkflowLoader(workflow)
Dim surface AsNew DesignSurface
Dim view AsNew WorkflowView(CType(surface, IServiceProvider))
Private _workflowDefinition As Activity
SubNew(ByVal workflowDefinition As Activity)
_workflowDefinition = workflowDefinition
ProtectedOverridesSub PerformLoad(ByVal serializationManager As IDesignerSerializationManager)
Dim designerHost As IDesignerHost = Me.GetService(GetType(IDesignerHost))
Dim allActivities As List(Of Activity) = WorkflowUtils.GetAllActivities(_workflowDefinition)
ForEach item As Activity In allActivities
PublicOverridesReadOnlyProperty FileName() AsString
PublicOverridesFunction GetFileReader(ByVal filePath AsString) As System.IO.TextReader
PublicOverridesFunction GetFileWriter(ByVal filePath AsString) As System.IO.TextWriter
If you are interested in the future of VB, or .NET in general because this is equally true for C#, you might want to read the blog post by Scott Wisniewski. In this blog post he describes how extension methods will work in VB 9 and what the benefit for the average developer will be. Of course extension methods are also a big part of LINQ so if you want to understand how LINQ works you need to understand extension methods too.
Its and interesting blog post you can find here
I was tagged by Alex Thissen
so I guess its my turn to tell 5 things you may not know about me.
- I never used computers much back in high school or in university. In fact I trained as an officer in the merchant navy and spend 5 years at sea. Most of this was spend working supply/anchor handing tugs for the offshore and oil industry. Had a great with lots of horror stories but its not the kind of job to do until you retire.
- I really love troubleshooting issues. I see it as a challenge to figure out what is going on deep down under the hood and getting stuff to work. And not just software. The only problem is that it sometimes takes a lot of time. This is also the reason for my company name ABL - The Problem Solver.
- ABL used to stand for Automatiserings Bureau Leiden. A real lame name I left behind when I moved out of Leiden. Just kept the initials for no good reason.
- I like doing extreme things. So I went skydiving, scuba diving, drive a motor bike (well used to, it hasn't moved much last year) and go paragliding. I crashed my paraglider last year breaking a vertebrae so I guess I will be taking it easy for some time to come :-(
- I love to travel and meat people. Amongst my favorite places to go are Cuba and Syria. Both leave a bit to be desired on the political front but the people are real hospitable. Some years ago I bought a single way ticket to the Middle East and just went backpacking for over a month and when the time was up got myself a ticket back home. Turns out I was in Jordan :-)
One thing I really dislike is instable development tools
. Now I do quite a bit of work with beta software and in that case it is to be expected. However when using production software it should just work and not throw random errors around. Not only does it mean I am not nearly as productive as I should be but if it happens to often I might just stop using a perfectly good piece of technology because the lack of good tooling.
One place I have been receiving a fair number of errors inside of Visual Studio 2005 is Windows Workflow Foundation. Now most errors seem to occur when working in Visual Basic
, switching to C# seems to produce better results.
However one error that crops up both in VB and C# is “Error HRESULT E_FAIL has been returned from a call to a COM component” while trying to open a Expression condition in a state workflow. This error seems to appear pretty randomly and most of the time just closing all windows and reopening the workflow is enough to solve it and continue work. There are times when that isn’t enough though and I need to restart VS.
Now the problem is I cannot reproduce this error on demand and to report it I really need to. So if you have any clue to the cause please let me know so we can report this as a bug and with any luck get it fixed.
Maurice de Beijer