Alexander Beletsky's development blog

My profession is engineering

NancyFX Migration from 0.9 to 0.10

Recently, I’ve upgraded IdeaStrike project from NancyFX 0.9 to the latest 0.10 version. There were some very unclear moments, fortunately due to great support of @thecodejunkie and @grumpydev they are solved. As a result, the issue 520 was born, that contains a lot of text inside. I’ll try to sum up all important things into one blog post.

Running update

With NuGet updating dependencies is very easy, just run in package manager console:

    PM> update-package

It will give you the information about the upgrade process, it should be very smooth, so the end you will see:

Successfully uninstalled 'Nancy.Hosting.Aspnet 0.9.0'.
Updating 'Nancy.Testing' from version '0.9.0' to '0.10.0' in project 'IdeaStrike.Tests'.
Successfully removed 'Nancy.Testing 0.9.0' from IdeaStrike.Tests.
Successfully removed 'Nancy 0.9.0' from IdeaStrike.Tests.
Successfully added 'Nancy 0.10.0' to IdeaStrike.Tests.
Successfully installed 'Nancy.Testing 0.10.0'.
Successfully added 'Nancy.Testing 0.10.0' to IdeaStrike.Tests.
Successfully uninstalled 'Nancy.Testing 0.9.0'.
The directory is not empty.

Successfully uninstalled 'Nancy 0.9.0'.

Fixing compilation errors

As you try to build the project after upgrade, it would fail because of compilation issues.

If you using Nancy bootstrapper and overriding RequestStratup method, you will see that method had changed the signature:

    // in 0.9
    protected override void RequestStartup(ILifetimeScope container, IPipelines pipelines);

    // in 0.10
    protected override void RequestStartup(ILifetimeScope container, IPipelines pipelines, NancyContext context)

So, it now receives additional parameter - NancyContext.

Next compilation errors is inside the Request.Headers.AcceptLanguage.

    // in 0.9
    public IEnumerable<string> AcceptLanguage { get; }

    // in 0.10
    public IEnumerable<Tuple<string, decimal>> AcceptLanguage { get; }

Both of these compilation errors are really easy to fix. See changes to src/Ideastrike.Nancy/IdeastrikeBootstrapper.cs in this commit.

After that, the compilation will be fine. I had to restart my Visual Studio after, since NCrunch was failing to compile the application.

Fixing failing unit tests

After VS is restarted and NCrunch able to pick up changes, I got 3 unit tests failures. All of them with the similar exception, like:

    System.Exception: System.Exception : ConfigurableBootstrapper Exception
    ---- Nancy.RequestExecutionException : Oh noes!
    -------- Nancy.ViewEngines.ViewNotFoundException : Unable to locate view '404'. Currently available view engine extensions: sshtml,html,htm,cshtml,vbhtml
    at Nancy.Testing.PassThroughErrorHandler.Handle(HttpStatusCode statusCode, NancyContext context)
    at Nancy.NancyEngine.CheckErrorHandler(NancyContext context)
    at Nancy.NancyEngine.HandleRequest(Request request)
    at Nancy.Testing.Browser.HandleRequest(String method, String path, Action1 browserContext)
    at Nancy.Testing.Browser.Get(String path, Action1 browserContext)
    at IdeaStrike.Tests.IdeaStrikeSpecBase1.Get(String path, Action1 browserContext) in D:\Development\Projects\Ideastrike\tests\IdeaStrike.Tests\IdeaStrikeSpecBase.cs:line 85

That was a mystery. All of those tests were fine, but suddenly stopped to work in 0.10.

The reason was very interesting. Those tests for views were actually never run. It’s been unseen for 2 reasons: Nancy 0.9 failed silently about missing view, IdeaStrike unit tests never tested view content.

To make those run, the IdeaStrikeSpecBase have to be setup with IRootPathProvider. IRootPathProvider, provides the path root for modules, so views could be located, based on default conventions. I finished up with this implementation:

public class CustomRootPathProvider : IRootPathProvider
{
    public string GetRootPath()
    {
        return Path.GetDirectoryName(typeof(IdeastrikeBootstrapper).Assembly.Location);
    }
}

This root provider is used with spec base class configuration:

public IdeaStrikeSpecBase()
{
 Bootstrapper = new ConfigurableBootstrapper(with =>
 {
  with.Module<TModule>();
  with.Dependencies(_Users.Object, _Ideas.Object, _Features.Object, _Activity.Object, _Settings.Object, _Images.Object);
  with.DisableAutoRegistration();
  with.NancyEngine<NancyEngine>();
  with.RootPathProvider<CustomRootPathProvider>(); // <- Here
 });
}

So, we are saying that root is the same folder as IdeaStike assembly. The problem is that location depends on actual Test runner. In case of NCrunch, it would be some deeply hidden folder in user\AppData\Local\Temp, in case of ReSharper runner it would be temp ASP.NET folder. That’s a little annoying.

Tests were still red, since neither NCrush nor ReSharper is copying actual views into target folder. Fortunately, I found this great article, explaining NancyFX view testing gotchas. I ended up with just saying “Copy if newer” for views under test, so they are in the same folder as target binary. That’s not cool, but currently I see no other option.

After those changes, tests became green, so I run the application. No surprise, it failed to start.

Fixing runtime errors

At the first run, I got nothing more as bunch of Razor compilation errors:

    Error Compiling Template: (51, 18) The name 'Url' does not exist in the current context)
    Error Compiling Template: (61, 33) The name 'Model' does not exist in the current context)
    Error Compiling Template: (68, 26) The name 'Model' does not exist in the current context)
    Error Compiling Template: (78, 7) The name 'Model' does not exist in the current context)
    Error Compiling Template: (83, 31) The name 'Model' does not exist in the current context)
    Error Compiling Template: (88, 14) The name 'Model' does not exist in the current context)
    Error Compiling Template: (103, 14) The name 'Model' does not exist in the current context)
    Error Compiling Template: (118, 14) The name 'Model' does not exist in the current context)
    Error Compiling Template: (132, 25) The name 'Model' does not exist in the current context)
    Error Compiling Template: (234, 19) The name 'Url' does not exist in the current context)

The proposed solution that NancyRazorViewBase should always be specified with generic parameter.

    // in 0.9
    @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase

    // in 0.10
    @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<dynamic>
     

OK, that helped a little, but right after that error saying System.Web.IHtmlString type is not references:

    Error Compiling Template: (83, 1) The type 'System.Web.IHtmlString' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.)

NancyFX is being developed to be independent of System.Web. It was not totally true for 0.9 with Razor support, but it has been fixed in 0.10. That of cause, brings some surprises. So, if your application is using Razor view engine, you need to do following:

  • update all usages of IHtmlString to use Nancy.ViewEngines.Razor.IHtmlString
  • update all usages of McvHtmlString to use Nancy.ViewEngines.Razor.NonEncodedHtmlString
  • let the razor engine know about system.web (in case if HttpContext is used in layout)
<razor disableAutoIncludeModelNamespace="false">
    <assemblies>
        <add assembly="System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
    </assemblies>
    <namespaces>
        <add namespace="System.Web" />
    </namespaces>
</razor>

Changing the Partial views

After these steps were done, application finally started. But some page gave runtime exception:

Server Error in '/' Application.

Sequence contains no elements

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.InvalidOperationException: Sequence contains no elements

My dump analisys showed, that it works fine as soon as I removing partial views:

    @Html.Partial("Shared/Templates/upload.html")
    @Html.Partial("Shared/Templates/download.html")

The reason turned out to be really simple. All templates in IdeaStrike project were using .html extension. For 0.10 it required to be .cshtml. So, renaming from .html to .cshtml made the IdeaStrike up and running again.