Jun 30 2009

Microsoft addresses one of my WF issues in 4.0

Category: .NetRory Primrose @ 08:32

I just read on Guy Burstein’s blog that WF 4.0 introduces a WorkflowInvoker class. This class makes it much easier to execute synchronous workflows without having to write all the plumbing code required by the WorkflowInstance class. This is really great news. See this post for the details.

Tags:

Jun 24 2009

A new job and almost a new foot

Category: PersonalRory Primrose @ 10:47

I had been chasing a job opportunity for a while as I was getting close to the time where I would like a change. The deal was completed just before I came back from a wonderful overseas holiday a couple of weeks ago.

Before starting in the new position I was able to do a handover to my colleagues in the old job. The next day had me in hospital for some planned foot surgery and then in the new job the day after that. It has been a whirlwind couple of weeks but I am already enjoying the fresh winds of change as I settle into a new company and project.

I hope to get back into some more regular blogging now that I am getting back into the groove.

Tags:

Jun 15 2009

Generating Sandcastle documentation with TeamBuild

Category: .NetRory Primrose @ 11:00

Automatically generating technical documentation from code comments is really easy with Sandcastle and SHFB. If you are using TeamBuild to provide continuous integration then this is a great place to ensure up to date documentation is being produced. There a two ways that Sandcastle can be used to generate documentation. The first is dynamically without a SHFB project file and the second is with a SHFB project file.

Dynamically creating documentation is an easy solution that essentially documents all dll files found in the build directory with some known exclusions. This has the advantage that you don’t need to manage the documentation configuration as assemblies are added and removed from the solution. The disadvantages is that it potentially generates documentation for more assemblies than intended, namely the dependencies for the solution. The dynamic documentation generation is really good for framework/toolkit type solutions that don’t have external dependencies. The dynamic solution tells SHFB the information that it requires that would otherwise be defined via a project file. The MSBuild script looks something like the following.

<Target Name="BuildSandcastleWithDynamicProjectDefinition">

  <!-- Uses Sandcastle Help File Builder to build a CHM documentation for all assemblies in the output directory. -->
  <Message Text="Setting empty DocumentationName to ProductName '$(ProductName)'" />

  <CreateProperty Value="$(ProductName)"
                  Condition="$(DocumentationName) == ''">
    <Output TaskParameter="Value"
            PropertyName="DocumentationName"/>
  </CreateProperty>

  <!-- Find all assemblies in the output directory -->
  <CreateItem Include="$(OutDir)*.dll"
              Exclude="$(OutDir)*Tests.dll;$(OutDir)*_Accessor.dll">
    <Output ItemName="Assemblies"
            TaskParameter="Include" />
  </CreateItem>

  <!-- Document assemblies that have corresponding XML documentation files -->
  <CreateItem Include="@(Assemblies)"
              Condition="Exists('%(Assemblies.RelativeDir)%(Assemblies.Filename).xml')">
    <Output ItemName="AssembliesToDocument"
            TaskParameter="Include" />
  </CreateItem>

  <!-- Include assemblies that are missing XML documentation files as dependencies -->
  <CreateItem Include="@(Assemblies)"
              Condition="!Exists('%(Assemblies.RelativeDir)%(Assemblies.Filename).xml')">
    <Output ItemName="DependenciesToDocument"
            TaskParameter="Include" />
  </CreateItem>

  <!-- Include all files in the \Documentation subdirectory of a project as content -->
  <CreateItem Include="$(SolutionRoot)\**\Documentation\*"
              Exclude="$(SolutionRoot)\**\Thumbs.db">
    <Output ItemName="DocumentationContent"
            TaskParameter="Include" />
  </CreateItem>

  <!-- Check if the assemblies for corresponding XML files exist -->
  <Error Text="Assembly not found %(AssembliesToDocument.RelativeDir)%(AssembliesToDocument.Filename).dll, but XML was."
         Condition="!Exists('%(AssembliesToDocument.RelativeDir)%(AssembliesToDocument.Filename).dll')" />

  <PropertyGroup>
    <SandcastleBuilderPath>$(ProgramFiles)\EWSoftware\Sandcastle Help File Builder\SandcastleBuilderConsole.exe</SandcastleBuilderPath>
    <SandcastleBuilderArguments>-new @(AssembliesToDocument -> '-assembly=&quot;%(RelativeDir)%(Filename).dll&quot;',' ') @(DependenciesToDocument -> '-dependency=&quot;%(RelativeDir)%(Filename).dll&quot;',' ') @(DocumentationContent -> '-addcontent=&quot;%(Fullpath)*.*,html\Documentation&quot;',' ') -outputpath=&quot;$(OutDir).&quot; -HelpTitle=&quot;Documentation for $(ProductName)&quot; -Language=&quot;en-AU&quot; -HtmlHelpName=&quot;$(DocumentationName)&quot; -FooterText=&quot;Version: $(VersionNumber) &lt;br /&gt; Build Number: $(BuildNumber)&quot; -KeepLogFile=&quot;false&quot; -RootNamespaceContainer=&quot;true&quot; -SyntaxFilters=&quot;CSharp&quot;</SandcastleBuilderArguments>
  </PropertyGroup>

  <!-- Execute sandcastle -->
  <Message Text="Running Sandcastle: &quot;$(SandcastleBuilderPath)&quot; $(SandcastleBuilderArguments)" />
  <Exec Command="&quot;$(SandcastleBuilderPath)&quot; $(SandcastleBuilderArguments)" />

</Target>

Dynamically creating documentation is easy, but the resultant documentation can become dirty as external dependencies appear in the build directory. This is when using a SHFB project file becomes an advantage. This allows the solution to contain the specific definition of what gets included in the Sandcastle generated documentation. The best option for managing the SHFB project file is to add it as a solution item in Visual Studio. The MSBuild script for using the SHFB project file looks like the following.

<Target Name="BuildSandcastleProjectFile">
 
  <Message Text="Building Sandcastle documentation using the project '$(SandcastleProjectFilePath)'" />
 
  <Message Text="Setting empty DocumentationName to ProductName '$(ProductName)'" />
 
  <CreateProperty Value="$(ProductName)"
                  Condition="$(DocumentationName) == ''">
    <Output TaskParameter="Value"
            PropertyName="DocumentationName"/>
  </CreateProperty>
 
  <PropertyGroup>
    <SandcastleBuilderArguments>-outputpath=&quot;$(OutDir).&quot; -HelpTitle=&quot;Documentation for $(ProductName)&quot; -Language=&quot;en-AU&quot; -HtmlHelpName=&quot;$(DocumentationName)&quot; -FooterText=&quot;Build Number: $(BuildNumber)&quot; -KeepLogFile=&quot;false&quot; -RootNamespaceContainer=&quot;true&quot; -SyntaxFilters=&quot;CSharp&quot;</SandcastleBuilderArguments>
  </PropertyGroup>
 
  <!-- Execute sandcastle -->
  <Message Text="Running Sandcastle: &quot;$(SandcastleBuilderPath)&quot; &quot;$(SandcastleProjectFilePath)&quot; $(SandcastleBuilderArguments)" />
  <Exec Command="&quot;$(SandcastleBuilderPath)&quot; &quot;$(SandcastleProjectFilePath)&quot; $(SandcastleBuilderArguments)" />
 
</Target>

These targets run the implementation for generating the documentation. The following targets are used to orchestrate this work.

<PropertyGroup>
 
  <ProductName></ProductName>

  <!-- Enables/Disables generation of Sandcastle Documentation -->
  <BuildDocumentation>true</BuildDocumentation>
 
  <!-- 
  Defines the name of the Sandcastle project file to build 
  If not defined, a search is run for *.shfb files. The build fails if multiple project definitions are found.
  -->
  <SandcastleProjectFile></SandcastleProjectFile>
  <SandcastleProjectFilePath Condition="'$(SandcastleProjectFile)' != ''">$(SolutionRoot)\$(SandcastleProjectFile)</SandcastleProjectFilePath>
 
  <!-- Defines the documentation name -->
  <DocumentationName></DocumentationName>
 
  <SandcastleBuilderPath>$(ProgramFiles)\EWSoftware\Sandcastle Help File Builder\SandcastleBuilderConsole.exe</SandcastleBuilderPath>
 
  <!-- Used to store the updated version number for generating Sandcastle documentation -->
  <EmptyVersionNumber>0.0.0.0</EmptyVersionNumber>
  <VersionNumber>$(EmptyVersionNumber)</VersionNumber>
 
</PropertyGroup>
 
<Target Name="GenerateDocumentation"
        Condition="$(BuildDocumentation)">
 
  <GetBuildProperties TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
                      BuildUri="$(BuildUri)">
    <Output TaskParameter="TestSuccess"
            PropertyName="TestSuccess" />
  </GetBuildProperties>
 
  <Message Text="Skipping Sandcastle documentation generation"
           Condition="$(TestSuccess) == false" />
 
  <CallTarget Targets="FindSandcastleProjectFile"
              Condition="$(TestSuccess)" />
 
  <CallTarget Targets="GenerateSandcastleDocumentation"
              Condition="$(TestSuccess)" />
 
</Target>
 
<Target Name="FindSandcastleProjectFile"
        Condition="'$(SandcastleProjectFilePath)' == ''">
 
  <Message Text="No Sandcastle project defined. Searching %24(SolutionRoot)\**\*.shfb" />
 
  <ItemGroup>
 
    <SandcastleProjectDefinitionFiles Include="$(SolutionRoot)\**\*.shfb" />
 
  </ItemGroup>
 
  <Message Text="Searching failed to find a Sandcastle project file."
           Condition="'@(SandcastleProjectDefinitionFiles)' == ''" />
  <Message Text="Found the following Sandcastle project files: %0D%0A @(SandcastleProjectDefinitionFiles -> '%(FullPath)', '%0D%0A')"
           Condition="'@(SandcastleProjectDefinitionFiles)' != ''" />
 
  <StringComparison Comparison="Contains"
                    Param1="@(SandcastleProjectDefinitionFiles)"
                    Param2=";"
                    Condition="'@(SandcastleProjectDefinitionFiles)' != ''">
    <Output TaskParameter="Result"
            ItemName="MultipleSandcastleProjectsFound" />
  </StringComparison>
 
  <Error Text="Multiple Sandcastle project definitions found."
         Condition="'@(SandcastleProjectDefinitionFiles)' != '' And @(MultipleSandcastleProjectsFound) == true" />
 
  <PropertyGroup>
 
    <SandcastleProjectFilePath>@(SandcastleProjectDefinitionFiles)</SandcastleProjectFilePath>
 
  </PropertyGroup>
 
</Target>
 
<Target Name="GenerateSandcastleDocumentation">

  <BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
             BuildUri="$(BuildUri)"
             Name="Generating Sandcastle documentation"
             Message="Generating Sandcastle documentation">
    <Output TaskParameter="Id"
            PropertyName="SandcastleBuildStepId" />
  </BuildStep>
 
  <!-- Check if builder and files exist -->
  <Error Text="Sandcastle Help File Builder is not found at $(SandcastleBuilderPath)."
         Condition="!Exists('$(SandcastleBuilderPath)')"  />
 
  <Error Text="No ProductName property value has been defined."
         Condition="'$(ProductName)' == ''" />
 
  <Error Text="Sandcastle project file '$(SandcastleProjectFilePath)' does not exist"
         Condition="'$(SandcastleProjectFilePath)' != '' And !Exists('$(SandcastleProjectFilePath)')" />
 
  <BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
             BuildUri="$(BuildUri)"
             Id="$(SandcastleBuildStepId)"
             Message="Compiling Sandcastle documentation project"
             Condition="'$(SandcastleProjectFilePath)' != ''" />
 
  <CallTarget Targets="BuildSandcastleProjectFile"
              Condition="'$(SandcastleProjectFilePath)' != ''" />
 
  <BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
             BuildUri="$(BuildUri)"
             Id="$(SandcastleBuildStepId)"
             Message="Compiling Sandcastle documentation without a project"
             Condition="'$(SandcastleProjectFilePath)' == ''" />
 
  <CallTarget Targets="BuildSandcastleWithDynamicProjectDefinition"
              Condition="'$(SandcastleProjectFilePath)' == ''" />
 
  <BuildStep TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
             BuildUri="$(BuildUri)"
             Id="$(SandcastleBuildStepId)"
             Status="Succeeded" />
 
</Target>

Tags: , , ,

Jun 1 2009

GhostDoc has been acquired by SubMain

Category: Applications | .NetRory Primrose @ 21:26

Just like Reflector, SubMain has just acquired GhostDoc and has released an updated version. Looks like SubMain has already put in a decent amount of effort for GhostDoc. This is appears to be significantly different in contrast to what Red Gate has published for Reflector. Looks like it will be interesting times ahead for these tools.

Tags:

Jun 1 2009

How to stream data in WCF service operations

Category: .NetRory Primrose @ 20:09

Bruce Zhang has put together a great post about stream operations in WCF.

I have never liked the limitations imposed with streaming in WCF although I do agree with the design. The biggest issue for me is that WCF needs to be configured for a maximum transfer size. The main problem here is that a service and a client will probably not know the maximum size of data that a service could process. To know the maximum size would require a business rule in the service design and that would not be very common for a service that handles large amounts of data via a stream.I prefer a chunking service design as you get around all of these limitations with the one disadvantage of creating a chatty service. This introduces a little overhead but I think it is worth it for the benefit of avoiding WCF streaming restrictions.

Tags: ,