Thursday, January 31, 2019

ASP.NET Core Diagnostic Scenarios

While browsing through some GitHub repositories I stumbled over this repo by David Fowler(probably this name rings a bell?):

Start by having a look at the Guidance markdown file. It provides an entrance to guidance and examples based real customer experiences for:

A must read for every ASP.NET (Core) developer!

Wednesday, January 30, 2019

Config transformations for your app.config

Config transformations are not limited to web.config files. Thanks to tools like SlowCheetah, you can apply this to any config file.

If you don’t want to introduce an extra dependency, you can still apply these transformations by using the TransformXml task from the Microsoft.Web.Publishing.Tasks.dll inside your msbuild file:

Import the task dll:

<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />

Call the imported task:

<Target Name="AfterBuild">

<MakeDir Directories="$(OutputPath)" Condition="!Exists('$(OutputPath)')" />

<TransformXml Source="App.config" Transform="App.$(Configuration).config" Destination="$(OutputPath)\SampleApp.exe.config" />

<TransformXml Source="App.config" Transform="App.$(Configuration).config" Destination="$(OutputPath)\SampleApp.vshost.exe.config" />

</Target>

Tuesday, January 29, 2019

GraphQL–DotNet - Organizing your queries

In GraphQL there is only a single root Query object. As a consequence the root object keeps growing over time.

To solve this problem you can split a root object in multiple groups:

The trick is to return an empty object.
Field<NonNullGraphType<ProductsQuery>>().Name("Products").Resolve(context => new { });
Remark: This also works for mutations and subscriptions.

Monday, January 28, 2019

Unable to load DLL 'libSkiaSharp' or one of its dependencies: Access is denied.

One of my projects we are using SkiaSharp to create and manipulate images on the server.

However when we tried to load the SkiaSharp library through our ASP.NET Core application, we got the following error message:

System.DllNotFoundException: Unable to load DLL 'libSkiaSharp' or one of its dependencies: Access is denied.

Problem is that this is a native library and the application pool user by default doesn’t have ‘Execute’ permissions to load and run this library.

Here are the steps to fix this issue:

  • Go to your local nuget cache, by default %userprofile%\.nuget\packages
  • Search for the SkiaSharp folder
  • Right click on the folder and choose Properties from the context menu
  • The Properties window is loaded. Go to the Security tab and select the application pool identity from the list of users
  • Click on Edit and check the Read and Execute permission in the Allow column
  • Click on OK to apply the changes.

That’s it!

Friday, January 25, 2019

Improve your Postman skills using the in-app Postman lessons

Did you know that Postman had in interactive in-app learning center? You can opt-in to any lesson to improve or practice your Postman skills.

From the announcement:

When you enter the learning center in the app, you’ll see a library of interactive lessons that range from beginner to expert level. We designed the learning center to track your progress, so your lessons will always be geared to your skillset. In addition, we are consistently adding new lessons. Your learning center will automatically populate with new and relevant material for you to master. 

Here are some things you can learn right now on Postman’s in-app learning center

  • Designing and mocking APIs (2 lessons) – Designing and mocking your APIs before you build them helps you define dependencies, create contracts, and identify expected functionality as well as potential problems.
  • Debugging and manual testing (4 lessons) – Manually testing and debugging your APIs is a great skill, and it’s the first step on the way to automation!
  • Automated testing (4 lessons) – Save time by using Postman’s powerful test scripts to automate your tests.
  • API documentation (1 lesson) – Postman allows you to automatically create beautiful, web-viewable documentation right from your collection.
  • Monitoring (1 lesson) – Monitoring allows you to create automated tests that monitor your APIs on a custom schedule. You can monitor for uptime, responsiveness, and correctness.
  • Collaboration (1 lesson) – This lesson will show you how to use Postman’s tools to strengthen your team’s collaboration efficiency.

To get started you only have to click on the Learn button in the right bottom corner of your Postman app:

After logging in, you’ll see a list of all available lessons:

Choose a lesson from the list and click Learn to get started:

Thursday, January 24, 2019

Angular Console - The hidden Electron magic

I blogged before about the Angular Console. I don’t use it every day but it has become my ‘go to’ tool when I have to install and try new Angular schematics.

The application itself is built as a small Electron app where they are using Electron as a small wrapper around a web app served by chromium and node.js.

Don’t believe me? Open your Angular Console app and browse to http://localhost:7777. Magic happens!

Wednesday, January 23, 2019

Git- Delete old branches

My list with local branches was going through the roof so it was time to do some cleanup work.

Let’s start by looking at all our local branches:

$ git branch

All our integrations happen on the “development” branch, so let’s have a look at all branches that are already merged to “development”:

$ git checkout development

$ git branch --merged

Now, we can go through the list and remove all outdated branches one by one using:

$ git branch –d branch-to-remove

Of course as a developer we’ll prefer to automate this. Here is the Powershell script I used:

git branch --merged | ?{-not ($_ -like "*development")} | %{git branch -d $_.trim()}

Tuesday, January 22, 2019

ElasticSearch - The refresh parameter

ElasticSearch is designed as an eventual consistent system, meaning that there is a time gap between the moment a document is indexed and when it becomes available in your search results. This is because ElasticSearch puts documents first in an in-memory buffer. At specified time intervals(by default every second), a refresh operation is triggered that copies the in-memory buffer content to a newly created segment, making the data available for search.

You can start playing around with the refresh interval or trigger the refresh operation manually but in general this is not a good idea.

A better alternative is to use the refresh parameter when inserting/updating your document. This parameter brings you into control when changes made by a request become visible for search.

You have 3 possible options:

  • false: this is the default value, changes to a document are only visible after the next refresh operation
  • true: this forces a refresh in the affected shards
  • wait_for: the request will wait until the next refresh operation completes

As a general recommendation, use false whenever possible, use true only for development purposes and wait_for if you really want to wait for the updated data.

Monday, January 21, 2019

GraphQL–Apollo Client Devtools

To simplify your GraphQL development workflow, a great extension to your toolbox is the Apollo Client Devtools Chrome extension.

GraphiQL Console

This Chrome extension offers the following features:

  • GraphiQL UI: Serves a GraphiQL UI to query your GraphQL endpoint(and local cache) through the Apollo network interface.
  • Queries inspector: View the queries being actively watched on any given page. See when they’re loading, what variables they’re using.
  • Mutations inspector: View executed mutations and variables.
  • Cache: View the state of your client-side cache as a tree and inspect every object inside. Visualize the mental model of the Apollo cache. Search for specific keys and values in the store and see the path to those keys highlighted.

Friday, January 18, 2019

Azure DevOps–Build task - NuGet pack issues

Today I had to configure a new build pipeline. One of the steps in this pipeline is the creation of our NuGet packages that our pushed to our internal NuGet store.

Before I always used the (depecrated) NuGet Packager task

As I was in an adventurous mood I decided to switch to the latest version. But no luck for me, my first build failed with the following error message:

2019-01-22T14:48:56.7624685Z ##[section]Starting: NuGet pack

2019-01-22T14:48:56.7644225Z ==============================================================================

2019-01-22T14:48:56.7644225Z Task         : NuGet

2019-01-22T14:48:56.7644225Z Description  : Restore, pack, or push NuGet packages, or run a NuGet command. Supports NuGet.org and authenticated feeds like Package Management and MyGet. Uses NuGet.exe and works with .NET Framework apps. For .NET Core and .NET Standard apps, use the .NET Core task.

2019-01-22T14:48:56.7644225Z Version      : 2.0.24

2019-01-22T14:48:56.7644225Z Author       : Microsoft Corporation

2019-01-22T14:48:56.7644225Z Help         : [More Information](https://go.microsoft.com/fwlink/?LinkID=613747)

2019-01-22T14:48:56.7644225Z ==============================================================================

2019-01-22T14:48:58.5015285Z Found tool in cache: NuGet 4.1.0 x64

2019-01-22T14:48:58.5044595Z Found tool in cache: NuGet 4.1.0 x64

2019-01-22T14:48:58.5562405Z Resolved from tool cache: 4.1.0

2019-01-22T14:48:58.5562405Z Using version: 4.1.0

2019-01-22T14:48:58.5572175Z Found tool in cache: NuGet 4.1.0 x64

2019-01-22T14:48:58.7428475Z [command]C:\Windows\system32\chcp.com 65001

2019-01-22T14:48:58.7428475Z Active code page: 65001

2019-01-22T14:49:19.3380075Z Attempting to pack file: D:\b\4\agent\_work\34\s\SOFA\NugetSpecs\Company.Library.csproj.nuspec

2019-01-22T14:49:19.3497315Z [command]D:\b\4\agent\_work\_tool\NuGet\4.1.0\x64\nuget.exe pack D:\b\4\agent\_work\34\s\SOFA\NugetSpecs\Company.Library.csproj.nuspec -NonInteractive -OutputDirectory D:\b\4\agent\_work\34\a -Properties "Configuration=release;-BasePath D:\b\4\agent\_work\34\a" -Verbosity Detailed

2019-01-22T14:49:21.3310875Z System.IO.FileNotFoundException: File not found: Company.Library.dll'.

2019-01-22T14:49:21.3310875Z NuGet Version: 4.1.0.2450

2019-01-22T14:49:21.3310875Z Attempting to build package from Company.Library.csproj.nuspec'.

2019-01-22T14:49:21.3320645Z    at NuGet.Packaging.PackageBuilder.AddFiles(String basePath, String source, String destination, String exclude)

2019-01-22T14:49:21.3730985Z ##[error]The nuget command failed with exit code(1) and error(System.IO.FileNotFoundException: File not found: 'Company.Library.dll'.

   at NuGet.Packaging.PackageBuilder.AddFiles(String basePath, String source, String destination, String exclude)

   at NuGet.Packaging.PackageBuilder.PopulateFiles(String basePath, IEnumerable`1 files)

   at NuGet.Packaging.PackageBuilder.ReadManifest(Stream stream, String basePath, Func`2 propertyProvider)

   at NuGet.Packaging.PackageBuilder..ctor(String path, String basePath, Func`2 propertyProvider, Boolean includeEmptyDirectories)

   at NuGet.Commands.PackCommandRunner.CreatePackageBuilderFromNuspec(String path)

   at NuGet.Commands.PackCommandRunner.BuildFromNuspec(String path)

   at NuGet.CommandLine.PackCommand.ExecuteCommand()

   at NuGet.CommandLine.Command.ExecuteCommandAsync()

   at NuGet.CommandLine.Command.Execute()

   at NuGet.CommandLine.Program.MainCore(String workingDirectory, String[] args))

2019-01-22T14:49:21.3740755Z ##[error]An error ocurred while trying to pack the files.

2019-01-22T14:49:21.3740755Z    at NuGet.Packaging.PackageBuilder.PopulateFiles(String basePath, IEnumerable`1 files)

2019-01-22T14:49:21.3740755Z    at NuGet.Packaging.PackageBuilder.ReadManifest(Stream stream, String basePath, Func`2 propertyProvider)

2019-01-22T14:49:21.3740755Z    at NuGet.Packaging.PackageBuilder..ctor(String path, String basePath, Func`2 propertyProvider, Boolean includeEmptyDirectories)

2019-01-22T14:49:21.3740755Z    at NuGet.Commands.PackCommandRunner.CreatePackageBuilderFromNuspec(String path)

2019-01-22T14:49:21.3740755Z    at NuGet.Commands.PackCommandRunner.BuildFromNuspec(String path)

2019-01-22T14:49:21.3740755Z    at NuGet.CommandLine.PackCommand.ExecuteCommand()

2019-01-22T14:49:21.3740755Z    at NuGet.CommandLine.Command.ExecuteCommandAsync()

2019-01-22T14:49:21.3750525Z    at NuGet.CommandLine.Command.Execute()

2019-01-22T14:49:21.3750525Z    at NuGet.CommandLine.Program.MainCore(String workingDirectory, String[] args)

2019-01-22T14:49:21.5362575Z ##[section]Async Command Start: Telemetry

2019-01-22T14:49:21.5362575Z ##[section]Async Command End: Telemetry

2019-01-22T14:49:21.5362575Z ##[section]Async Command Start: Telemetry

2019-01-22T14:49:21.5362575Z ##[section]Async Command End: Telemetry

2019-01-22T14:49:21.5362575Z ##[section]Finishing: NuGet pack

What is going wrong?

Problem is that the location of my NuGet spec files is not the same as the location of our built libraries. To solve this before I specified a BasePath using the NuGet Arguments field:

Unfortunately the option to specify extra NuGet Arguments seems to be gone in the new(er) NuGet Build task.

Anyone who knows a solution?(At the moment I reverted back to the old deprecated task).

Thursday, January 17, 2019

How to GraphQL? The Fullstack tutorial for GraphQL

In my journey to learn the ins and outs of GraphQL, I found the following great tutorial, starting with a short video introduction followed by a set of tutorials using specific technology stacks.



Tuesday, January 15, 2019

Angular–Karma - google is not defined

When trying to get our unit tests running using Karma, I got the following error message:

“google is not defined”

Inside our application we are calling the google map API. During the tests the google map library cannot be found resulting in the error message above.

To fix it, we have to add this library to the files section in our karma.conf.js file:

Monday, January 14, 2019

GraphQL Weekly

If you are into GraphQL, I can definitely recommend subscribing to GraphQL Weekly, a weekly newsletter of the best news, articles and projects about GraphQL.

image

Friday, January 11, 2019

ASP.NET Core IIS - Application failed to start process with commandline '%LAUNCHER_PATH% %LAUNCHER_ARGS%'

After deploying an ASP.NET Core application to IIS, it failed to start with the following error message:

Application 'MACHINE/WEBROOT/APPHOST/SampleApp/' with physical root 'C:\Sites\sampleapp\' failed to start process with commandline '%LAUNCHER_PATH% %LAUNCHER_ARGS%', ErrorCode = '0x80070002' : 0.

So what is going on?

By default when you add a web.config file to your ASP.NET core project, the following configuration is added:

You see the 2 environment variables'(%LAUNCHER_PATH%, %LAUNCHER_ARGS%) that are added to the config and also mentioned in the error message above. These 2 variables are their for Visual Studio and are replaced by Visual Studio when you try build and run your app.

For example when you do a debug build, the web.config is transformed to:

<aspNetCore processPath="C:\Program Files\dotnet\dotnet.exe" arguments="exec &quot;C:\projects\sampleapp\bin\Release\netcoreapp2.2\sampleapp.dll&quot;" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" />

MsBuild applies the same magic when you package and deploy your application. The problem was that we were using web.config transformations. These web.config transformation were executed after the ‘replace magic’ re-introducing these placeholder variables. This meant we ended up with these placeholders in our deployed application where IIS had no clue what to do with it.

More information here: https://github.com/vijayrkn/webconfigtransform/blob/master/README.md

Thursday, January 10, 2019

Entity Framework - The DbContext of type 'SampleContext' cannot be pooled because it does not have a single public constructor accepting a single parameter of type DbContextOptions.

I was looking at some ways to improve the performance of one of our applications. One of the things I wanted to try was to active DbContext pooling(https://bartwullems.blogspot.com/2018/03/entity-framework-core-20dbcontext.html).

Unfortunately after adding the following lines:

the application started to fail with this exception message:

System.InvalidOperationException: The DbContext of type 'TargetSUContext' cannot be pooled because it does not have a single public constructor accepting a single parameter of type DbContextOptions.

   at Microsoft.EntityFrameworkCore.Internal.DbContextPool`1..ctor(DbContextOptions options)

   at Microsoft.Extensions.DependencyInjection.EntityFrameworkServiceCollectionExtensions.<>c__5`2.<AddDbContextPool>b__5_1(IServiceProvider sp)

   at lambda_method(Closure , IBuildSession , IContext )

Let’s take a look at my DbContext and indeed I had multiple constructors:

I don’t understand why Entity Framework cannot handle this, but I removed the other constructors to make EF happy…

Wednesday, January 9, 2019

GraphQL–DotNet–Nullability

In GraphQL all types are nullable by default. In the first version of GraphQL non-null fields were even not supported at all(!).

There is a good reason for this; evolvability of your API. Non-nullable fields make it hard to change your contract. Switching from a non-nullable to a nullable field will always be a breaking change. This explains why GraphQL makes every type nullable by default, and you have to explicitly mark a field as non-null by adding the bang character (!) to your schema definition.

When you are using GraphQL.NET, the principles above still apply but the library follows some different rules to mark a field as nullable or not. For structs (integers, booleans, …) GraphQL.NET will mark them as not-null by default. You even have to explicitly mark them as nullable(see this post: https://bartwullems.blogspot.com/2018/11/graphqldotnetnullable-types-nullable.html). For all other types the rules above are followed and every type is nullable by default.

If you don’t want this, you have to wrap your GraphQL.NET type in a NonNullGraphType wrapper.

Here is a example for a single type:

This will generate the following schema:

And also an example for a list:

This will generate the following schema:

More information:

Tuesday, January 8, 2019

SQL Server Management Studio–Reports menu is disabled

When opening SQL Server Management Studio, I noticed that the Reports menu item was disabled:

image

I first thought it had to do something with my rights but I was serveradmin on this specific instance.

The problem wasn’t related to security but to the fact that I was using an older SQL Server Management Studio version (2012) to a newer SQL Server Database (2016). After installing SQL Server Management Studio 2016 (or later), the menu item was enabled.

Problem solved!

Monday, January 7, 2019

NPM–Install a package as a dev dependency

A quick reminder for myself on how to add a NPM package to your devDependencies.

npm install --save-dev <packagename>

devDependencies are modules which are only required during development, while dependencies are modules which are also required at runtime.

An example:

Friday, January 4, 2019

ASP.NET Core - OIDC middleware - IDX10500: Signature validation failed

Last Friday I had some fun investigating the following problem:

We have a frontend Angular application(FrontEnd) with a corresponding backend API(Backend1). This backend API calls another backend(Backend2). All communication is secured through a combination of  OIDC, oAuth and IdentityServer.

So what’s the problem; when the backend1 API calls the backend2 API the security handshake fails with the following error message:

"Bearer" was not authenticated. Failure message: "IDX10500: Signature validation failed. No security keys were provided to validate the signature."

Here are the steps I took to find and fix the issue:

Backend2 API

I started by taking a look at the Backend2 API logs but this brought no new information:

2019-01-04 08:40:35.377 +01:00 [Information] Starting up

2019-01-04 08:40:36.416 +01:00 [Information] "Bearer" was not authenticated. Failure message: "IDX10500: Signature validation failed. No security keys were provided to validate the signature."

Failed to validate the token "<removed>".

Microsoft.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException: IDX10501: Signature validation failed. Unable to match 'kid': '8d9d5c2a95ebf3ee923f8f7c4cc2a5a4',

token: '{"alg":"RS256","typ":"JWT","kid":"8d9d5c2a95ebf3ee923f8f7c4cc2a5a4"}.{"nbf":1546523279,"exp":1546609679,"iss":"https://identityserver","aud":["https://identityserver/resources","backend2api"],"client_id":"8fb9780a-eb1d-4ac1-ba43-dac70d08645d","jti":"07185c41f7b6f06523e85e32cab2a977","scope":["backend2api"]}'.

   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters)

   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)

   at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.<HandleAuthenticateAsync>d__6.MoveNext()

2019-01-03 14:48:03.806 +01:00 [Information] "Bearer" was not authenticated. Failure message: "IDX10501: Signature validation failed. Unable to match 'kid': '8d9d5c2a95ebf3ee923f8f7c4cc2a5a4',

token: '{\"alg\":\"RS256\",\"typ\":\"JWT\",\"kid\":\"8d9d5c2a95ebf3ee923f8f7c4cc2a5a4\"}.{\"nbf\":1546523279,\"exp\":1546609679,\"iss\":\"https://identityserver\",\"aud\":[\"https://identityserver/resources\",\"backend2api\"],\"client_id\":\"8fb9780a-eb1d-4ac1-ba43-dac70d08645d\",\"jti\":\"07185c41f7b6f06523e85e32cab2a977\",\"scope\":[\"backend2api\"]}'."

IdentityServer

Then I had a look at the Identity Server logs.

2019-01-04 09:11:14.169 +01:00 [DBG] Start token request.

2019-01-04 09:11:14.169 +01:00 [DBG] Start client validation

2019-01-04 09:11:14.169 +01:00 [DBG] Start parsing Basic Authentication secret

2019-01-04 09:11:14.170 +01:00 [DBG] Parser found secret: BasicAuthenticationSecretParser

2019-01-04 09:11:14.170 +01:00 [DBG] Secret id found: 8fb9780a-eb1d-4ac1-ba43-dac70d08645d

2019-01-04 09:11:14.181 +01:00 [DBG] client configuration validation for client 8fb9780a-eb1d-4ac1-ba43-dac70d08645d succeeded.

2019-01-04 09:11:14.181 +01:00 [DBG] Secret validator success: HashedSharedSecretValidator

2019-01-04 09:11:14.181 +01:00 [DBG] Client validation success

2019-01-04 09:11:14.181 +01:00 [DBG] Start token request validation

2019-01-04 09:11:14.181 +01:00 [DBG] Start client credentials token request validation

2019-01-04 09:11:14.199 +01:00 [DBG] 8fb9780a-eb1d-4ac1-ba43-dac70d08645d credentials token request validation success

2019-01-04 09:11:14.199 +01:00 [INF] Token request validation success

{

  "ClientId": "8fb9780a-eb1d-4ac1-ba43-dac70d08645d",

  "ClientName": "FrontendApp",

  "GrantType": "client_credentials",

  "Scopes": "backend2api",

  "Raw": {

    "grant_type": "client_credentials",

    "scope": "backend2api"

  }

}

2019-01-04 09:11:14.199 +01:00 [DBG] Getting claims for access token for client: 8fb9780a-eb1d-4ac1-ba43-dac70d08645d

2019-01-04 09:11:14.202 +01:00 [DBG] Token request success.

Here I noticed 2 things:

  1. The frontend app correctly called the IdentityServer and received the necessary tokens
  2. I couldn’t find a call from the backend2

It was the second item that lead me to the solution. What I would expect is that the backend2api calls IdentityServer to validate the token. However no call was done. So I opened up the backend API solution and had a look at the configuration.

I noticed that all IdentityServer related configuration was found in the AppSettings.development.json:

However inside the web.config the ASP.NET Development Environment wasn’t set correctly. This resulted in the fact that the settings were not loaded. As the backend2 api was not able to validate the token(due to missing configuration), it throwed the IDX10500: Signature validation failed error message.

A more specific error message would have been handy but we found the root cause. Pfew!

Thursday, January 3, 2019

Visual Studio 2017 - No projects supported by NuGet in the solution

I started my day with a rather fun error message in Visual Studio. Visual Studio failed in building my application. When I took a look at the error messages I noticed that Visual Studio failed to download all dependencies from NuGet.

However when I tried to open the Nuget Package Manager console, I got the following error message:

clip_image002

A magical restart did the trick… Confused smile

Wednesday, January 2, 2019

Team Foundation Server Version Control–Remove a lock

I thought that the first week of the new year would be a good time do some cleaning and decided to remove some old branches from Team Foundation Server Version Control. However deleting one of the branches failed with the following error message:

LockIssue

Another user had one of the files locked inside his (private) workspace which prevented me from deleting this branch. As I couldn’t contact this person, I had to find another solution.

Remove the lock

Removing a lock can be done using the tf undo command:

  • Open a Visual Studio command prompt
  • Enter the following command
    • tf undo /workspace:FRIEND-PC;personname  /collection:http://servername/DefaultCollection $/xxxx/ProjectFolder/ProjectName/filepath/filename.css

Here is an example:

C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise>tf undo /workspace:"Framework en Tooling 169;Bart Wullems" /collection:http://tfs.server.be:8080/tfs/defaultcollection "$/Framework en Tooling/Main/IAM/packages/Antlr.3.5.0.2/lib/Antlr3.Runtime.dll"

The operation completed successfully.  Because the workspace Framework en Tooling 169;Bart Wullems is not on this computer, you must perform a separate get operation in that workspace to update it with the changes that have been made on the server.

Delete the workspace

An alternative solution would be to delete the whole workspace using the tf workspace command:

  • Open a Visual Studio command prompt
  • Enter the following command
    • tf workspace /delete FRIEND-PC;personname

Here is an example:

tf workspace /delete "Framework en Tooling 169;Bart Wullems" /collection:http://tfs.server.be:8080/tfs/defaultcollection

A deleted workspace cannot be recovered.

Workspace 'Framework en Tooling 169;Bart Wullems on server 'http://tfs.server.be:8080/tfs/defaultcollection' has 752 pending change(s).

Are you sure you want to delete the workspace? (Yes/No) Y

Tuesday, January 1, 2019

IIS–Keep your application running

A few years ago I blogged about a way to keep your web application running in IIS. To summarize the blog posts, IIS 7.5 introduced the Application Initialization Module that allowed you to start a worker process without waiting for a request.

To activate this feature you have to dive into the applicationhost.config file directly and change the startMode of your application pool to alwaysRunning:

<system.applicationHost> 
<applicationPools>
<add name="DefaultAppPool" autoStart="true" startMode="alwaysRunning" />
</applicationPools>
</system.applicationHost>
This will guarantee that the moment your application pool is started, a worker process spins ups decreasing the time to handle the first request. 
Unfortunately the blog posts wasn’t complete. Although the steps above guarantee that a worker process is spinned up immediately, it doesn’t mean that the application is loaded into the worker process. 

The Application Initialization module provides a solution here by adding a new property to the application settings called preloadEnabled. We’ll have to dive into the applicationhost.config file again to set this value:

<system.applicationHost> 
<sites>
<site name="Default Web Site" id="1">
<application path="/">
<virtualDirectory path="/" physicalPath="%SystemDrive%\inetpub\wwwroot" />
</application>
<application name="WebApp1" applicationPool="DefaultAppPool" preloadEnabled="true">
<virtualDirectory path="/WebApp1" physicalPath="c:\inetpub\wwwroot\WebApp1" />
</application>
</site>
</sites>
</system.applicationHost>
This will guarantee that our application is always up and running.

What is happening behind the scenes?

From https://blogs.iis.net/wadeh/application-initialization-part-2:

Here's how Application Initialization uses this property.  When a new worker process spins up, Application Initialization will enumerate all of the applications that it will host and checks for this property.  For any application where preloadEnabled="true", it will build a URL corresponding to the default page for the application and run it through the pipeline.  This request does not go through the network, and there is no client listening for a response (IIS discards any data that would have gone to the client.)

This "fake" request accomplishes a few key things.  First, it goes through the IIS pipeline and kicks off an application start event.  This initializes a number of parts inside of IIS, and if the request is for ASP.NET, it will cause global.asax to run.  It also reaches the application, which will see it is the first request after starting.