Hosting Commerce Scale Unit extensions in .NET 6
Table of Contents
Introduction
Since approximately Fall 2021, Commerce Scale Unit (CSU) Extensions are required, see the purple Note, to be built by targeting .netstandard2.0 (.NET 6 is recommended starting 2023) rather than older versions of .netstandard/.NET Framework/.NET. That allows to host the CSU in .NET 6, rather than .NET Framework, which brings performance, scalability and reliability improvements.
.netstandard2.0 or .NET 6 is used for extensions based on the new Commerce SDK, but if you have not migrated (hurry up since the Retail SDK retired in Oct 2023) to it yet and still employing retired and not recommended legacy Retail SDK, make sure the projects you use to build the extensions target .netstandard2.0 or .NET 6, it then will be easier for you to migrate to the Commerce SDK.
When the Commerce SDK was introduced several years ago, we recommended to build CSU extensions by using .netstandard2.0 because:
a) that is the only “glue” between .NET Framework and .NET 6+. In other words, .netstandard 2.0 is supported in both - .NET Framework and .NET 6.
b) there was the time when both of the SDK were in support - Retail SDK, targeting CSUs hosted in .NET Framework, and Commerce SDK, targeting CSUs hosted in .NET 6. Therefore, while customers onboard Commerce SDK, they would need for some time to have the same extension working in both - .NET Framework and .NET 6 hosted CSUs.
After Oct 2023, when the Retail SDK has been retired and CSUs starting version 10.0.38 and newer can only be hosted in .NET 6, you can still build your extensions by targeting .netstandard2.0 but we recommend much more newer .NET 6 instead. The same applies to Store Commerce extensions. As of Fall 2023, .NET Framework is only used to build extensions for Hardware Station.
If the extension was compiled by using .NET 6, or better to say, anything else than .netstandard2.0 or .NET Framework, it cannot be deployed into .NET 6 hosted CSU.
Reliability improvements
One of the aspects to be mentioned is .NET 6 known to better handle cases when an exception is thrown deep in the async call stack, such as for instance, inside a Data Service interacting with a Channel DB where there could be exceptions associated with extensions related issues such as timeouts or invalid SQL syntax and so on. That "innocent" exception might result in a StackOverflowException crashing an instance of Azure App Service hosted in .NET Framework 4.x which will bring not ideal user's experience because the request, failed processing due to the crash, will have to be sent by the client again. In addition, some time will be needed to initialize a new instance of the CSU's App Service.
Therefore, if your CSU is experiencing such type of crashes, which you might know about from a correspondence with the Support in a response to your inquiries, making sure your extensions are following the .netstandard2.0/.NET 6 requirements becomes even more important as it will bring not only "free" (the ASP.NET Core pipeline used in .NET 6 is faster than ASP.NET used in .NET Framework 4.x) performance and scalability improvements but also stability ones brought by the .NET 6.
Checking the type of the host
Point your browser to the Url formed by concatenating the CSU's base Url with /Commerce/GetEnvironmentConfiguration
This will print a very basic information about your CSU including the field FrameworkName. If the CSU is hosted in .NET 6, the value corresponding to that field will be equal to .NET. If the CSU is hosted in .NET Framework 4.x, the value will be .NET Framework. Below is an example of the CSU hosted in .NET 6:
And here is how the .NET Framework 4.x hosted CSU responds:
If your CSU's FrameworkName renders .NET Framework - that means your CSU is not hosted in .NET 6, most likely because of the extensions not following the .netstandard2.0 /.NET 6 requirements.
Diagnosing Extensions
You can check if the extensions deployed to your scale unit are compatible with .netstandard2.0/.NET 6 hosted CSU or not, to do that employ the healthcheck endpoint by appending /healthcheck?testname=extensions to the CSU's Base Url (the one without Commerce suffix). It will instruct the CSU to run a set of tests validating various aspects of the extension's compatibility. This is the output from the CSU hosting .netstandard2.0 compliant extensions which can be seen as a green color used to highlight the results of the test Target framework (extensions):
In case the Target framework (extensions) test doesn't pass - it will be highlighted in red printing ta number of violating assemblies. In order to see more details about violating assemblies, use your local dev environment and temporarily modify the web.config of your CSU to include the key (search for it first so you don't end-up adding it twice with different values) "HealthCheck.Extensions.ShowAssemblyFiles" with the value "true" inside the section appSettings
which already contains bunch of other keys.
If your CSU is already on .NET 6, you need to edit the configuration file Microsoft.Dynamics.Retail.RetailServer.AspNetCore.dll.config .
Once done, refresh the browser and you will see assemblies' names for both - passed and failed tests:
Interpreting tests failures
Prior migrating your CSU to .NET 6, all the tests, including Target Framework (dependencies), should pass (be green), but if that specific test contains errors it doesn't necessary means a real problem. That test analyzes dependencies of the assemblies listed in the extension's composition, if the test finds that the assembly listed there (A1.dll) was compiled by referencing another assembly (A2.dll) and that A2.dll is missing in the extension package, the A2.dll then will be reported in the failed (red) test's section. Only the author of the extension knows if that is a real issue or not because only the author knows the implementation of the A1.dll, there could be a number of cases:
a) A1.dll provides functionality which relies on A2.dll for every CRT/RS extension point available in A1.dll. For instance: A1.dll contains handlers for 5 requests and all
5 handlers require A2.dll for them to work. In this case, missing A2.dll is a real problem.
b) A1.dll provides functionality which relies on A2.dll only for a subset of CRT/RS extension points available in A1.dll. For instance: A1.dll contains handlers for 5 requests but only 1 handler requires A2.dll for it to work. If your scenarios will never result in the usage of that handler for which the A2.dll dependency is required, then you might decide that the missing A2.dll is not really a problem
c) A1.dll provides functionality which doesn't rely on A2.dll at all but A2.dll was still referenced while compiling the A1.dll. In this case missing A2.dll is not really a problem.
As you can see this test cannot simply be used to say for sure if absence of the dll is real issue or not and it requires the above analysis. At the same time, if you are the extension's provider, it is good idea to make sure your extension's package is shipped with all assemblies it references so your customers don't have to spend time communicating with you asking if missing assemblies is a real issue or not.
Let's consider another case like the one seen here:
Despite the test Obsolete extensions (IRequestTrigger) didn't pass and indicates the extension violates the requirement to not employ obsolete extensions constructions, that issue doesn't prevent from hosting the CSU in .NET 6 but still is highly recommended to be resolved. Otherwise, the CSU might behave without visible issues during low/average load (number of incoming requests), but is almost guaranteed to experience performance/scalability/availability issues under a high load which can be hard to investigate. The same applies to at least the test Obsolete extensions (IRequestHandler).
In case the Target framework (extensions) doesn't pass - the CSU is not eligible to be hosted in .NET 6.
The test Target framework (others) validates target framework used to build the assemblies found in the extension folder for which the test didn't find, analyzing compile time dependencies, any usage. Therefore, if you extensively validated your extension and it is working as you expect even with the errors seen in this test - they should not prevent you from deploying an extension in .NET 6 based CSU. These are rather "FYI warnings" which could be used to bring your attention the extension is packaged with assemblies for which no usage was found and therefore they might be candidates for you to clean-up.
Overall, it is a very good idea to consider *any* failing tests as preventing migration to .NET 6 because they were invented based on years of analyzing mistakes in the Extensions which often lead to severe PROD issues including the performance/scalability/availability area.
The below tests are required to pass so the CSU is considered eligible to be hosted in .NET 6:
- Target framework (extensions)
- Target framework (dependencies)
- Controllers
- Controllers (Invalid route prefix)
- Obsolete extensions (ICommerceController)
To avoid PROD outages, it is important and highly recommended to extensively test extensions in .NET 6 hosted CSU outside of PROD environment, prior migrating a PROD CSU to .NET 6.
If you created a fresh non-PROD F&O Environment in LCS, then any CSUs deployed there will be hosted in .NET 6. Therefore the extensions deployed into that CSU must be compatible with .NET 6.
Here you can find additional information on how to interpret specific tests seen in the "Test Name" column: Commerce Scale Unit extensions health check
Switching CSU host type
An extension is considered to be eligible for .NET 6 hosting is all of the below conditions are satisifed:
a) the CSU version is newer than 10.0.26 and has not reached End of Service
b) the extension was compiled targeting .netstandard2.0 or .NET 6
c) the health-check's test suite extensions doesn't report failed tests required to succeed for .NET 6 migration (see the previous 2 sections)
*This post is locked for comments