Friday, February 26, 2021

NUnit–SetUp and TearDown methods

NUnit allows you to bootstrap individual tests through the SetUp and TearDown attributes. I typically try to keep my test classes clean and one way to do this is by moving logic to a test base class.

NUnit will walk through the inheritance tree and call all Setup and TearDown methods. Setup methods (both types) are called on base classes first, then on derived classes. If any setup method throws an exception, no further setups are called.

Something I noticed in the documentation:

Teardown methods (again, both types) are called on derived classes first, then on the base class. The teardown methods at any level in the inheritance hierarchy will be called only if a setup method at the same level was called. The following example is illustrates the difference.

So if you have a test structure like below, only the base SetUp and TearDown are called:

This is a breaking change from NUnit 2.x and something to be aware of…

Thursday, February 25, 2021

MassTransit–How to test consumers with dependencies

I’m using Dependency Injection in my MassTransit consumers. To test these consumers you can use MassTransit in-memory test harness;

But when you try to use the code above with a consumer that has dependencies injected through the constructor, it will not work and you end up with the following error message:

'SubmitOrderConsumer' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T' in the generic type or method 'ConsumerTestHarnessExtensions.Consumer<T>(BusTestHarness, string)'

To fix this, you have to change your test code and use the consumer factory method:

Wednesday, February 24, 2021

Elastic App Search - JSON must be an array or hash

I’m a big fan of ElasticSearch. One of the products in the Elastic suite is App Search.

I tried to get some documents through the App Search API using the following code:

Unfortunately this didn’t work but resulted in the following error message:

JSON must be an array or hash

The reason I got this exception is because the API is expecting a JSON array or object for the id parameter.

Unstructured JSON data is not permitted. You have to encapsulate your object in an array [] or a hash {} (what the error message is referring to).

If you are using query parameters, ensure that ids is followed with %5D%5B - this is an array, but escaped: ?ids%5D%5B=:

Tuesday, February 23, 2021

NuGet - Change the location of the global package cache

One of the improvements that were introduced to NuGet a few years ago was the introduction of a global package cache. Instead of having a copy of every nuget package inside your project folder, all nuget packages are downloaded and stored once on a central location on your file system. Each package is fully expanded into a subfolder that matches the package identifier and version number.

By default these packages are stored at the following location:

  • Windows: %userprofile%\.nuget\packages
  • Mac/Linux: ~/.nuget/packages

You can override this by setting the NUGET_PACKAGES environment variable, the globalPackagesFolder or repositoryPath configuration settings (when using PackageReference and packages.config, respectively), or the RestorePackagesPath MSBuild property (MSBuild only).

Remark: The environment variable takes precedence over the configuration setting.

Monday, February 22, 2021

Dapr–Ebook

With the v1 release of Dapr, it is time to investigate it and evaluate how it can help simplify your cloud-native application development.

A good introduction is the following free e-book: Dapr for .NET Developers

Thursday, February 18, 2021

.NET Core HttpClient - Uploading multiple files through a multipart/form-data POST

 Yesterday I blogged about uploading files through Postman. Let's now move on to the real application and implement the client API to upload the files using the .NET Core HttpClient.

The code is not that hard but I'm sharing it here just a reminder for myself:


Wednesday, February 17, 2021

Postman - Uploading multiple files through a multipart/form-data POST

 I had to test an ASP.NET Core API that was expecting multiple files through a multipart-formdata request.

Here are the steps I had to take to get this done through Postman:

  • Open Postman and click on the '+' sign to create a new request:

  • Change the request type to 'POST' and enter the request URL:

  • Click on the 'body' section and change the type to 'form-data':

  • Enter a key name for the files you want to upload(in my case it was 'files'):
  • Now comes the important part, go to the right border of the Key column. A dropdown appears where you can change the type from 'text' to 'file':
  • Now the Values column changes and you get a 'Select Files' button that allows you to specify the files you want to upload:

That should do the trick...

Tuesday, February 16, 2021

.NET Reflection - The invoked member is not supported in a dynamic assembly.

In the bootstrapping logic of my application I was using some reflection to dynamically register dependencies in the IoC container:

When I was using the same code together with the .NET Core Test server the code above didn't work but resulted in the following exception:

Message: System.NotSupportedException : The invoked member is not supported in a dynamic assembly.

Stack Trace: 

    InternalAssemblyBuilder.GetExportedTypes()

    Assembly.get_ExportedTypes()

    <>c.<ScanForModules>b__5_0(Assembly a)

    SelectManySingleSelectorIterator`2.MoveNext()

    List`1.InsertRange(Int32 index, IEnumerable`1 collection)

    FrameworkBuilder.ScanForModules()

    <>c__DisplayClass0_0.<UseSOFACore>b__1(HostBuilderContext ctx, ContainerBuilder builder)

    ConfigureContainerAdapter`1.ConfigureContainer(HostBuilderContext hostContext, Object containerBuilder)

    HostBuilder.CreateServiceProvider()

    HostBuilder.Build()

    WebApplicationFactory`1.CreateHost(IHostBuilder builder)

    WebApplicationFactory`1.EnsureServer()

    WebApplicationFactory`1.CreateDefaultClient(DelegatingHandler[] handlers)

    WebApplicationFactory`1.CreateDefaultClient(Uri baseAddress, DelegatingHandler[] handlers)

    WebApplicationFactory`1.CreateClient(WebApplicationFactoryClientOptions options)

    WebApplicationFactory`1.CreateClient()

    ConversionApiTests.ctor(SelfHostedApi fixture) line 19

This is because an Assembly is generated dynamically during the tests and the "GetExportedTypes()" is not supported on these assemblies. 

To fix the error I updated the code to remove dynamic assemblies from the iteration:

Monday, February 15, 2021

Azure DevOps - My Pull Requests

I was always annoyed that I had to go through all my Azure DevOps project to find out what Pull Requests I had to review. Before this wasn't such a big issue as I was typically working on a few Azure DevOps projects at max. But since we switched to a Microservices approach where every service had its own Git repo, it started to annoy me... 

Until a colleague sent me a screenshot where he had all pull requests on one screen???? 

If you go to the root url of your Azure DevOps organisation(https://dev.azure.com/{yourorganizationname}), you have a 'My Pull Requests tab' at your disposal:

This feature existed for a long time but I never noticed it...😏

Friday, February 12, 2021

System.UnauthorizedAccessException - MemoryStream's internal buffer cannot be accessed

 When trying to read a MemoryStream I got the following error message:

Here is the code I was using:

Couldn't be any simpler but unfortunately it didn't work. 

I couldn't figure out what I did wrong...until I noticed the following in the documentation:

This constructor does not expose the underlying stream. GetBuffer throws UnauthorizedAccessException.

Aha, it seems that the OpenXmlPowerToolsDocument is calling GetBuffer behind the scenes. So we need to use a different constructor:

 

Thursday, February 11, 2021

ASP.NET Core Integration Tests - Breaking change in .NET Core 3.0

 Yesterday I blogged about how to get started with integration testing in ASP.NET Core.

If you are using this technique, you might get a few issues while migrating from .NET Core 2.2 to .NET Core 3.0 or 3.1.

It could be that you get the following error message:

No method 'public static IHostBuilder CreateHostBuilder(string[] args)' or 'public static IWebHostBuilder CreateWebHostBuilder(string[] args)' found on 'AutoGeneratedProgram'. Alternatively, WebApplicationFactory`1 can be extended and 'CreateHostBuilder' or 'CreateWebHostBuilder' can be overridden to provide your own instance.

This is due to the switch from the WebHostBuilder to the more generic HostBuilder in .NET Core 3.x. 

You should use the CreateHostBuilder() method. It is still possible to access the WebHostBuilder through the ConfigureWebHostDefault() method:

Wednesday, February 10, 2021

ASP.NET Core Integration testing

 Integration testing your ASP.NET Core API's shouldn't be too difficult thanks to the Microsoft.AspNetCore.Mvc.Testing package.

First add the package to your project:

dotnet add package Microsoft.AspNetCore.Mvc.Testing

Now you can inject the WebApplicationFactory<TEntryPoint> in your tests:


You can tweak the configuration by inheriting from the WebApplicationFactory and use one of the overrides:

Tuesday, February 9, 2021

Saying goodbye to the Monolith - The one Entity abstraction

 In most systems I encounter there is a strong focus on "Entities". We try to model everything in this "Entity" abstraction where one "Entity" is typically mapped to one table.

Every piece of data that is related to this "Entity" is encapsulated in this object. This could sound fine from a OO perspective but can bring us on a path where we get too much coupling and information from multiple domains get encapsulated in the same object.

A first step in the right direction is to focus on the difference between related entities and child entities. Could some parts of the data move to a child entity or even a value object?

A second step is to identity your bounded contexts and start to split your entity across the boundaries of these contexts. This means you will typically not end up with one entity but many entities each containing a subset of the data.

Let's take a look at an example...

Imagine that we have a Product entity.

If we try to split this across the context boundaries, we could end up with something like this:

This not only allows us to encapsulate the logic a lot better, it gives use a more evolvable system where changes in one context don't impact another.

Monday, February 8, 2021

GraphQL Hot Chocolate - Entity Framework error: A second operation was started on this context before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext

 If you try to combine HotChocolate GrapQL implementation together with Entity Framework, a possible attempt could look like this:

Unfortunately this doesn't work the moment multiple resolvers kick in. When you try to resolve multiple queries/fields, chances are you end up with the following exception message:

"message": "A second operation was started on this context before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.",

        "stackTrace": "   at Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection()\r\n   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()\r\n   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)\r\n   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)\r\n   at Bridges.Sectors.Infrastructure.Repositories.SectorRepository.GetSectorById(Guid sectorId) in C:\\Users\\StVa\\source\\repos\\bridges\\Sector\\Bridges.Sectors.Infrastructure\\Repositories\\SectorRepository.cs:line 46\r\n   at Bridges.Sectors.API.Services.SectorService.GetSector(Guid sectorId) in C:\\Users\\StVa\\source\\repos\\bridges\\Sector\\Bridges.Sectors.API\\Services\\SectorService.cs:line 26\r\n   at Bridges.Sectors.API.Schema.Queries.SectorQuery.GetSector(ISectorService sectorService, Guid sectorId) in C:\\Users\\StVa\\source\\repos\\bridges\\Sector\\Bridges.Sectors.API\\Schema\\Queries\\SectorQuery.cs:line 27\r\n   at HotChocolate.Resolvers.Expressions.ExpressionHelper.AwaitTaskHelper[T](Task`1 task)\r\n   at HotChocolate.Types.FieldMiddlewareCompiler.<>c__DisplayClass3_0.<<CreateResolverMiddleware>b__0>d.MoveNext()\r\n--- End of stack trace from previous location ---\r\n   at HotChocolate.Execution.Processing.ResolverTask.ExecuteResolverPipelineAsync(CancellationToken cancellationToken)\r\n   at HotChocolate.Execution.Processing.ResolverTask.TryExecuteAsync(CancellationToken cancellationToken)",

This is because HotChocolate executes multiple resolvers in parallel and the DbContext is inherently not threadsafe. More about this here.

You could try to go for a single threaded execution model but this would end up being a fight against GraphQL.

Luckily the creators of HotChocolate got your back and created a HotChocolate.Data.EntityFramework nuget package. This package allows you to create a pool of DbContexts and use these in your resolvers.

First register a DbContext pool:

And then we can update our code to use a DbContext instance from this pool:

More information about this feature here:https://chillicream.com/docs/hotchocolate/integrations/entity-framework 

Friday, February 5, 2021

ASP.NET Core - Async action names

 I got into trouble when I created the following controller action in my ASP.NET Core application:

This didn't work as expected but returned a 500 error stating:

No route matches the supplied values.

 The strange thing was that when I removed the 'async' suffix in the Action name it worked.

This turned out to be a breaking change in ASP.NET Core 3.0. Starting from ASP.NET Core 3.0, ASP.NET Core MVC removes the Async suffix from controller action names. Both routing and link generation are impacted by this new default.

If you want to move back to the old behaviour, you can change it in the configuration:

Thursday, February 4, 2021

XUnit - Assert.Inconclusive() alternative

 I got a question from a colleague who was used to MSTest and now was switching to XUnit.

For tests that were not implemented yet, he was using the Assert.Inconclusive method in MSTest but he couldn't find a similar Assert method for XUnit.

I typically use the 'Skip' property for this in XUnit:

Wednesday, February 3, 2021

TypeScript - Resolve JSON as a Javascript Module

 I discovered something neat that is possible in TypeScript. Did you know that you can import JSON as a Javascript module? I didn't...

I had a configuration file like this:

In my tsconfig.json I had to set the resolveJsonModule to true:

Now I could import the config file as a module. The nice thing is that I even get type checking and autocompletion!

Remark: Not enabling this option out of the box was done by the TypeScript team to avoid pulling in large JSON files that would consume a lot of memory.

Tuesday, February 2, 2021

Azure DevOps - Delete an existing collection

 After a failed Azure DevOps migration, I ended up with an unusable collection on our Azure DevOps server. 

I had a look at the Azure DevOps administration console but couldn't find a way to delete the collection.

Luckily there is still the command line; I was able to do it through the tfsconfig tool:

D:\Program Files\Azure DevOps Server 2020\Tools>tfsconfig collection /delete /collectionName:FailedCollection 

Logging sent to file C:\ProgramData\Microsoft\Azure DevOps\Server Configuration\Logs\CFG_TPC_AT_0129_130947.log 

TfsConfig - Azure DevOps Server Configuration Tool Copyright (c) Microsoft Corporation. All rights reserved. 

Command:collection 

TfsConfig -Azure DevOps Server Configuration Tool Copyright (c) Microsoft Corporation. All rights reserved. 

Deleting a Team Project Collection is an irreversible operation. A deleted collection can not be reattached to the same or another Azure DevOps Server. Are you sure you want to delete 'FailedCollection'? (Yes/No) Y

Monday, February 1, 2021

Azure DevOps Server–Move collections–Offline detach

One of the basic building blocks of Azure DevOps Server is a “Project collection”.

The documentation describes it like this:

The primary organizational unit for all data in Azure DevOps Server. Collections determine the resources available to the projects added to them. 

From a technical standpoint, a project collection is a separate database. It is possible to move project collection between different database instances but be aware that just backing up a collection on one database server and restoring it on another database server is not sufficient.  The well documented way is through a detach/attach operation as I explained last week.

But another ‘hidden’ option exists through an “Offline detach”.

Backup the collection and configuration database

Take a backup of the collection database that you want to move using a tool of your choice and restore it on the database server used by your target Azure DevOps server. Also take a backup of the configuration database and restore it as well(using a different name to avoid overriding the real configuration database).

Convert the database to an attachable collection

The trick is to convert the database to an attachable collection by copying over the configuration data from the configuration database to the collection database.

TfsConfig offlineDetach /configurationDB:DevOpsSQL;Tfs_Configuration /collectionDB:DevOpsSQL;Tfs_ExampleCollection

Attach the collection

Now you can to attach the collection again as with the normal procedure. So back to the Azure DevOps Administration console:

Remark:

Although I tested the approach above and it worked, it isn’t recommended. The collection and configuration database should be perfectly in sync. So preferably only do this when your Azure DevOps instance is offline.