OdeToCode IC Logo

Moving APIs to .NET Core

Monday, August 13, 2018

I’ve been porting a service from .NET to .NET Core. Part of the work is re-writing the Azure Service Bus code for .NET Core. The original Service Bus API lives in the NuGet package WindowsAzure.ServiceBus, but that package needs the full .NET framework.

The newer .NET Standard package is Microsoft.Azure.ServiceBus.

The idea of this post is to look at the changes in the APIs with a critical eye. There are a few things we can learn about the new world of .NET Core.

Statics Are Frowned Upon

The old way to construct a QueueClient was to use a static method on the QueueClient class itself.

var connectionString = "Endpoint://..";
var client = QueueClient.CreateFromConnectionString(connectionString);

The new style uses new with a constructor.

var connectionString = "Endpoint://..";
var client = new QueueClient(connectionString, "[QueueName]");

ASPNET core, with its service provider and dependency injection built-in, avoids APIs that use static types and static members. There is no more HttpContext.Current, for example.

Avoiding statics is good, but I’ll make an exception for using static methods instead of constructors in some situations.

When a type like QueueClient has several overloads for the constructor, each for a different purpose, the overloads become disjointed and confusing. Constructors are nameless methods, while static factory methods provide a name and a context for how an object comes to life. In other words, QueueClient.CreateFromConnectionString is easier to read and easier to find compared to examining parameters in the various overloads for the QueueClient constructor.

The New World is async Only

The old API offered both synchronous and asynchronous operations for sending and receiving messages. The new API is async only, which is perfectly acceptable in today's world where even the Main method can be async.

Binary Serialization is Still Tricky

The old queue client offered a BrokeredMessage type to encapsulate messages on the bus.

var message = new BrokeredMessage("Hello!");

Behind the scenes, BrokeredMessage would use a DataContractBinarySerializer to convert the payload into bytes. Originally there were no plans to offer any type of binary serialization in .NET Core. While binary serialization can offer benefits for type fidelity and performance, binary serialization also comes with compatibility headaches and attack vectors.

Although binary serializers did become available with .NET Core 2.0, you won’t find a BrokeredMessage in the new API. Instead, you must take serialization into your own hands and supply a Message object with an array of bytes. From "Messages, payloads, and serialization":

While this hidden serialization magic is convenient, applications should take explicit control of object serialization and turn their object graphs into streams before including them into a message, and do the reverse on the receiver side. This yields interoperable results.

Interoperability is good, and the API change certainly pushes developers into the pit of success, which is also a general theme for .NET Core APIs.