Yearly Archives: 2019

…and the number of the counting shall be 3

.NET Core 3.0 is here, allegedly the penultimate stop on the roadmap before the Singularity, when they finally bin .NET Framework and unify on top of Windows XP… er… .NET 5. The news are packed with stuff about WinForms, WPF and other legacy technologies, but I’m going to stick with the webby and consoley bits, where I’ve been mostly operating since I started using .NET Core back in 1.0 days.

Scope

As usual I will mostly just write down gotchas I have come across so that if I come across it again I will have a greater chance of not wasting so much time the second time around.

We are starting from a .NET Core 2.2 app that initially was .NET Core 2.1, so it may not have been fully upgraded in all respects between 2.1 and 2.2 if there were changes I couldn’t be bothered implementing.

Breaking changes

I followed an excellent guide to get started with references that need to leave your project file and other that need to come back in after they were exiled from the magic default Microsoft.AspNetCore.App assembly, as well as other breaking changes. It’s not that bad, and you really will enjoy the experience.

There has long been a trend among hipsters to forego the unstructured default folders in ASP.NET projects that buckets Controllers, Views and Models into separate folders, in favour of instead having two folders in the root, one called Features and another called Infrastructure. The Features folder would contain – you guessed it – each feature, with controllers, views, viewmodels and the data model grouped together. To make this work, there was the necessity of creating a new Convention for adding controllers with Feature, so that the view resolver would know where to look for the Views. Since AddMvc is now called AddControllersWithViews, I made that change hoping to make things look happy again. I noticed that the FeatureConvention had a squiggly. This is because the old interface IPageConvention that the FeatureConvention used to implement no longer existed. What to do? Well I looked around all over the internet and I found nothing, so finally I discovered the IControllerModelConvention interface, and by literally just replacing the name of the interface , everything just compiled, so the interfaces were identical.

services.AddControllersWithViews(options =>
            {
                var policy = new AuthorizationPolicyBuilder()
                    .RequireAuthenticatedUser()
                    .Build();
                options.Conventions.Add(new FeatureConvention());
                options.Filters.Add(new AuthorizeFilter(policy));
            })

Controller Actions

So with .NET Core 2.2 the ActionResult<T> type came into being, but now you are starting to see squigglies around IActionResult, saying the bell tolls for untyped responses. This is not such a big deal. Often this means you get to cut away vast swathes of boilerplate where you respond with Ok() or Json() around something, instead just returning what the handler created, replacing Task<IActionResult> with Task<ActionResult<SomeExcellentDto>>. Not only have you now achieved a lot of automagic swaggering where you otherwise would have had to write attributes manually to inform the consumer what the payload looks like, you have also eliminated the need for a class of tests just ensuring that the action methods return data of the right kind.

The thing to look out for is that if your handler returns an IEnumerable<T>, due to the way ActionResult<T> works, you need to cast the enumerable to an array or a list, because otherwise the type cannot be instantiated, i e instead of Task<ActionResult<IEnumerable<SomeExcellentDto>>> you need to go with Task<ActionResult<SomeExcellentDto[]>> or Task<ActionResult<List<SomeExcellentDto>>>

(Swash)buckle up, buttercup

The swaggering is a separate chapter – the latest versions of Swashbuckle are hard integrated with OpenAPI, so you have to replace any filters you may have created to format your swagger document since all the APIs are broken and replaced with similar ones from OpenAPI, and if you do any swaggering at all you have to get the latest prerelease of Swashbuckle to even be able to compile. Basically – if you are using Swashbuckle today – congratulations, you are about to start swearing.

IdentityModel crisis

The IdentityModel nuget package has been updated quite radically between versions, and if you were doing clever things in message handlers to keep track of or request tokens from the token endpoint when talking between services, you may need to update your code to do without the TokenClient that bereft of life has ceased to be.

The new method is to use extension methods on to HttpClient, and the canonical example is providing a typed HttpClient. created on demand by the HttpClientFactory, that in turn calls the extension methods. See documentation here. The token endpoint and the extension methods on HttpClient are covered in more detail here.

Show me the money

Payment gateways for small businesses. What do you look for in them? Personally I don’t care – I crave simple pleasures.

I would prefer if a checkout page looks like the rest of the site, but I need for it to be not-obviously-insecure and be compliant with current regs but also it must be less work to implement than the rest of the site was to create. If taking card payments was my core business, I would be in that business. I would prefer to sacrifice a significant chunk of revenue for this usability bonus.

The checklist

I would like a payment API

  1. To which I can connect with, ideally a .NET Core client, but pure HTTPS is fine.
  2. Where I can specify what my customer is buying
  3. I get to know who bought from me (email is all I need)
  4. Where I can indicate how much they will be charged (so that I can do discounts) – in fairness this is only sometimes missing
  5. That understands the concept of VAT and can just handle it for me. In the EU, VAT now in some cases has to be declared in the customer’s country. This is the type of faff a Stripe, Paypal et al should handle for me.
  6. Deal with 3D Secure automagically.
  7. Deal with PSD2/SCA automagically

Reality

Dodgy simile

Proper guitar amplifiers have a spectrum of volume*. Your exact volume knob indicators may vary, but the segments are universal.

Volume 1 – 4

practically silent,

4-4.8

Audible,

4.9

Decent volume, speakers are operating at reasonable dynamics, you can play. It’s just a bit quiet.

5.0 – 11.0

Massive noise complaints, police arrive.

Cards

From what I can tell, payment gateways operate similarly.

Level 1

You just need a button, and money might appear on your account. Never you mind who paid you for what.

Level 2

You can get to know who paid for what, but you’re SOL on VAT and have to do discounts manually like some schmuck. And webhooks. MOAR webhooks FTW.

Level 3

First you must create the Universe, then you must do 3DSecure manually and do three API calls to just begin to set up the first thing that might eventually become a card transaction.

Resolution

There is none that I can see. Am open to suggestions.

* I am aware attenuators solve this problem, but play along please.

Logging

I have had the misfortune of delving into logging a lot lately. To save time for next time I will write down the findings here.

My goals are simple. A couple of sites and APIs log into the same log aggregator, could be loggly, seq or graylog for instance. Given that I supply a correlation ID, I want to be able to tag all log entries related to one user as it travels through the system. This isn’t even on the bare minimum Charity Majors event logging, this is just glorified text but searchable with fields.

As of the date today, I want to be clear that for .NET, Serilog is best. Log4net is out since a long time ago, NLog tried but cannot explain how to do structured logging, so will have to be excused. Serilog has a more pleasant interface t and although I have struggled in the past to get the log context to enrich properly and had to resort to the Microsoft log abstraction combined with Serilog.AspNetCore to succeed and had problems getting the loggly sink working at all since docs skipped the need for the loggly-csharp nuget package. Still, it keeps winning, on old .NET Framework as well as .NET Core.

Setting up the Correlation ID has two parts. The first part is a piece of middleware in the request pipeline that wraps the call to the next stage in the pipeline in a using() statement. Here you extract the correlation ID from the caller or supply a suitable unique default for this call.

Then you create a message handler for setting a correlation ID on the outgoing HttpClient call. You can use the IHttpContextAccessor to get the incoming CorrelationId or the same default as earlier and map the message handler to any HttpClients you have defined in the projects.