Blog

Home / Thoughts, sketches, dev cases

Upgrade netcore 3.1 to dotnet 7

Upgrade netcore 3.1 to dotnet 7

Wednesday, February 8, 2023

dotnet 7

Comments

The team I am the member of recently faced the need to upgrade dotnet version of our backend product, which is the REST API web application serving several dozens of endpoints. Because of end-of-support for netcore 3.1 we've got a choice — to upgrade to dotnet 6 LTS or dotnet 7 STS version. On the one hand LTS promises longer support period, which is making our management more confident, on the other — dotnet 7 was tempting because of its latest language features and widely announced by Microsoft evangelists performance improvements. Finally, the decision was made for dotnet 7, taking into account just half-a-year gap between dotnets 6-7 end-of-support time, so future switching to dotnet 8 LTS will be fine in any case.

Application solution the team is working on consists of some dozen of c# projects. Frontend interaction is provided by REST API, some business tasks are processed by several Azure Functions. As it turned out later, source code modification was required only in two or three places, the story about one of them is below. Almost all efforts for the upgrade were taken by changes in .csproj files, where nuget packages are configured. Through the time of solution developing, its projects got some number of obsolete nuget packages, so my first intention was to use this dotnet upgrade task as opportunity to update those packages to the latest versions all at once. This is what I did. However, as Thanos said, "reality is often disappointing". General update of nuget packages cause so great mess in the code that I needed to revert the solution to its initial state.

After that I tried opposite approach, that is to retain as many nuget packages on their current versions as possible, and to update only those of them which were crucial for the overall upgrade progress. That strategy proved to be working and it allowed me eventually to upgrade the whole solution successfully.

About changes in the source code — some refactoring was required for one of Azure Functions that is triggered by Service Bus message. In netcore 3.1 times the signature of the method looked like this:


[FunctionName("LandManagementIdentityServiceBusListenerFunction")]
public async Task Run(
    [ServiceBusTrigger("%ServiceBusTopicName%", "%ServiceBusSubscriptionName%", Connection = "ServiceBusConnectionString")] Message message,
    ILogger log,
    CancellationToken cancellationToken)
{
    if (!message.UserProperties.ContainsKey("eventName") || string.IsNullOrWhiteSpace(message.UserProperties["eventName"].ToString()))
    {
        log.LogError("Event name is empty. Message cannot be processed.");
        throw new InvalidOperationException("Event name is empty. Message cannot be processed.");
    }

	// .. downstream code
}

After upgrade to dotnet 7 the problem with the message parameter cropped up. Turned out that Microsoft.Azure.ServiceBus.Message type is no longer used to contain both the message itself and its properties. In the new dotnet versions the message parameter is expected to be a string, object or strong class type to deserialize incoming data into. However, in case of our Function, to process the message it requires several UserProperties which are passed not as message body properties but as properties of the message itself.

Service Bus explorer - message properties

In netcore 3.1 the type of Microsoft.Azure.ServiceBus.Message was a complex object which has properties for the message body as well as for the message's properties. This was changed in dotnet 6/7 for isolated mode functions and now, to obtain those UserProperties, another parameter of type FunctionContext is needed for the executable method of the function:

       
[Function("LandManagementIdentityServiceBusListenerFunction")]
public async Task Run(
    [ServiceBusTrigger("%ServiceBusTopicName%", "%ServiceBusSubscriptionName%", Connection = "ServiceBusConnectionString")] string message,
    FunctionContext functionContext,
    CancellationToken cancellationToken)
{ 
    var messageProps = JsonConvert.DeserializeObject>(
        functionContext.BindingContext.BindingData.FirstOrDefault(x => x.Key == "UserProperties").Value?.ToString() ?? string.Empty);

    if (messageProps is null || !messageProps.ContainsKey("eventName") || string.IsNullOrWhiteSpace(messageProps["eventName"].ToString()))
    {
        _logger.LogError("Event name is empty. Message cannot be processed.");
        throw new InvalidOperationException("Event name is empty. Message cannot be processed.");
    }

	// .. downstream code
}

Hopefullt this will help someone. Thanks for reading!

© theyur.dev. All Rights Reserved. Designed by HTML Codex