.NET on AWS Blog
Port .NET Framework workloads to Linux with HAQM Q Developer, Part 3: Web APIs
Introduction
This blog series explores how to port different kinds of .NET Framework projects to cross-platform .NET with HAQM Q Developer Transformation for .NET, currently in public preview. Part 1 covers porting of class library projects and Part 2 covers porting of .NET Test Projects. Here in Part 3 , we walk you through porting of a .NET Framework Web API to NET Core.
Modernizing a Windows-based .NET Framework solution to run on Linux can reduce operational costs by up to 40%. However, organizations face specific challenges when modernizing their .NET Framework applications. Development teams must evaluate their existing codebase, analyze dependencies, and address compatibility requirements before migrating to modern .NET and Linux environments. This migration requires careful planning, especially for enterprise applications with complex architectures.
HAQM Q Developer, a generative AI-powered assistant, uses advanced .NET transformation capabilities to streamline the migration process, offering intelligent code analysis, automated transformation suggestions, and comprehensive compatibility assessments. HAQM Q Developer significantly reduces the complexity and risk associated with modernizing .NET Framework projects by intelligently identifying potential transformation challenges, recommending best practices, and providing real-time guidance.
Overview
Application Programming Interfaces (APIs) enable software components to communicate through defined protocols. ASP.NET Framework initially offered Web API as its solution for building RESTful APIs. The release of .NET Core marked a significant advancement in API development. Unlike its predecessor, .NET Core API runs on Windows, macOS, and Linux distributions. It delivers improved performance through built-in dependency injection, middleware support, and a streamlined development model.
This post walks through the modernization of a .NET Framework Web API to cross-platform .NET using HAQM Q Developer. Starting with a sample Products API, we demonstrate the automated transformation process, including code migration, dependency injection updates, and application validation. The walkthrough showcases HAQM Q Developer capabilities in simplifying the modernization journey.
Prerequisites
For this tutorial, you will need:
1. Visual Studio 2022 IDE installed.
2. A subscription to HAQM Q Developer Pro.
3. AWS Toolkit for Visual Studio IDE extension installed and configured for HAQM Q Developer.
4. Download or clone the sample application from GitHub.
Walkthrough
Step 1: Run the original solution
1. We use a sample ASP.NET Web API application (.NET framework 4.8). The Products API provides CRUD operations to manage Product records in a database. Figure 1 shows the Web API solution structure in Visual Studio:

Figure 1: Web API Sample Solution Structure
2. The solution uses SimpleInjector package to implement Dependency Injection (DI) framework as shown in Listing 2:
using System.Web.Http;
using ProductsWebAPI.Service;
using SimpleInjector;
using SimpleInjector.Integration.WebApi;
using SimpleInjector.Lifestyles;
namespace ProductsWebAPI.App_Start
{
public static class DependencyInjectionSetup
{
public static void Configure()
{
var container = new Container();
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
// Register your dependencies
container.Register<IProductsService, ProductsService>(Lifestyle.Scoped);
// Register other services...
// Register Web API Controllers
container.RegisterWebApiControllers(GlobalConfiguration.Configuration);
// Set the dependency resolver for Web API
GlobalConfiguration.Configuration.DependencyResolver =
new SimpleInjectorWebApiDependencyResolver(container);
container.Verify();
}
}
}
Listing 2: Dependency Injection using SimpleInjector
3. The ProductsController has CRUD operations like ListProducts
, GetProduct
, CreateProduct
, UpdateProduct
, and DeleteProduct
. The controller is derived from ApiController
(Systems.Web.Http
) as shown in Listing 3.
using System;
using System.Web.Http;
using ProductsWebAPI.Models;
using ProductsWebAPI.Service
namespace ProductsWebAPI.Controllers
{
public class ProductsController : ApiController
{
private readonly IProductsService _productsService;
public ProductsController(IProductsService productsService)
{
_productsService = productsService ?? throw new ArgumentNullException(nameof(productsService));
}
[Route("api/products")]
[HttpGet]
public IHttpActionResult ListProducts()
{
try
{
var products = _productsService.GetAllProducts();
return Ok(products);
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
[Route("api/products/{id:int}")]
[HttpGet]
public IHttpActionResult GetProduct(int id)
{
if (id <= 0)
{
return BadRequest("Invalid product ID. ID must be greater than 0.");
}
try
{
var product = _productsService.GetProduct(id);
if (product == null)
{
return NotFound();
}
return Ok(product);
}
catch (System.Exception ex)
{
return InternalServerError(ex);
}
}
[Route("api/products")]
[HttpPost]
public IHttpActionResult CreateProduct([FromBody] Product value)
{
if (value == null)
{
return BadRequest("Product data cannot be null");
}
try
{
_productsService.SaveProduct(value);
return Created($"api/products/{value.Id}", value);
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
[Route("api/products/{id:int}")]
[HttpPut]
public IHttpActionResult UpdateProduct(int id, [FromBody] Product newValue)
{
if (id <= 0)
{
return BadRequest("Invalid product ID. ID must be greater than 0.");
}
if (newValue == null)
{
return BadRequest("Product data cannot be null");
}
try
{
var existingProduct = _productsService.GetProduct(id);
if (existingProduct == null)
{
return NotFound();
}
_productsService.UpdateProduct(id, newValue);
return Ok(newValue);
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
[Route("api/products/{id:int}")]
[HttpDelete]
public IHttpActionResult DeleteProduct(int id)
{
if (id <= 0)
{
return BadRequest("Invalid product ID. ID must be greater than 0.");
}
try
{
_productsService.DeleteProduct(id);
return Ok();
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
}
}
Listing 3: ProductsController class with routes
4. Use class ProductsService
as the service interface to talk to the repository, that is the ProductsContext
class.
using System.Collections.Generic;
using System.Linq;
using ProductsWebAPI.Models;
using ProductsWebAPI.Repository;
namespace ProductsWebAPI.Service
{
public class ProductsService : IProductsService
{
public ProductsService() { }
//Implement interface definitions
public IEnumerable<Product> GetAllProducts()
{
//return products;
using (var db = new ProductsContext())
{
return db.Products.ToArray();
}
}
public Product GetProduct(int id)
{
using (var db = new ProductsContext())
{
Product query = (from p in db.Products
where p.Id == id
select p).FirstOrDefault();
if (query == null)
{
return null;
}
return query;
}
}
public void SaveProduct(Product product)
{
using (var db = new ProductsContext())
{
db.Products.Add(product);
db.SaveChanges();
}
}
public void DeleteProduct(int id)
{
using (var db = new ProductsContext())
{
Product value = (from p in db.Products
where p.Id == id
select p).FirstOrDefault();
if (value == null)
{
return;
}
db.Products.Remove(value);
db.SaveChanges();
}
}
public void UpdateProduct(int id, Product product)
{
using (var db = new ProductsContext())
{
Product value = (from p in db.Products
where p.Id == id
select p).FirstOrDefault();
if (value == null)
{
return;
}
value.Name = product.Name;
value.Price = product.Price;
value.Category = product.Category;
db.SaveChanges();
}
}
}
}
Listing 4: ProductService class
5. The solution uses a SQL Server express and Entity Framework to instantiate the SQL client. Follow the steps for Code First to a New Database to develop a new database and Code First will create an empty database and add new tables. Listings 5 and 6 show a sample DbContext class and a connection string to the local database. Follow security best practices when deploying to test/production environment in terms of connection string encryption, using only trusted data source providers, and running the application with minimum permissions.
using System.Data.Entity;
using ProductsWebAPI.Models;
namespace ProductsWebAPI.Repository
{
public class ProductsContext : DbContext
{
public DbSet<Product> Products { get; set; }
}
}
Listing 5: Db Context
<configSections>
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
<connectionStrings>
<add name="ProductsContext" connectionString="Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=ProductsContext;Integrated Security=True;Connect Timeout=30;Encrypt=False;" providerName="System.Data.SqlClient" />
</connectionStrings>
Listing 6: Connection String
6. Press F5 or choose the Run (▶) icon in Visual Studio to launch an IIS express server with the default web API page. You can use any test client to test the APIs, such as Postman.
Step 2: Port Project with HAQM Q Developer
This step covers porting the solution from .NET Framework to cross-platform .NET with HAQM Q Developer.
1. Connect to HAQM Q. In Visual Studio navigate to Extensions > AWS Toolkit > Getting Started to view the Getting Started: AWS Toolkit with HAQM Q page. Under HAQM Q, if it does not say Connected, choose Sign in and sign in with your credentials.

Figure 7: HAQM Q Getting Started
2. Open a code file (any .cs file) in the editor to enable .NET transformation.
3. Start porting. In Solution Explorer, right-click the solution and choose Port solution with HAQM Q Developer.
4. On the Port a solution with HAQM Q dialog, choose Start. The following figure shows a short video of the process.

Figure 8: Start porting a solution
5. An HAQM Q Developer transformation job kicks off, indicated by a notification message at top, Q Transformation is in progress. Figure 9 shows a transformation plan.

Figure 9: HAQM Q Developer Transformation Progress
6. Wait for the transformation job to complete (Figure 10). HAQM Q Developer displays the detailed steps of each phase of transformation. It displays a Your transformation is completed notification near the top of Visual Studio when the job completes. It offers you to view diffs of each changed file as seen in the following figure.

Figure 10: HAQM Q Developer Transformation Complete
Step 3: Review and apply the code transformation
In this step, review the changes proposed by HAQM Q Developer and apply them to the project.
1. Choose View code transformation summary to display a summary of changes that includes many renamed files and a few newly added files.

Figure 11: HAQM Q Developer Transformation Summary
2. Choose View diffs to review individual files with changes. Choose Show Changes for any file to review the differences in Visual Studio.

Figure 12: HAQM Q Developer Transformation Diff of changed files
3. Figure 13 shows the changes in the newly added Program.cs file:

Figure 13: Viewing transformation differences in Program class file
4. Evaluate the changes in files. To accept the changes, choose Select all and Apply changes to accept the transformation of the .NET 4.8 Web API into a .NET 8.0 Core Web API.

Figure 14: Accepting transformation changes
Step 4: Review and Run tests on ported solution
In this step, run tests against the ported solution to confirm all APIs are working.
1. Testing the Ported Solution requires two steps. First, build the solution. Be aware that after transformation, API calls may generate a System.InvalidOperationException
. You can implement dependency injection to resolve this. ASP.NET Core in .NET 8 includes native dependency injection capabilities, which replaces the SimpleInjector functionality. Add the dependency injection code to the ConfigureServices
method in Startup.cs
file. Following this the API runs without errors.

Figure 15: Dependency Injection code
2. Next, test the API. Refer to How to use Visual Studio Endpoint Explorer to test ASP.Net Core APIs. Figure 16 displays all the API operations visible in the Endpoints Explorer. You can test individual API methods in the Endpoint Explorer.
The tests confirms that the transformation does not compromise the APIs original functionality.

Figure 16: API Test
Cleanup
There is no AWS cleanup since we did not deploy to AWS. You can delete the test project on your local development machine when you are finished with it.
Conclusion
In this post, you learned how to modernize a .NET Framework solution that includes Web APIs using HAQM Q Developer .NET transformation. To get started with HAQM Q Developer .NET transformation, visit HAQM Q Developer transformation capabilities for .NET porting (Preview). Here are some tips as you explore modernization of your .NET workloads to AWS:
1. Explore HAQM Q Developer: Transform for transforming .NET as well as mainframe, VMware and Java workloads.
2. Configure HAQM Q Developer Pro: Set up subscriptions, manage user access, and resolve common subscription issues to ensure seamless team adoption.
3. Remember to always test thoroughly and validate the migrated application in a staging environment before deploying to production.