https://wbates.net/Warren Bates20182018-01-27T20:40:08ZWarren Bateshttps://wbates.net/posts/baking-a-cake-addin-part-3-recipeSetting up the addin build process2017-08-26T00:00:00Z<p>Now that we have our Cake addin written, it's time to put the finishing touches on and utilise the Cake teams preferred way of building addins - Cake.Recipe. I've written about <a href="./getting-started-with-cake-recipe.md">getting started with Cake.Recipe</a>, and there might be some crossover with that post, but most the content should be new.</p>
<p>We're going to make use of Cake.Recipe's tasks for documentation, versioning, as well as the normal aspects of building and running unit tests.</p>
<h1 id="ready-the-repository">Ready the repository</h1>
<h2 id="move-the-files">Move the files</h2>
<p>Cake.Recipe assumes a certain folder structure, and whilst it can be changed using the setup.cake file to alter various parameters, it's far easier if we stick with the convention.
In the root of the working copy, create a new folder called src. I suggest using git to move the files into this directory so you can maintain the history of each. We need to move both the project folders (Cake.Markdown-pdf and Cake.Markdown-pdf.Tests) as well as the solution file.</p>
<pre><code class="language-bash"># Hint - Case sensitive for directories/files even in Windows
mkdir src
git mv Cake.Markdown-Pdf src
git mv Cake.Markdown-Pdf.Tests src
git mv Cake.Markdown-Pdf.sln src
</code></pre>
<p>I suggest you also move the packages folder from the root into the src folder, otherwise the first time you build it will recreate the packages folder and you'll have a duplicate that could cause confusion. It doesn't need tracking by git though.</p>
<h2 id="create-a-nuspec-file">Create a nuspec file</h2>
<p>Add another new folder called nuspec with a subdirectory of nuget</p>
<pre><code class="language-bash">mkdir -p nuspec\nuget
</code></pre>
<p>Create a new text file in here called <code>Cake.Markdown_Pdf.nuspec</code>. The filename must be the same as the root namespace for the project or cake will not be able to locate the files for packaging.
<img src="../assets/images/default-namespace.png" class="img-fluid" alt="Default namespace" />
Open it up and paste the following in, replacing as appropriate.</p>
<pre><code class="language-xml"><?xml version="1.0"?>
<package >
<metadata>
<id>Cake.Markdown-Pdf</id>
<title>Cake.Markdown-Pdf</title>
<authors>Warren Bates</authors>
<owners>Warren Bates</owners>
<licenseUrl>https://github.com/wozzo/Cake.Markdown-Pdf/blob/master/LICENSE</licenseUrl>
<projectUrl>https://github.com/wozzo/Cake.Markdown-Pdf</projectUrl>
<iconUrl>https://cdn.rawgit.com/cake-contrib/graphics/a5cf0f881c390650144b2243ae551d5b9f836196/png/cake-contrib-medium.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<summary>Adds Markdown-Pdf alias for Cake</summary>
<description>An alias for Cake to help with running Markdown-Pdf commands as part of a build</description>
<tags>Cake Script Build Markdown Pdf Markdown-Pdf</tags>
</metadata>
<files>
<file src="Cake.Markdown-Pdf.xml" target="lib\net45" />
<file src="Cake.Markdown-Pdf.dll" target="lib\net45" />
</files>
</package>
</code></pre>
<h2 id="gitversion">GitVersion</h2>
<p>Add a new <code>SolutionInfo.cs</code> file in the src folder and give it the following content</p>
<pre><code class="language-xml">using System.Reflection;
[assembly: AssemblyVersion("0.1.0")]
[assembly: AssemblyFileVersion("0.1.0")]
[assembly: AssemblyInformationalVersion("0.1.0")]
</code></pre>
<p>This is going to be a shared file that will get updated with version information as part of the cake build. The following steps need to be repeated for each project in the solution.</p>
<ol>
<li>Open up the <code>properties\AssesmblyInfo.cs</code> file and remove the three properties we've just added to the <code>SolutionInfo.cs</code> file.</li>
<li>Open up the csproj file, find the reference to the <code>AssemblyInfo.cs</code> file, and add the following after</li>
</ol>
<pre><code class="language-xml"><Compile Include="..\SolutionInfo.cs">
<Link>Properties\SolutionInfo.cs</Link>
</Compile>
</code></pre>
<h2 id="xml-documentation">XML Documentation</h2>
<p>Open up the solution in Visual Studio and open the Cake.Markdown-Pdf project properties. On the Build page select the 'XML Documentation file' checkbox. Do this for each build configuration.</p>
<p><img src="../assets/images/xml-documentation.png" class="img-fluid" alt="XML Documentation" /></p>
<p>This will mean you'll need to add in xml comments or the build will fail. Building in Visual Studio should show warnings were they are missing</p>
<h1 id="get-cake.recipe">Get Cake.Recipe</h1>
<p>Head to the <a href="https://github.com/cake-contrib/Cake.Recipe">Cake.Recipe repository</a> on GitHub and download the following list of files into the root of our repository.</p>
<ul>
<li>.appveyor.yml</li>
<li>.gitignore</li>
<li>GitReleaseManager.yaml</li>
<li>build.ps1</li>
<li>config.wyam</li>
<li>setup.cake</li>
</ul>
<p>Open up <code>setup.cake</code> in a text editor and change the title, repositoryOwner, repositoryName, and appVeyorAccountName settings as appropriate. I've also added the nuspecFilePath from earlier.</p>
<pre><code class="language-csharp">BuildParameters.SetParameters(context: Context,
buildSystem: BuildSystem,
sourceDirectoryPath: "./src",
title: "Cake.Markdown-Pdf",
repositoryOwner: "wozzo",
repositoryName: "Cake.Markdown-Pdf",
appVeyorAccountName: "wozzo",
nuspecFilePath: "nuspec/nuget/Cake.Markdown-Pdf.nuspec");
</code></pre>
<p>Ensure the last line calls the <code>Run()</code> method.</p>
<p>You should now be able to run the build from the command line</p>
<pre><code class="language-powershell">.\build.ps1
</code></pre>
<h1 id="documentation">Documentation</h1>
<p>Cake.Recipe uses <a href="https://wyam.io">Wyam</a> to auto generate a static site and commit it to the gh-pages branch of the project on GitHub. For those that don't know this is a special branch that GitHub uses to serve up site content at the url <a href="https://%3Cusername%3E.github.io/%3Creponame%3E.">https://<username>.github.io/<reponame>.</a> For example this project's documentation will be at <a href="https://wozzo.github.io/Cake.Markdown-Pdf.">https://wozzo.github.io/Cake.Markdown-Pdf.</a></p>
<p>Add a new folder called <code>docs</code> in the root of the repo. Get wyam installed and run the following from that directory.</p>
<pre><code>wyam new -r docs
</code></pre>
<p>You can then delete the wyam.config file it creates in that directory.</p>
<p>You can now start editing the markdown pages to change the content of the site. I won't go into detail of using wyam as there are plenty of other blogs on that that go outside the scope of this tutorial.</p>
<p>You can preview the site by running this command and then visisting <a href="http://localhost:5080/Cake.Markdown-Pdf">http://localhost:5080/Cake.Markdown-Pdf</a> (case sensitive)</p>
<pre><code class="language-powershell">.\build.ps1 -target preview
</code></pre>
<h1 id="appveyor">AppVeyor</h1>
<p>To use AppVeyor and cake together you'll need to setup several environment variables for your project. This lies outside of the scope of this tutorial but you can figure most of it out by reading through the appveyor.cake/environment.cake files in Cake.Recipe.</p>
<p>The only additional change I needed to do to get the build working was to add an <code>Image:</code> property specifying which <a href="https://www.appveyor.com/docs/build-environment/#build-worker-images">build worker image</a> AppVeyor should use. Because I used Visual Studio 2017 I needed to add the following to my <code>.appveyor.yml</code> file.</p>
<pre><code class="language-yaml">image: Visual Studio 2017
</code></pre>
<h1 id="finishing-up">Finishing up</h1>
<p>Commit the files and push to GitHub. The build should take care of the rest. You should definitely consider getting your project added to the Cake-Contrib team on GitHub. See <a href="https://github.com/cake-contrib/Home">the cake-contrib</a> page for more details.</p>
<p>Now that we have our Cake addin written, it's time to put the finishing touches on and utilise the Cake teams preferred way of building addins - Cake.Recipe. I've written about <a href="./getting-started-with-cake-recipe.md">getting started with Cake.Recipe</a>, and there might be some crossover with that post, but most the content should be new.</p>https://wbates.net/posts/getting-started-with-cake-recipeGetting Started with Cake.Recipe2017-08-25T00:00:00Z<p>I've been playing around with the Cake build tool for a little while now, but having recently had need of a plugin that didn't yet exist I embarked on a journey that lead me to Cake.Recipe.</p>
<blockquote class="blockquote">
<p>Cake.Recipe is a set of convention based Cake scripts.</p>
</blockquote>
<p>If you check out the Content folder in that repository you'll find a host of pre-written cake scripts that cover a wide range of use cases. They've been designed so that they all work together and can be called through a single cake file that contains a few bits of setup. They're used by most of the addin's under the cake-contrib team. The only thing that was lacking at the start of this journey was documentation about how to use Cake.Recipe, which is why I'm writing this post.</p>
<h1 id="getting-started">Getting started</h1>
<p>Before I knew what Cake.Recipe was I was looking around various repositories looking for good ideas for how to improve the addin I was working on at the time (Cake.Bower), and I started to notice that several of the repositories were missing a build.cake file. The .cake file can of course be called whatever you like, but by default it is called build.cake, so for this to be missing, or rather called setup.cake, I found a bit odd.
Looking into the setup.cake file I found something very different from what I'm used to seeing in build.cake files.</p>
<pre><code class="language-csharp">#load nuget:https://www.myget.org/F/cake-contrib/api/v2?package=Cake.Recipe&prerelease
Environment.SetVariableNames();
BuildParameters.SetParameters(context: Context,
buildSystem: BuildSystem,
sourceDirectoryPath: "./",
title: "Cake.Bower",
repositoryOwner: "cake-contrib",
repositoryName: "Cake.Bower",
appVeyorAccountName: "cakecontrib");
BuildParameters.PrintParameters(Context);
ToolSettings.SetToolSettings(context: Context,
dupFinderExcludePattern: new string[] {
BuildParameters.RootDirectoryPath + "/src/Cake.Bower.Tests/*.cs" },
testCoverageFilter: "+[*]* -[xunit.*]* -[Cake.Core]* -[Cake.Testing]* -[*.Tests]* ",
testCoverageExcludeByAttribute: "*.ExcludeFromCodeCoverage*",
testCoverageExcludeByFile: "*/*Designer.cs;*/*.g.cs;*/*.g.i.cs");
Build.Run();
</code></pre>
<p>No tasks. None at all.</p>
<p>The top line is the critical one. It fetches the Cake.Recipe scripts and loads each one providing the methods you can see being used here. The bootstrapped build.ps1 that you'd normally download if you followed the instructions on the "Setting up a new project" tutorial page has been modified to use a default file of setup.cake purely as a convention to distinguish between normal cake builds and recipe builds. You can then modify the parameters in the setup.cake file to achieve the desired result. There are close to 80 different parameters that can be set here that can be used to configure the ~40 odd tasks that cake.recipe provides.</p>
<h1 id="parameters">Parameters</h1>
<p>Take a look at the parameters.cake file to see what options are available to you out of the box. These can all be set either in the SetParameters or using environment variables.
Tasks
The tasks.cake file paradoxically doesn't contain any tasks, but does give you a handy list of tasks that are configured. It also provides you with the first way of overriding cake.recipe functionality by changing the value assigned to these properties, should one of the tasks need tweaking to suit your needs. More on that in a bit.
The tasks are set in a cake file specific to the purpose of the task, i.e. the tasks related to testing are all in testing.cake, the tasks for code analysis are in analyzing.cake, etc. The best starting point for figuring out what is going is in the build.cake file, currently on line 354</p>
<pre><code class="language-csharp">BuildParameters.Tasks.DefaultTask = Task("Default")
.IsDependentOn("Package");
</code></pre>
<p>We can start to see the intended targets for a full build. Don't get thrown by the lack of dependencies here. For example the Package task target doesn't only depend on the Export-Release-Notes task. Look above the task definition and find the SetupTasks method. In there you can see more dependencies being set.</p>
<p>This should start to give you an idea what will happen when you run one of the targets listed here. The default is probably the best place to start. If you need a safe project to test it out on, fork Cake.Bower and run .\build.ps1. It will take a while on the first run as it has to download all of the tools and addins required to run.</p>
<h1 id="cake.bower-build-running">Cake.Bower build running</h1>
<p>The tasks that are run by the default target are</p>
<pre><code>Task Duration
---------------------------------------------------------
Export-Release-Notes Skipped
Show-Info 00:00:00.0093039
Print-AppVeyor-Environment-Variables Skipped
Clean 00:00:00.0292549
Restore 00:00:01.0742619
Build 00:00:05.7580859
DupFinder 00:00:09.2430267
InspectCode 00:00:22.4277417
Analyze 00:00:00.0042806
Install-ReportGenerator 00:00:02.2837979
Install-ReportUnit 00:00:02.2187105
Install-OpenCover 00:00:02.2890312
Test-NUnit Skipped
Test-xUnit 00:00:06.9958299
Test-MSTest Skipped
Test-VSTest Skipped
Test-Fixie Skipped
Test 00:00:00.0065763
Create-NuGet-Packages 00:00:00.7237132
Create-Chocolatey-Packages Skipped
Package 00:00:00.0050486
Default 00:00:00.0055089
---------------------------------------------------------
Total: 00:00:53.0741721
</code></pre>
<p>You can see some of the tasks were skipped. This means the task appeared in the list of dependencies but the criteria to run it (defined using the <code>WithCriteria</code> method) were not met. For example the Test-NUnit task will only run if NUnit tests are found. There are none in Cake.Bower so it gets skipped. If I wanted to always skip a step I can find the parameter that governs whether it runs and set it in the setup.cake file. For example if I wanted to disable the InspectCode step I would change my setup.cake's call to BuildParameters.SetParameters to the following.</p>
<pre><code class="language-csharp">BuildParameters.SetParameters(context: Context,
buildSystem: BuildSystem,
sourceDirectoryPath: "./",
title: "Cake.Bower",
repositoryOwner: "cake-contrib",
repositoryName: "Cake.Bower",
appVeyorAccountName: "cakecontrib",
shouldRunInspectCode: false);
</code></pre>
<p>Run it again and this time the task will be skipped but the rest should be the same.</p>
<h1 id="replacing-a-task">Replacing a task</h1>
<p>With Cake, once a Task has been created you can't create another with the same name. Calling the .Does(...) method adds an additional action but doesn't replace the existing one. If you want to replace the tasks actions with something completely different you first need to clear the existing actions. To clear the InspectCode actions use the following</p>
<pre><code class="language-csharp">BuildParameters.Tasks.InspectCodeTask.Task.Actions.Clear();
</code></pre>
<p>I'm now free to call the .Does(...) method with the action I want it to undertake and be sure it is the only thing it will do, for example:</p>
<pre><code class="language-csharp">BuildParameters.Tasks.InspectCodeTask.Does( () => Information("And now for something completely different..."));
</code></pre>
<p>Running .\build.ps1 now yields the following output when you get to the InspectCode task.</p>
<pre><code>========================================
InspectCode
========================================
Executing task: InspectCode
And now for something completely different...
Finished executing task: InspectCode
</code></pre>
<p>You are also free to create your own tasks and add them as dependencies to the existing ones. Something like this...</p>
<pre><code class="language-csharp">Task("MakeTea").Does( () => Information("Make Tea not Love"));
BuildParameters.Tasks.InspectCodeTask.Task.Actions.Clear();
BuildParameters.Tasks.InspectCodeTask
.IsDependentOn("MakeTea")
.Does( () => Information("And now for something completely different..."));
</code></pre>
<p>which yields...</p>
<pre><code>========================================
MakeTea
========================================
Executing task: MakeTea
Make Tea not Love
Finished executing task: MakeTea
========================================
InspectCode
========================================
Executing task: InspectCode
And now for something completely different...
Finished executing task: InspectCode
</code></pre>
<p>And of course if you find that you need loads of additional scripts for a particular use case consider a pull request into Cake.Recipe</p>
<p>I've been playing around with the Cake build tool for a little while now, but having recently had need of a plugin that didn't yet exist I embarked on a journey that lead me to Cake.Recipe.</p>https://wbates.net/posts/setting-up-a-wyam-blog-with-cake-recipe-and-github-pagesSetting up a Wyam blog with Cake.Recipe and GitHub pages2017-08-24T00:00:00Z<p>I first saw <a href="https://wyam.io">Wyam</a> in action when I was working on the Cake.Recipe blog series, and was so impressed I decided to move the entire blog to it using GitHub pages to serve it up.
This post is going to document that process for others to follow.
One important distinction to get out of the way is the difference between a personal github page and a project page.</p>
<p>Personal page url's take the form <code>https://<username>.github.io/</code> while project pages take the form <code>https://<username>.github.io/<project_name></code>. Project pages can be served from any branch in the repository but personal pages can only be served from the master branch. Because of this there will be some slightly different instructions/hacks depending on whether you intend to use this process to create a personal or project page.</p>
<h1 id="setting-up-the-repositories">Setting up the repositories</h1>
<h2 id="personal-pages">Personal pages</h2>
<p>Create a repository which matches the pattern <code><username>.github.io</code>, e.g. mine is <a href="https://github.com/wozzo/wozzo.github.io">wozzo.github.io</a>. For personal pages the <code>master</code> branch must be the one that contains the output from the Wyam build. Make a single commit to the <code>master</code> branch of this empty repository.</p>
<p>Use the following commands to create a new orphaned branch called <code>develop</code>. It must be called <code>develop</code> for cake.recipe to publish the documentation using AppVeyor (remember I mentioned some hacks?).</p>
<pre><code>git checkout --orphan develop
</code></pre>
<p>Do an initial commit on this branch too. The majority of our work will be in this <code>develop</code> branch, while we leave the <code>master</code> branch to AppVeyor. Use the branch name <code>develop</code> elsewhere in this tutorial where I use the term "working branch"</p>
<h2 id="project-pages">Project pages</h2>
<p>If a branch exists called <code>gh-pages</code> then GitHub will use this branch to serve your sites content.
Create a new orphaned branch with the following command</p>
<pre><code>git checkout --orphan gh-pages
</code></pre>
<p>Do an initial commit on this branch and then switch back to the master branch. Use the branch name <code>master</code> elsewhere in this tutorial where I use the term "working branch".</p>
<h1 id="starting-with-wyam">Starting with Wyam</h1>
<p>Not strictly a required step, but it might make life easier. Install wyam on your machine - to see how to get Wyam go to their <a href="https://wyam.io/docs/usage/obtaining">obtaining</a> page.
Note: I had to add %appdata%/local/wyam to my path to allow powershell to be able to run wyam commands.</p>
<h1 id="creating-your-blog">Creating your blog</h1>
<p>Wyam uses recipes to setup the outline of your site quickly. Create a folder called "docs" in the root of your working branch. In your command line interface of choice browse to the docs folder and run the following command</p>
<pre><code>wyam new --recipe blog
</code></pre>
<p>This will create a new folder called input and setup a couple of pages under there. Follow the next few steps to see what this blog will look like, then we'll start adding some content. The recipe will also add a config.wyam file, but we won't be needing this. It is safe to delete.</p>
<h1 id="getting-cake.recipe">Getting Cake.Recipe</h1>
<p>Go to <a href="https://github.com/cake-contrib/Cake.Recipe">Cake.Recipe</a> on GitHub and download the following files to the root of your working branch.</p>
<ul>
<li>.appveyor.yml</li>
<li>.gitignore</li>
<li>build.ps1</li>
<li>config.wyam</li>
<li>GitReleaseManager.yaml</li>
<li>setup.cake</li>
</ul>
<h2 id="setup-cake.recipe">Setup Cake.Recipe</h2>
<p>Open up the <code>setup.cake</code> file in your favourite text editor and edit the parameters so that it reflects the name of your blog.</p>
<pre><code class="language-csharp">BuildParameters.SetParameters(context: Context,
buildSystem: BuildSystem,
title: "Wozzo.Blog",
repositoryOwner: "wozzo",
repositoryName: "wozzo.github.io",
appVeyorAccountName: "wozzo",
wyamRecipe: "Blog",
wyamTheme: "CleanBlog");
</code></pre>
<p>If you're working on a personal page then you may also want to provide an additional parameter to change the virtual directory the website is compiled for. By default Cake will use the <code>repositoryName</code> value.
For a personal site you add the following parameter.</p>
<pre><code>webLinkRoot: "/"
</code></pre>
<p>I've also added two additional parameters telling Wyam that it should use the blog recipe and Phantom theme.</p>
<h2 id="setup-appveyors-config">Setup AppVeyor's config</h2>
<p>We won't setup AppVeyor just yet, but we can start to get things in place with the <code>.appveyor.yml</code> config file. Open it up and change the target of the build to <code>Force-Publish-Documentation</code>.</p>
<pre><code>#---------------------------------#
# Build Script #
#---------------------------------#
build_script:
- ps: .\build.ps1 -Target Force-Publish-Documentation
</code></pre>
<p>If you're working on a personal page you should also remove <code>master</code> from the branch whitelist</p>
<pre><code>#---------------------------------#
# Branches to build #
#---------------------------------#
branches:
# Whitelist
only:
- develop
- /release/.*/
- /hotfix/.*/
</code></pre>
<p>At this point you should make another commit. If you don't have any commits GitVersion will throw an exception when attempting to run the cake build.</p>
<h1 id="previewing-the-site-locally">Previewing the site locally</h1>
<p>Time to see what it looks like. The cake.recipe Wyam script comes with a Preview task which will fire up a local web server, and run wyam with a watch on your files. This means you can view the site in your browser, edit your posts and the browser will refressh so you can see your changes immediately.</p>
<p>Run the following command in the root of your repository.</p>
<pre><code>.\build.ps1 -target preview
</code></pre>
<p>The first run may take a while as it is going to get all the tools it requires, future runs should be much faster.
Look at the end of the build log and you should find a message like the following</p>
<pre><code>Preview server listening on port 5080 and serving from path file:///.../wozzo
.blog/BuildArtifacts/Documentation with virtual directory Wozzo.Blog and LiveReload support
Watching paths(s) file:///.../content, theme, input
Watching configuration file file:///.../wozzo.blog/config.wyam
Hit any key to exit
</code></pre>
<p>The key parts are the port and virtual directory. For personal sites there will be no virtual directory. Replace them in the following url in your browser.</p>
<pre><code>http://localhost:<port>/<virtual_directory>/
# Example
http://localhost:5080/Wozzo.Blog
</code></pre>
<p>The virtual directory is case sensitive by the way ;-)</p>
<p><img src="../assets/images/wyam-in-browser.png" class="img-fluid" alt="Wyam blog in the browser inception" /></p>
<p>Isn't it pretty. For some reason when I tried to use the SolidState theme I got exceptions due to missing files. If you get it working, please get in touch.</p>
<h1 id="appveyor">AppVeyor</h1>
<p>Wyam's site has some really great <a href="https://wyam.io/docs/deployment/appveyor">instructions for setting up Wyam with AppVeyor</a>, and it's worth looking through but we're going to do things differently to get the benefits of working with Cake.Recipe.</p>
<p>Cake.Recipe has everything setup with either parameters, or Environment variables. The credentials and settings for most of what we'll need is done through environment variables.
Log in to AppVeyor and add a new project, select your repository. If doing a personal site then change the default branch to <code>develop</code> on the project settings page.
Go to the project settings page and find the Environment section. We'll need to add three variables here for Cake.Recipe to be able to deploy our site.</p>
<pre><code># Url of the git repository
WYAM_DEPLOY_REMOTE = https://github.com/wozzo/wozzo.github.io
# Branch to deploy the site too
WYAM_DEPLOY_BRANCH = master / gh-pages
# GitHub personal access token
WYAM_ACCESS_TOKEN = **************************************
</code></pre>
<p>The access token must be kept secret. You can create one by following these <a href="https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/">instructions</a>. This token will need the repo -> public_repo scope.</p>
<p><img src="../assets/images/appveyor-environment-variables.png" class="img-fluid" alt="Setting AppVeyor environment variables" /></p>
<h1 id="get-blogging">Get Blogging</h1>
<p>That's all the setup complete. Now commit any uncommitted changes and push your working branch to the remote. AppVeyor should pick it up and run it deploying your site.</p>
<p>If there are any problems check the AppVeyor build log. In particular the <code>Printing Build Parameters...</code> section can provide useful insight.</p>
<p>I first saw <a href="https://wyam.io">Wyam</a> in action when I was working on the Cake.Recipe blog series, and was so impressed I decided to move the entire blog to it using GitHub pages to serve it up.
This post is going to document that process for others to follow.
One important distinction to get out of the way is the difference between a personal github page and a project page.</p>https://wbates.net/posts/baking-a-cake-adding-part-2-testingTesting a Cake Addin2017-08-21T00:00:00Z<p>Tasting the cake would be the obvious pun here, but hopefully by now you've gotten stuck in and had more than a slice. Today I'm going to walkthrough setting up some simple unit tests on the Markdown-Pdf addin I wrote in <a href="http://wbates.net/baking-a-cake-addin/">part 1</a>.
In your solution add a new .Net framework class library project. Add the Cake.Testing nuget package to this solution along with your testing framework of choice. I'll be using <a href="https://xunit.github.io/">xUnit</a> along with the <a href="https://github.com/shouldly/shouldly">Shouldly</a> assertion package.</p>
<h2 id="fixture">Fixture</h2>
<p>Create a new class for your test fixture called MarkdownPdfRunnerFixture. This class should inherit from Cake.Testing's ToolFixture<TSetting> where TSetting is the settings class from the addin that we want to test.
Add a constructor that calls the base constructor passing in a string with the tool's name.</p>
<pre><code class="language-csharp">public MarkdownPdfFixture() : base("markdown-pdf") { }
</code></pre>
<p>then add a property which will hold an action for configuring the settings</p>
<pre><code class="language-csharp">public Action<MarkdownPdfRunnerSettings> RunnerSettings;
</code></pre>
<p>Finally add override the RunTool method and use it to instantiate a runner and call its Run method</p>
<pre><code class="language-csharp">protected override void RunTool()
{
var tool = new MarkdownPdfRunner(FileSystem, Environment, ProcessRunner, Tools);
tool.Run(RunnerSettings);
}
</code></pre>
<p>That's the fixture complete. We're going to try various settings out and then call the Run() method on the fixture, which will in turn call the RunTool method we just created and will return a <a href="http://cakebuild.net/api/Cake.Testing.Fixtures/ToolFixtureResult/">ToolFixtureResult</a> to us. We'll be able to query this result and check that the correct arguments were sent to the tool.</p>
<h2 id="tests">Tests</h2>
<p>I'm not going to go through each test I'll write for this class. You can check the code <a href="https://github.com/wozzo/Cake_Addin_Blog_Posts/tree/master/Part%202">here</a> if you want to see them all. I'll just showcase a couple of examples.
In the first test the configurator is null, so default settings should be used. In this case that means that no arguments are to get passed to the markdown-pdf tool so the Args should be empty.</p>
<pre><code class="language-csharp">[Fact]
public void No_Settings_Should_Use_Correct_Argument_Provided_In_MarkdownPdfRunnerSettings()
{
fixture.RunnerSettings = null;
var result = fixture.Run();
result.Args.ShouldBe("");
}
</code></pre>
<p>Here we use the WithFilePath method and pass in a test string. Because the desired action here is to pass that string as the only argument that's what we check for</p>
<pre><code class="language-csharp">[Fact]
public void WithFilePath_Settings_Should_Use_Correct_Argument_Provided_In_MarkdownPdfRunnerSettings()
{
fixture.RunnerSettings = s => s.WithFilePath(TestFilePath);
var result = fixture.Run();
result.Args.ShouldBe(TestFilePath);
}
</code></pre>
<p>The WithHelp() method should use the --help switch</p>
<pre><code class="language-csharp">[Fact]
public void WithHelp_Settings_Should_Use_Correct_Argument_Provided_In_MarkdownPdfRunnerSettings()
{
fixture.RunnerSettings = s => s.WithHelp();
var result = fixture.Run();
result.Args.ShouldBe("--help");
}
</code></pre>
<p>Finally a combination test that checks that all the correct settings are applied and in the correct order.</p>
<pre><code class="language-csharp">[Fact]
public void WithFilePath_And_CssFilePath_Settings_Should_Use_Correct_Argument_Provided_In_MarkdownPdfRunnerSettings()
{
fixture.RunnerSettings = s => s.WithFilePath(TestFilePath).WithCssPath(TestCssFilePath);
var result = fixture.Run();
result.Args.ShouldBe($"--css-path {TestCssFilePath} {TestFilePath}");
}
</code></pre>
<h2 id="run-the-tests">Run the tests</h2>
<p>Hopefully you've added a cake build script to your project by now, in which case you can add the following to it to get cake to run your unit tests as part of a task</p>
<pre><code class="language-csharp">#tool "xunit.runner.console"
...
var testResultsPath = MakeAbsolute(Directory(artifacts + "./test-results"));
var testAssemblies = new List<FilePath> { MakeAbsolute(File("./src/Cake.Markdown-Pdf.Tests/bin/" + configuration + "/Cake.Markdown-Pdf.Tests.dll")) };
Task("Run-Unit-Tests")
.IsDependentOn("Build")
.Does(() =>
{
CreateDirectory(testResultsPath);
var settings = new XUnit2Settings {
XmlReportV1 = true,
NoAppDomain = true,
OutputDirectory = testResultsPath,
};
settings.ExcludeTrait("Category", "Integration");
XUnit2(testAssemblies, settings);
});
</code></pre>
<p>Happy testing :)</p>
<p>Tasting the cake would be the obvious pun here, but hopefully by now you've gotten stuck in and had more than a slice. Today I'm going to walkthrough setting up some simple unit tests on the Markdown-Pdf addin I wrote in <a href="http://wbates.net/baking-a-cake-addin/">part 1</a>.
In your solution add a new .Net framework class library project. Add the Cake.Testing nuget package to this solution along with your testing framework of choice. I'll be using <a href="https://xunit.github.io/">xUnit</a> along with the <a href="https://github.com/shouldly/shouldly">Shouldly</a> assertion package.</p>https://wbates.net/posts/baking-a-cake-addin-part-1Baking a Cake Addin2017-08-20T00:00:00Z<p>No blog post on Cake is complete without a few puns, and bonus, now while you're googling technical stuff you always get a few recipes for delicious cakes thrown in. So what is cake and why do we need add ins?</p>
<blockquote class="blockquote">
<p><a href="https://www.cakebuild.net/">Cake (C# Make)</a> is a cross platform build automation system with a C# DSL to do things like compiling code, copy files/folders, running unit tests, compress files and build NuGet packages.</p>
</blockquote>
<p>I've recently been switching my organisation from a psake build process to Cake. The psake process had been in place for a long time, but since psake is no longer actively maintained and doesn't support the latest version of Visual Studio we knew we had to move to something different to allow us to use the latest .Net framework and C# language features. Enter Cake. Cake scripts are written in C#, and support using methods from referenced add ins that have been compiled into a dll. As C# developers being able to write our build process in C# is a no brainer right? You still use powershell to compile and run the cake script, but that's all taken care of by the bootstrapped build.ps1 that cake provides. Anyway we're here to talk about cake addins; if you need more information on getting started with cake please visit <a href="https://www.cakebuild.net/docs/tutorials/getting-started">cakebuild.net</a>.</p>
<p>At the time of writing there are 166 cake addins available performing a variety of jobs. We're going to write an addin which runs a command line tool available from npm. Based on <a href="https://github.com/cake-contrib/Cake.Npm/issues/16">this issue</a> I decided to make a markdown-pdf addin for this walkthrough.</p>
<p>Start with a new .NET framework class library project in Visual Studio and add the Cake.Core Nuget project.
We're going to start with two classes and an interface which will form the basics of our addin.</p>
<pre><code class="language-csharp">namespace Cake.Markdown_Pdf
{
public interface IMarkdownPdfRunner
{
}
public class MarkdownPdfRunnerSettings : ToolSettings
{
}
public class MarkdownPdfRunner : Tool<MarkdownPdfRunnerSettings>, IMarkdownPdfRunner
{
}
}
</code></pre>
<p>The interface will define the commands that can be run from the cake script.
The settings describes the command to be run and will generate the arguments that get passed into it.
The runner will implement the interface as well as helping cake to locate the command we want to run.</p>
<h2 id="markdownpdfrunner-basics">MarkdownPdfRunner basics</h2>
<p>You'll need a constructor that calls the Tool<TSettings> base constructor.</p>
<pre><code class="language-csharp">public MarkdownPdfRunner(IFileSystem fileSystem, ICakeEnvironment environment, IProcessRunner processRunner, IToolLocator tools)
: base(fileSystem, environment, processRunner, tools) { }
</code></pre>
<p>Then override the GetToolName() method and get it to return a string giving the name of your tool</p>
<pre><code class="language-csharp">protected override string GetToolName() => "Markdown-pdf Runner";
</code></pre>
<p>Override GetToolExecutableNames(), here is where we provide the names of the files the ToolLocator needs to look for. To find out go to %appdata%\roaming\npm on your machine.</p>
<p><img src="../assets/images/finding-npm-commands.png" class="img-fluid" alt="Finding Npm commands" /></p>
<p>This will usually be the normal command you'd run suffixed with .cmd followed without the suffix</p>
<pre><code class="language-csharp">protected override IEnumerable<string> GetToolExecutableNames() => new[] { "markdown-pdf.cmd", "markdown-pdf" };
</code></pre>
<p>I'm a big fan of expression bodied methods for these trivial one liners in case you hadn't realised ;)
One final thing to add at this stage is a method that will process the settings and convert them to arguments, or more accurately a <a href="http://cakebuild.net/api/Cake.Core.IO/ProcessArgumentBuilder/">ProcessArgumentBuilder</a>
All it will really do is call a method on the settings object that knows how to do that for the specific settings type that we've passed in.</p>
<pre><code class="language-csharp">protected static ProcessArgumentBuilder GetSettingsArguments(MarkdownPdfRunnerSettings settings)
{
var args = new ProcessArgumentBuilder();
settings?.Evaluate(args);
return args;
}
</code></pre>
<p>Put it all together and so far our runner class should look like this. We'll start adding some commands to it shortly.</p>
<pre><code class="language-csharp"> public class MarkdownPdfRunner : Tool<MarkdownPdfRunnerSettings>, IMarkdownPdfRunner
{
public MarkdownPdfRunner(IFileSystem fileSystem, ICakeEnvironment environment, IProcessRunner processRunner, IToolLocator tools)
: base(fileSystem, environment, processRunner, tools) { }
protected override string GetToolName() => "Markdown-pdf Runner";
protected override IEnumerable<string> GetToolExecutableNames() => new[] { "markdown-pdf.cmd", "markdown-pdf" };
protected static ProcessArgumentBuilder GetSettingsArguments(MarkdownPdfRunnerSettings settings)
{
var args = new ProcessArgumentBuilder();
settings?.Evaluate(args);
return args;
}
}
</code></pre>
<h2 id="markdownpdfrunnersettings">MarkdownPdfRunnerSettings</h2>
<p>This class is going to contain the properties that apply to all the commands we're going to use and the instructions for converting those into command line arguments. Markdown-pdf doesn't have separate "commands" just options so we only need one settings type. See <a href="https://github.com/cake-contrib/Cake.Bower">Cake.Bower</a> for an example of a plugin that uses multiple settings types.
We now need to know what options are available to us through markdown-pdf's CLI. Handily this is available <a href="https://www.npmjs.com/package/markdown-pdf#cli-interface">here</a>.</p>
<pre><code>Usage: markdown-pdf [options] <markdown-file-path>
Options:
-h, --help output usage information
-V, --version output the version number
<markdown-file-path> Path of the markdown file to convert
-c, --cwd [path] Current working directory
-p, --phantom-path [path] Path to phantom binary
-h, --runnings-path [path] Path to runnings (header, footer)
-s, --css-path [path] Path to custom CSS file
-z, --highlight-css-path [path] Path to custom highlight-CSS file
-m, --remarkable-options [json] Options to pass to Remarkable
-f, --paper-format [format] 'A3', 'A4', 'A5', 'Legal', 'Letter' or 'Tabloid'
-r, --paper-orientation [orientation] 'portrait' or 'landscape'
-b, --paper-border [measurement] Supported dimension units are: 'mm', 'cm', 'in', 'px'
-d, --render-delay [millis] Delay before rendering the PDF
-t, --load-timeout [millis] Timeout before the page is rendered in case `page.onLoadFinished` isn't fired
-o, --out [path] Path of where to save the PDF
</code></pre>
<p>I'm going to add a boolean property for each of the help and version switches, and string/int/enum properties for each of the other values as appropriate, except for the working directory. The working directory is already a property on the ToolSettings we inherited from. Our MarkdownPdfRunnerSettings class should now look like this</p>
<pre><code class="language-csharp">public class MarkdownPdfRunnerSettings : ToolSettings
{
public bool Help { get; set; }
public bool Version { get; set; }
public string FilePath { get; set; }
public string PhantomPath { get; set; }
public string RunningsPath { get; set; }
public string CssPath { get; set; }
public string HighlightCssPath { get; set; }
public string RemarkableOptions { get; set; }
public MarkdownPdfPaperFormat PaperFormat { get; set; }
public MarkdownPdfOrientation Orientation { get; set; }
public string PaperBorder { get; set; }
public int RenderDelay { get; set; }
public int LoadTimeout { get; set; }
public string OutFilePath { get; set; }
}
public enum MarkdownPdfPaperFormat
{
None,
A3,
A4,
A5,
Legal,
Letter,
Tabloid
}
public enum MarkdownPdfOrientation
{
None,
Portrait,
Landscape
}
</code></pre>
<p>I've obviously made some decisions about what type to use to store details about the property, such as an enum for PaperFormat and Orientation and you'll need to make similar decisions when creating your adding depending on what options are available for the tool you're creating an addin for.
We need to add a method that will evaulate these properties and produce our ProcessArgumentBuilder for use in the runner. This is where the evaluate command comes in. Effectively it will query each property and the relevant string to the ProcessArgumentBuilder passed in.</p>
<pre><code class="language-csharp">internal void Evaluate(ProcessArgumentBuilder args)
{
if (Help)
args.Append("--help");
if (Version)
args.Append("--version");
if (!string.IsNullOrWhiteSpace(PhantomPath))
args.Append($"--phantom-path {PhantomPath}");
if (!string.IsNullOrWhiteSpace(RunningsPath))
args.Append($"--runnings-path {RunningsPath}");
if (!string.IsNullOrWhiteSpace(CssPath))
args.Append($"--css-path {CssPath}");
if (!string.IsNullOrWhiteSpace(HighlightCssPath))
args.Append($"--highlight-css-path {HighlightCssPath}");
if (!string.IsNullOrWhiteSpace(RemarkableOptions))
args.Append($"--remarkable-options {RemarkableOptions}");
if (PaperFormat != MarkdownPdfPaperFormat.None)
args.Append($"--paper-format {PaperFormat}");
if (Orientation != MarkdownPdfOrientation.None)
args.Append($"--paper-orientation {Orientation}");
if (!string.IsNullOrWhiteSpace(PaperBorder))
args.Append($"--paper-border {PaperBorder}");
if (RenderDelay > 0)
args.Append($"--render-delay {RenderDelay}");
if (LoadTimeout > 0)
args.Append($"--load-timeout {LoadTimeout}");
if (!string.IsNullOrWhiteSpace(OutFilePath))
args.Append(OutFilePath);
if (!string.IsNullOrWhiteSpace(FilePath))
args.Append(FilePath);
}
</code></pre>
<h2 id="setting-properties">Setting properties</h2>
<p>I'm going to add a set of fluent extension methods for setting these properties so we can set several of them at once in a single lambda expression.</p>
<pre><code class="language-csharp">public static class MarkdownPdfRunnerSettingsExtensions
{
public static MarkdownPdfRunnerSettings UseWorkingDirectory(this MarkdownPdfRunnerSettings settings, DirectoryPath workingDirectory)
{
settings.WorkingDirectory = workingDirectory;
return settings;
}
public static MarkdownPdfRunnerSettings WithHelp(this MarkdownPdfRunnerSettings settings)
{
settings.Help = true;
return settings;
}
public static MarkdownPdfRunnerSettings WithVersion(this MarkdownPdfRunnerSettings settings)
{
settings.Version = true;
return settings;
}
public static MarkdownPdfRunnerSettings WithPhantomPath(this MarkdownPdfRunnerSettings settings, string phantomPath)
{
settings.PhantomPath = phantomPath;
return settings;
}
public static MarkdownPdfRunnerSettings WithRunningsPath(this MarkdownPdfRunnerSettings settings, string runningsPath)
{
settings.RunningsPath = runningsPath;
return settings;
}
public static MarkdownPdfRunnerSettings WithCssPath(this MarkdownPdfRunnerSettings settings, string cssPath)
{
settings.CssPath = cssPath;
return settings;
}
public static MarkdownPdfRunnerSettings WithHighlightCssPath(this MarkdownPdfRunnerSettings settings, string highlightCssPath)
{
settings.HighlightCssPath = highlightCssPath;
return settings;
}
public static MarkdownPdfRunnerSettings WithRemarkableOptions(this MarkdownPdfRunnerSettings settings, string remarkableOptions)
{
settings.RemarkableOptions = remarkableOptions;
return settings;
}
public static MarkdownPdfRunnerSettings WithPaperFormat(this MarkdownPdfRunnerSettings settings, MarkdownPdfPaperFormat paperFormat)
{
settings.PaperFormat = paperFormat;
return settings;
}
public static MarkdownPdfRunnerSettings WithOrientation(this MarkdownPdfRunnerSettings settings, MarkdownPdfOrientation orientation)
{
settings.Orientation = orientation;
return settings;
}
public static MarkdownPdfRunnerSettings WithPaperBorder(this MarkdownPdfRunnerSettings settings, string border)
{
settings.PaperBorder = border;
return settings;
}
public static MarkdownPdfRunnerSettings WithRenderDelay(this MarkdownPdfRunnerSettings settings, int renderDelay)
{
settings.RenderDelay = renderDelay;
return settings;
}
public static MarkdownPdfRunnerSettings WithLoadTimeout(this MarkdownPdfRunnerSettings settings, int loadTimeout)
{
settings.LoadTimeout = loadTimeout;
return settings;
}
public static MarkdownPdfRunnerSettings WithFilePath(this MarkdownPdfRunnerSettings settings, string filePath)
{
settings.FilePath = filePath;
return settings;
}
public static MarkdownPdfRunnerSettings WithOutFilePath(this MarkdownPdfRunnerSettings settings, string outFilePath)
{
settings.OutFilePath = outFilePath;
return settings;
}
}
</code></pre>
<h2 id="commands">Commands</h2>
<p>Finally we're going to add the commands to process the settings and run the command line tool.
Add the following two definitions to the interface (again for addins that will require more than one command use Cake.Bower as an example)</p>
<pre><code class="language-csharp">IMarkdownPdfRunner Run(Action<MarkdownPdfRunnerSettings> configure = null);
IMarkdownPdfRunner Run(MarkdownPdfRunnerSettings settings);
</code></pre>
<p>Then to the MarkdownPdfRunner class to fill in the implementation
The first method will create a new settings object and run the lambda passed in before passing on the responsibility for execution to the other method.</p>
<pre><code class="language-csharp">public IMarkdownPdfRunner Run(Action<MarkdownPdfRunnerSettings> configure = null)
{
var settings = new MarkdownPdfRunnerSettings();
configure?.Invoke(settings);
return Run(settings);
}
</code></pre>
<p>The method accepting a settings object will use our GetSettingsArguments method from earlier to build the ProcessArgumentBuilder and pass that on to the base classes Run method which will run the tool passing in the arguments given</p>
<pre><code class="language-csharp">public IMarkdownPdfRunner Run(MarkdownPdfRunnerSettings settings)
{
var args = GetSettingsArguments(settings);
Run(settings, args);
return this;
}
</code></pre>
<p>The only thing that's missing now (apart from a build script, unit tests, etc.) is the ability to run this from a cake file.
In order to call any of this from cake you need a cake alias.
I'm going to show two different options for this part, one using a CakeMethodAlias and the other a CakePropertyAlias. For this particular addin the CakeMethodAlias is probably the most suitable since there aren't different commands which the markdown-pdf tool accepts, but for something like npm, bower, gulp, etc which have subcommands the property alias approach is the way to go. The difference between the two is that a CakePropertyAlias can't accept arguments.</p>
<h3 id="cakemethodalias">CakeMethodAlias</h3>
<p>Here we'll create a new static class with an ICakeContext extension method. My adding the CakeMethodAlias attribute this method will be available to us from our cake script. This method will instantiate a runner and call its Run method.</p>
<pre><code class="language-csharp">public static class MarkdownPdfRunnerAliases
{
[CakeMethodAlias]
public static IMarkdownPdfRunner MarkdownPdf(this ICakeContext context,
Action<MarkdownPdfRunnerSettings> configure)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
var runner = new MarkdownPdfRunner(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools);
return runner.Run(configure);
}
}
</code></pre>
<p>This addin is now ready to be used in a cake script as below.</p>
<pre><code class="language-csharp">#reference "./bin/Cake.Markdown-Pdf.dll" // Use the relative path to your Cake.Markdown-Pdf.dll file built by Visual Studio.
var target = Argument("target", "Default");
Task("Default")
.Does(() =>
{
MarkdownPdf(s => s.WithFilePath("README.md"));
});
RunTarget(target);
</code></pre>
<h3 id="cakepropertyalias">CakePropertyAlias</h3>
<p>This approach will return a new MarkdownPdfRunner. This exposes all of the public methods on the runner to the cake script through the object, which is why this approach is more useful to addins that have multiple commands (e.g. bower's install, update, and uninstall commands etc.)</p>
<pre><code class="language-csharp">[CakePropertyAlias]
public static IMarkdownPdfRunner MarkdownPdf(this ICakeContext context)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
return new MarkdownPdfRunner(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools);
}
</code></pre>
<p>This changes the call from <code>MarkdownPdf(s => s.WithFilePath("README.md"));</code> to <code>MarkdownPdf.Run(s => s.WithFilePath("README.md"));</code>
A subtle difference but as stated, important if there's more than one command that needs to be run.</p>
<h2 id="final-steps">Final steps</h2>
<ul>
<li>Check out Cake.Testing for some utilities to help with testing these scripts. I'll write a follow up blog post soon with more details on how to unit test this addin.
Add to nuget, and then you can change the #reference line to #addin and your script will automatically download it when run.</li>
<li>Setup a cake script for this project.</li>
<li>Check out <a href="https://github.com/cake-contrib/Home">Cake-Contrib on Github</a> and consider adding your addin on there. I started with Cake.Bower, and decided to write this blog post off the back of that.</li>
</ul>
<p>The resulting addin from this blog post can be found at <a href="https://github.com/wozzo/Cake.Markdown-Pdf">Wozzo/Cake.Markdown-Pdf on github</a> and the blog series can be found at <a href="https://github.com/wozzo/Cake_Addin_Blog_Posts">Wozzo/Cake_Addin_Blog_Posts</a></p>
<p>No blog post on Cake is complete without a few puns, and bonus, now while you're googling technical stuff you always get a few recipes for delicious cakes thrown in. So what is cake and why do we need add ins?</p>https://wbates.net/posts/seeding-data-using-entity-framework-in-asp.net-coreSeeding data using Entity Framework in ASP.NET Core2016-09-17T00:00:00Z<p>With thanks to <a href="http://coderhints.com/ef7-seed-user/">coderhints.com</a> for the initial steps in doing this. I've done a bit of refactoring and added some additional steps to enable setting up a lot of data quickly and easily.</p>
<p>I'm going to demonstrate how you can set up some default information in your database using Entity Framework, and a default user for your application. Firstly find the ConfigureServices method in the Startup class. This is the first way my approach differs from above; ConfigureServices is where the DbContext is added to the application and it's where it should be setup, while Configure is where you tell the application how to respond to HTTP requests [1]. At the end of this method add the following code.</p>
<pre><code class="language-csharp">var serviceProvider = services.BuildServiceProvider();
var dbContext = serviceProvider.GetService<ApplicationDbContext>();
var userManager = serviceProvider.GetService<UserManager<ApplicationUser>>();
var roleManager = serviceProvider.GetService<RoleManager<IdentityRole>>();
dbContext.Database.Migrate();
dbContext.EnsureSeedData(userManager, roleManager).Wait();
</code></pre>
<p>The serviceProvider is what allows us to retrieve instances of the services we need, in this case the ApplicationDbContext, UserManager and RoleManager. Calling the migrate method ensures we have applied the latest migrations and are working with an up to date schema. Finally that horrible red line there is the method where we're going to our seeding. So flick over to your ApplicationDbContext class and add a new method with the following signature</p>
<pre><code class="language-csharp">public async Task EnsureSeedData(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager)
</code></pre>
<p>This method needs to be rerunnable without throwing exceptions for adding repeat items, and nor do you want that, so every call you make that's going to update the database should have a check to make sure that data doesn't already exist.</p>
<h1 id="adding-a-default-user">Adding a default user</h1>
<p>We're going to start by setting up our default 'Administrator' user. To do that we need to have an Administrator role.</p>
<pre><code class="language-csharp">// Add roles
if (!await Roles.AnyAsync(r => string.Equals(r.Name, "Administrator", System.StringComparison.OrdinalIgnoreCase)))
await roleManager.CreateAsync(new IdentityRole("Administrator"));
</code></pre>
<p>Here we check for the roles existence and if it isn't there, we add it. This should be repeated for any roles you require.
Now to create our new user</p>
<pre><code class="language-csharp">// Add Admin user
var adminUser = userManager.Users.FirstOrDefault(u => string.Equals(u.UserName, "admin@wbates.net", System.StringComparison.OrdinalIgnoreCase));
if (adminUser == null)
{
adminUser = new ApplicationUser
{
UserName = "admin@wbates.net",
Email = "admin@wbates.net"
};
var result = await userManager.CreateAsync(adminUser, "AReallyDifficultImpossibleToGuessPassword123#");
if (result != IdentityResult.Success)
throw new System.Exception($"Unable to create '{adminUser.UserName}' account: {result}");
}
</code></pre>
<p>As always, check for the users existence and because we'll be wanting to ensure this user is enabled and has the 'Administrator' role we'll keep a reference to it. It's worth noting that no Exception is normally thrown if the user has not been created, instead the object returned from the CreateAsync call will tell us and give a message if it has failed. The most common reason for this is that the password is not complicated enough, hence the '#' on the password above.</p>
<pre><code class="language-csharp">await userManager.SetLockoutEnabledAsync(adminUser, false);
</code></pre>
<p>Well we wouldn't want our administrator to be locked out of the system would we?</p>
<pre><code class="language-csharp">// Check AdminUserRoles
var adminRoles = await userManager.GetRolesAsync(adminUser);
if (!adminRoles.Any(r => string.Equals(r, "Administrator")))
await userManager.AddToRoleAsync(adminUser, "Administrator");
</code></pre>
<p>Finally we check that the user is in the admin role, and we're done with setting up our default user. Check the previous post for how ASP.NET Core configuration allows us to set some of the above defaults using .json files instead of hard coding them as we have here.</p>
<h1 id="adding-seed-data">Adding seed data</h1>
<p>Assuming you've added additional DbSets to your ApplicationDbContext for your models, they'll be available in the EnsureSeedData method. As above you'll want to check for each values existing before adding it, but otherwise create an instance of the model and .Add it to the relevant DbSet. The changes won't be saved until you call SaveChangesAsync and it has completed, so leave that until the end. Here's a quick sample that shows how to add some records and save the changes to finish.</p>
<pre><code class="language-csharp">// Ensure default MediaTypes
if (!MediaTypes.Any(t => string.Equals(t.Name, "DVD", System.StringComparison.OrdinalIgnoreCase)))
{
var mediaType = new MediaType { Name = "DVD", Description = "Digital Versatile Disc" }
MediaTypes.Add(mediaType);
}
await SaveChangesAsync();
</code></pre>
<h1 id="top-tips">Top tips</h1>
<p>Use ASP.NET's Configuration Options[2] to describe default values and allow them to be set by using .json files for different environments.
Move most of this additional configuration out to an extension method on the IServicesCollection. Keep the configure method clean.
Define the roles as auto properties in a static class so you don't need to use strings like �Administrator� to reference them everywhere</p>
<p>With thanks to <a href="http://coderhints.com/ef7-seed-user/">coderhints.com</a> for the initial steps in doing this. I've done a bit of refactoring and added some additional steps to enable setting up a lot of data quickly and easily.</p>https://wbates.net/posts/asp.net-core-configurationASP.NET Core Configuration2016-04-24T00:00:00Z<p>Only just beginning to dive into ASP.NET Core (or ASP.NET 5 if you prefer), but there's a great deal to like. Configuration in Core is extremely simple. Values can simply be added to a json (or other format, but why bother) configuration file and are then available to your application where Configuration is instantiated.
The default projects will include the following lines in the Startup method of Startup.cs.</p>
<pre><code class="language-csharp">var builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
Configuration = builder.Build().ReloadOnChanged("appsettings.json");
</code></pre>
<p>This will load the values from the appsettings.json file (if it exists) into the Configuration property. Multiple calls to this method can be chained together (see environment specific config below). If appsettings.json looked like this</p>
<pre><code class="language-json">{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Verbose",
"System": "Information",
"Microsoft": "Information"
}
},
"MyConfigValue": "An important configuration string"
}
</code></pre>
<p>The logging stuff is default, I've added the MyConfigValue key, which means that once Configuration is instantiated I can then access it's value within the Startup class with the following:</p>
<pre><code class="language-csharp">Configuration["MyConfigValue"];
</code></pre>
<p>The Logging.LogLevel.Default value can be retrieved using</p>
<pre><code class="language-csharp">Configuration["Logging:LogLevel:Default"];
</code></pre>
<p>By creating config classes specific to different areas of your application you can use the built in DI to inject that config only where it's needed.</p>
<p>If we replace MyConfigValue in the appsettings.json file above with the following key</p>
<pre><code class="language-json">"MyConfig": {
"Value1": "This is the first config value",
"Value2": "And this is the second",
"Number1": 15
}
</code></pre>
<p>Now add a new class to the project which can be called anything but I'm going with MyConfig.cs</p>
<pre><code class="language-csharp">public class MyConfig
{
public string Value1 { get; set; }
public string Value2 { get; set; }
public int Number1 { get; set; }
}
</code></pre>
<p>As you can see the properties match up. We now need to configure the options to be populated into a MyConfig object. Back to startup.cs and find the ConfigureServices method. Add the following lines</p>
<pre><code class="language-csharp">services.AddOptions();
services.Configure<MyConfig>(Configuration.GetSection("MyConfig");
</code></pre>
<p>This takes in the Configuration property and configures it so that when we ask for a MyConfig object it will provide it using whatever it has read from appsettings.json file by matching the classes properties to the keys with the same name. To see that in use create a new controller and add a constructor passing in IOptions as a parameter. This will provide an accessor to a populated config object. which is then accessible in the constructor and can be assigned as a property on that controller.</p>
<pre><code class="language-csharp">public MyController(IOptions<MyConfig> myConfig)
{
_myConfig = myConfig.Value;
}
</code></pre>
<p>The _myConfig object will now contain our config for this section.</p>
<h1 id="environment-specific-config">Environment specific config</h1>
<p>You can easily add additional config files for each environment by calling AddJsonFile on the ConfigurationBuilder multiple times. Since I wrote this the default configure method has the following code.</p>
<pre><code class="language-csharp">var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
</code></pre>
<p>The later files take priority over the earlier ones, overwriting any earlier values. See this page for info on how to set the EnvironmentName. I also recommend adding *.development.json to your .gitignore files so that your development environment configuration isn't committed.</p>
<p>Only just beginning to dive into ASP.NET Core (or ASP.NET 5 if you prefer), but there's a great deal to like. Configuration in Core is extremely simple. Values can simply be added to a json (or other format, but why bother) configuration file and are then available to your application where Configuration is instantiated.
The default projects will include the following lines in the Startup method of Startup.cs.</p>https://wbates.net/posts/configuring-for-pretty-urls-in-angularjs-and-visual-studioConfiguring for Pretty URLs in AngularJS and Visual Studio2015-06-08T00:00:00Z<p>Pretty URLs look much better than some of the long query string type URLs we've gotten used to.
AngularJS makes it very simple to use pretty urls just follow the steps below and then configure for your server.</p>
<p>You need to include the ngRoute module with the following line:</p>
<pre><code class="language-html"><script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular-route.js"></script>
</code></pre>
<p>Our SPA also needs to know where to put the templates when a sub page is requested. Create a div in your page and add the ng-view attribute. That's it.</p>
<pre><code class="language-html"><div ng-view>
<!-- Content will be injected here -->
</div>
</code></pre>
<p>Next you need to add <code>$routeProvider</code> and <code>$locationProvider</code> to your config and set them up.
You use the when() function to define which view to serve up depending on what the path is. For example:</p>
<pre><code class="language-javascript">mainApp.config(function ($routeProvider, $locationProvider) {
// Route for home page
$routeProvider.when('/', {
templateUrl: '_home/home.html',
controller: 'CarouselCtrl'
})
.when('/about', {
templateUrl: '_about/about.html',
controller: 'aboutCtrl'
})
.otherwise({
redirectTo: "/"
});
$locationProvider.html5Mode(true);
});
</code></pre>
<p>This means that if I go to <a href="http://yourserver.com/about">http://yourserver.com/about</a> the page will serve up the contents of the _about/about.html.
The $locationProvider line is required if you would like to also remove the hash from the URL. Without this your urls will appear like <a href="http://yourserver.com/#/about;">http://yourserver.com/#/about;</a> If you aren't worried about the # then you're finished and the following steps will not be necessary. Older browsers will also default back to using the # if they aren't fancy enough for pretty URLs.</p>
<p>Configuring your webserver to always route back to the index.html file, or whatever you main app's file is, is probably the trickiest aspect of this process. You also need to exclude any folders which actually do exist because otherwise the webserver will route back to index.html when trying to find each of your site assets (css files, images, etc.).</p>
<h1 id="for-iis-express">For IIS Express:</h1>
<p>(See below for IIS Web Core as used in VS)</p>
<ol>
<li>Download <a href="http://www.iis.net/downloads/microsoft/url-rewrite">URL Rewrite</a></li>
<li>Run IIS Manager (found in the start menu)</li>
<li>Expand the panel on the left to find your site</li>
<li>Load URL Rewrite</li>
</ol>
<p><img src="../assets/images/add-rule.png" class="img-fluid" alt="Add Url Rewrite rule" /></p>
<ol start="5">
<li><p>Click "Add Rule"</p>
</li>
<li><p>Choose a blank inbound rule
8 Call it Pretty URL Exclusions. These will be the folders that actually exist that you don't want to be rerouted.</p>
</li>
<li><p>Select "does not match the pattern"</p>
</li>
<li><p>Add in a regular expression to ignore all the folders that exists</p>
<p>For example:
To ignore the common folder and its contents use <code>common/.*</code>
Then use the pipe (|) to separate it from another folder
You might end up with something like
<code>css/.*|js/.*|images.*</code></p>
</li>
<li><p>For these folders we want the action type to be "None"</p>
</li>
<li><p>Check "Stop processing of subsequent rules</p>
</li>
<li><p>Apply the rule</p>
</li>
</ol>
<p><img src="../assets/images/edit-inbound-rule.png" class="img-fluid" alt="Edit inbound url rewrite rule" /></p>
<ol start="12">
<li>Create another blank inbound rule</li>
<li>Call it Pretty URL Redirect.</li>
<li>Select Matches the pattern and input .* to match all paths</li>
<li>Choose Redirect as the action type</li>
<li>Set the url to index.html or your main app file.</li>
<li>Apply the rule and restart the webserver.</li>
</ol>
<h1 id="for-iis-web-core-visual-studio">For IIS Web Core (Visual Studio)</h1>
<p>I found the easiest thing to do here was just add the same rules as above directly into the web.config file in the project.
Open it up and add in the following (edited for your folders ofcourse: see IIS Express above for more details) between your configuration tags.</p>
<pre><code class="language-xml"><system.webServer>
<rewrite>
<rules>
<clear />
<rule name="Pretty URL Exclusions" stopProcessing="true">
<match url="(css/.*|js/.*|images/.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
<action type="None" />
</rule>
<rule name="Pretty URL Redirect">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
<action type="Rewrite" url="index.html" />
</rule>
</rules>
</rewrite>
</system.webServer>
</code></pre>
<p>Restart your server and it should take effect straight away.</p>
<h1 id="apache-web-server">Apache Web Server</h1>
<p>Apache is configured using the .htaccess file. It's typically located in the wwwroot folder. Find and open it in your editor of choice. Assuming the mod_rewrite.c is installed add the following lines to the .htaccess file to check if a file exists and if not redirect to the base file as above.</p>
<pre><code class="language-xml"><ifModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !index
RewriteRule (.*) index.html [L]
</ifModule>
</code></pre>
<p>Note: If you're doing this on a unix based system it's likely case sensitive so you will need to be very careful that you have typed all of your src tags in correctly. Or you could add the following as well and install the mod_speling.c if necessary.</p>
<pre><code class="language-xml"><IfModule mod_speling.c>
CheckSpelling On
CheckCaseOnly On
</IfModule>
</code></pre>
<p>Restart your server and you should be good to go.</p>
<h1 id="hints">Hints:</h1>
<ol>
<li>Add <code>caseInsensitiveMatch: true</code> to the when functions to remove case sensitivity.</li>
<li>Check that all of your assets are linked correctly from your index.html file.
For example <code><link href="Content/nav.css" rel="stylesheet" /></code> will not work if you attempted to access one of the "subfolders" because the browser would be looking in that subfolder for the content folder and nav file.
Add a / to the front of the path to solve this problem (took me 20 minutes to figure that one out).
<code><link href="/Content/nav.css" rel="stylesheet" /></code> will work</li>
</ol>
<p>Pretty URLs look much better than some of the long query string type URLs we've gotten used to.
AngularJS makes it very simple to use pretty urls just follow the steps below and then configure for your server.</p>https://wbates.net/posts/closures-in-csharpClosures in C#2015-05-28T00:00:00Z<p>Closures are a vital part of any language that passes functions around as variables. They ensure that everything that was available when we defined the function is available when it's called later, even if it's called in a different scope or long after the original scope has completely ended. There's a vast amount of information available on closures in Javascript because as a classless language where functions are always first class objects closures are regularly used to imitate class like functionality.</p>
<p>For example:</p>
<pre><code class="language-javascript">function fakeClass() {
var publicVar = "This variable will be public";
this.publicVar = publicVar;
var privateVar = "This variable will be private";
this.getPrivateVar = function() {
return privateVar;
}
}
var instance = new fakeClass();
// This will output "This variable will be public"
console.log(instance.publicVar);
// This will output "This variable will be private"
console.log(instance.getPrivateVar());
// This will throw a type undefined error
console.log(instance.privateVar);
</code></pre>
<p>In the above code the publicVar variable is attached to the this object and can be set and read through accessing fakeClass.publicVar. However, the privateVar variable cannot be accessed since it has not been attached to the this object. That is where the getPrivateVar function comes in. The privateVar variable was available at the time the function was defined and so it will be available when the getPrivateVar function is called. The privateVar itself is no longer in scope at the bottom of the example and since it wasn't attached to the this object when we try to access it, we'll get an error.</p>
<p>This is just an example of how closures can be used and what they do. In C# we obviously have far better ways of declaring private variables, namely the private keyword. That's not to say that closures are suddenly useless in C#, in fact any time we want a function to have data that persists between uses we can use a closure to do so. The alternative would be to create a class that contains properties for all of the data we want to use, but closures are much briefer in terms of how much code is needed.</p>
<h1 id="prime-generator">Prime Generator</h1>
<p>For the Project Euler problems I realised I was needing to copy code between problems and one of the things I was regularly doing was generating prime numbers. Doing so with a normal function would require that I knew how many primes I needed, and could then return an array with all of the primes up to that point. .Net's IEnumerable achieves something very similar to this; closures are just another tool in the box. I like how simple they are to write and use, but sometimes we may need the more advanced functionality of the IEnumerable.
The pseudo code for generating prime numbers looks something like the following, where isPrime(num) is a function which returns a boolean telling us if the num variable passed is a prime number or not (more notes on that at the end).</p>
<pre><code>private int getPrime() {
int current = 2;
while (true) {
if (isPrime(current)) {
return current;
}
current++;
}
}
</code></pre>
<p>You've possibly already spotted the problem. As soon as a prime is found, which is immediately since we start with a prime, the function will return and the current variable will never get incremented. The next time the function is called the current value will once again be set as 2 and will be returned immediately. Enter the closure.</p>
<p>What if we could put the while loop into a separate function that incremented the current value? We first need to change the above function so that it returns a delegate function. Making a function a delegate means it can be passed around like a variable in the same way as all functions can be in javascript.</p>
<p>The function declaration needs to be changed to <code>private Func\<int\> getPrimeGenerator()</code>. This tells the compiler that we will be expecting the return from this function to be a delegate function which itself has a return type of int. Next we declare and initialise the variable that stores our current position, then we declare the function that will return our primes.</p>
<pre><code class="language-csharp">private Func<int> getPrimes()
{
int current = 1;
Func<int> nextPrime = delegate()
{
current++;
while (!isPrime(current))
{
current++;
}
return current;
};
return nextPrime;
}
</code></pre>
<p>The pseudo code has been modified slightly so that the if statement has been implemented with the while loop, but the functionality isn't any different. Each time the delegate function is called it will increment the current variable until a new prime is found and return that. A reference to the current variable has been closed into the delegate function. Let's see how this is used to get the first hundred prime numbers.</p>
<pre><code class="language-csharp">var getNextPrime = getPrimes();
for (int i = 0; i < 100; i++) {
Console.WriteLine(getNextPrime());
}
</code></pre>
<p>That's it. We call the first function and store the returned delegate in another variable, then we call that as many times as we like, each time it will return the next prime number that can be found. Eventually this code will hit the maximum value for an integer and will go back to the beginning, but dealing with that is left as an exercise for the reader.</p>
<p>Closures are a vital part of any language that passes functions around as variables. They ensure that everything that was available when we defined the function is available when it's called later, even if it's called in a different scope or long after the original scope has completely ended. There's a vast amount of information available on closures in Javascript because as a classless language where functions are always first class objects closures are regularly used to imitate class like functionality.</p>https://wbates.net/posts/theres-fast-code-and-theres-fast-codeThere's fast code and there's fast code2015-05-24T00:00:00Z<p>I was solving a problem recently and came across a number of different ways of solving it. I decided to test them each out to see which was the quickest. The functions purpose was to return the number of digits in an integer. I've tried four different methods out. Two of them are single line solutions, one uses a bit of looping to solve the problem and the final one... well you'll see.</p>
<h1 id="log-method">Log Method</h1>
<p>Logarithms return the power a number needs to be raised to in order to give that number. For example if our number is 1000. Log to the base 10 will return 3, because 1000 = 10^3. Any number up to 10000 will return a decimal value which rounds down to 3. e.g. log10(9999) = 3.99956568.... so we can use a combination of Math.Floor and Math.Log10 to give us the number of digits required. Our function should look something like this.</p>
<pre><code class="language-csharp">private int IntegerLength_Log(int num)
{
return (int)Math.Floor(Math.Log10(num) + 1);
}
</code></pre>
<p>Note: You need to add 1 to the return of the log value to get the number of digits.</p>
<h1 id="string-method">String method</h1>
<p>This method relies on built in methods of converting the integer to a string and using the length property. This is probably the simplest and most versatile technique (it should handle negatives), but I was also convinced that because of the overhead associated with converting the integer to a string that this would be the least efficient method.</p>
<p>The only code required is</p>
<pre><code class="language-csharp">private int IntegerLength_String(int num)
{
return num.ToString().Length;
}
</code></pre>
<h1 id="looping-method">Looping method</h1>
<p>This method loops, whilst increasing a variable's value by a power of 10 each cycle until that number is larger than the input.</p>
<pre><code class="language-csharp">private int IntegerLength_Loop(int num)
{
int length = 1;
int comparator = 10;
while (num > comparator)
{
comparator *= 10;
length++;
}
return length;
}
</code></pre>
<h1 id="if-statements">if statements</h1>
<p>The final method is easily the lengthiest to write, and also one that as a problem solving mathematical type makes me cringe a little. There's no elegance to this solution, just a bit of brute force and lots of repetitive code. It requires using a series of if statements to check the integers value and return the length when we know what number it is less than. This is effectively the same as the above method but without the loop.</p>
<pre><code class="language-csharp">private int IntegerLength_If(int num)
{
if (num < 10)
{
return 1;
}
else if (num < 100)
{
return 2;
}
else if (num < 1000)
{
return 3;
}
else if (num < 10000)
{
return 4;
}
else if (num < 100000)
{
return 5;
}
else if (num < 1000000)
{
return 6;
}
else if (num < 10000000)
{
return 7;
}
else if (num < 100000000)
{
return 8;
}
else if (num < 1000000000)
{
return 9;
}
else
{
return 10;
}
}
</code></pre>
<p>If using a comparison like this on a BigInteger even more if statements would be required to cover up to the maximum value, but for C#'s int type this has us covered.</p>
<h1 id="timing">Timing</h1>
<p>Each of these functions was then run using a for loop varying the input from 1'000 to 10'000'000 and the number of ticks taken to complete was recorded.</p>
<p>The results are in: String method 2'937'886 ticks, Log Method 1'119'507, the Looping method 642'857 & the If method 437'401. The cringey set of if statements method is nearly 7 times faster than the string conversion method and 2.5 times faster than the log method.</p>
<p>I think if this were for production code I'd probably use the looping method after changing it to handle integers of any type (i.e. BigInteger) and negative values because of the versatility and ease of rewriting should any problems be found, but this had definitely made me rethink some the assumptions I've built up over the years about how quickly code will run in comparison to how many lines of code there are.</p>
<p>I was solving a problem recently and came across a number of different ways of solving it. I decided to test them each out to see which was the quickest. The functions purpose was to return the number of digits in an integer. I've tried four different methods out. Two of them are single line solutions, one uses a bit of looping to solve the problem and the final one... well you'll see.</p>