OpenAI Codex CLI Tutorial for C#: Build a .NET 10 API

This OpenAI Codex CLI tutorial shows OpenAI Codex for C# developers in practice: how an AI coding agent can inspect a repository, implement an ASP.NET Core Web API, write tests, investigate failures, and review changes. The useful part is not asking an agent to “build everything” in one prompt. It is giving it a narrow task, checking its plan, running the project’s own tools, and reviewing every change before accepting it.

Short answer: yes, you can use Codex CLI with C#. Start the Codex command-line interface (CLI) in a .NET repository, ask it to inspect first, approve a small plan, let it implement one feature, then verify the result with dotnet build, dotnet test, and a Git diff review.

OpenAI Codex CLI workflow for building and testing a C# ASP.NET Core Web API with .NET 10

In this tutorial you will build and test an ASP.NET Core Web API with Codex: a small Products API using .NET 10 LTS, the Codex CLI, and PowerShell on Windows. You will create the project, work through inspect, plan, implement, test, debug, and review stages, run the endpoints by hand, and read the Git diff. The same workflow adapts to an existing repository when you use OpenAI Codex with ASP.NET Core in your own work.

Which “Codex” is this? This tutorial is about the Codex coding agent that works in your repository. It is not about calling OpenAI models from inside your C# application, and it is not the original Codex code-completion model and API that OpenAI retired in 2023, so NuGet wrappers built for that old API do not apply here. If you want to call OpenAI models from your own code, see the official OpenAI .NET library instead.

Version note (last reviewed October 26, 2026): The Codex install commands, CLI flags, codex review options, and slash commands in this article were checked against OpenAI’s published documentation and the official Codex repository in October 2026. Microsoft lists .NET 10 as a long-term-support (LTS) release supported through November 14, 2028, while .NET 8 and .NET 9 both reach end of support on November 10, 2026, per its .NET support policy. Codex clients, flags, and permission controls change often, so check the official Codex CLI documentation and run codex --help before relying on any option.

Testing note: The API code in this article was compiled with the .NET 10.0.113 SDK, and each endpoint and validation rule was exercised over HTTP. The xUnit test project could not be restored in that environment, so the test files were reviewed by reading, not run. The optional startup example with additional middleware in the production section was not compiled at all. Run dotnet build and dotnet test yourself and treat your own output as the final result.

What is the OpenAI Codex CLI?

Codex is an AI coding agent that works with a code repository instead of only suggesting a line of code. Depending on the client and your setup, it can read project files, propose or make edits, run development commands, help debug failures, and review a change. With the CLI, you start it from a project folder and direct it from the terminal. You stay responsible for the task, the permissions, the review, and the final decision.

This tutorial uses the local Codex CLI. It fits a .NET sample well because the agent works with the same files and tools you use for dotnet build and dotnet test, which is what makes it a practical AI coding agent for C# developers. The sample targets .NET 10, so this is also a Codex CLI .NET 10 walkthrough. Codex also has IDE integrations (the official IDE documentation lists VS Code and compatible editors such as Cursor and Windsurf, plus JetBrains IDEs including Rider), a desktop app, and a cloud workflow. Their features and execution environments are not identical, so follow the instructions for the client you actually use.

Codex is not a compiler, a test oracle, or a security certification tool. It can misread requirements, produce code that does not compile, miss edge cases, or change more than you asked. A successful build only says the compiler accepted the project; it does not prove the API is correct or safe.

Prerequisites and setup

The main walkthrough uses Windows PowerShell, the .NET 10 SDK, Git, and the Codex CLI. Codex can run natively on Windows or inside WSL2; the official Windows sandbox and WSL pages describe how each works. If you choose WSL, use WSL2: the approvals and security guide says current Codex versions no longer support WSL1. You should also be comfortable with basic C# and running commands in a terminal.

  • .NET SDK: Install the .NET 10 SDK from the official .NET download page. The SDK includes the tools to create, build, run, and test the project. A runtime alone is not enough.
  • Git: Install Git from the official Git downloads so you can see what the agent changes and return to a known state.
  • Codex CLI: Review the current Codex CLI documentation before installing.
  • Access: The CLI supports signing in with a ChatGPT plan or using an API key. Plans, limits, and terms change, so follow the sign-in screen and the official Codex documentation.

Open PowerShell and check the SDK first:

PowerShell
dotnet --info

The output should list a .NET 10 SDK. If PowerShell says the command is not recognized, install the SDK and open a new terminal window.

The official Codex CLI repository lists this installer command for Windows. It downloads and runs a script from the internet, so read the current official instructions first, and on a managed work computer check your IT policy:

PowerShell
powershell -ExecutionPolicy ByPass -c "irm https://chatgpt.com/codex/install.ps1 | iex"

If you would rather not pipe a script into PowerShell, the same repository lists a package-manager route. The npm package requires Node.js, so check the documentation for the minimum version:

PowerShell
npm install -g @openai/codex

Homebrew users on macOS can run brew install --cask codex, and the repository also offers downloadable release binaries. Make sure you install the scoped @openai/codex package; a similarly named unscoped package is a different project. On macOS or Linux, the repository gives this installer instead of the PowerShell command:

Bash
curl -fsSL https://chatgpt.com/codex/install.sh | sh

After installing, open a new PowerShell window and run codex from a project folder; the first run prompts you to sign in. Afterwards, codex login status reports the active sign-in mode when credentials are cached, and codex doctor generates a diagnostic report if something looks broken. If the executable is not found, check the installer output and your PATH before installing a second time. Never copy a cached authentication file or an API key into your project.

Control what Codex can do

Before you run the Codex CLI on any C# project, decide how much freedom the agent gets. Two separate settings decide that:

  • Sandbox mode limits what Codex can technically do when it runs commands: where it can write and whether it can reach the network.
  • Approval policy decides when Codex must stop and ask you before acting.

The official approvals and security guide describes both. In the CLI they are set with --sandbox (-s) and --ask-for-approval (-a).

Sandbox modeWhat it means
read-onlyCodex can read files and run commands, but cannot write to the workspace. Good for the inspection step.
workspace-writeCodex can edit files and run commands in the workspace. Network access is off by default. Protected paths such as .git, .agents, .codex, and .aws stay read-only, so Codex cannot make commits for you.
danger-full-accessNo sandbox. Avoid it on a normal development machine.
Approval policy (CLI)What it means
on-requestCodex works inside the sandbox by default and asks before actions that need more than the sandbox grants, such as network access or editing outside the workspace.
neverCodex never pauses to ask. Anything the sandbox blocks simply fails.

OpenAI’s documentation lists --sandbox workspace-write --ask-for-approval on-request as the recommended flags for low-friction local work, and this tutorial uses them. When you launch Codex in a folder under version control without any flags, the documentation says it recommends this same combination; in a folder that is not under version control it recommends read-only. Because the setup section creates a Git baseline commit, explicit flags make the active mode unambiguous.

Note on approval values: For the CLI flag --ask-for-approval, the documented values are on-request and never. The configuration file also accepts a documented granular approval-policy object. Do not set approval_policy = "untrusted": the configuration reference states that value is no longer supported, and the approvals guide warns that the retired setting can prevent Codex from starting, so remove it or choose a supported policy. The on-failure policy is deprecated. The separate trust_level = "untrusted" setting on a project entry in your user-level ~/.codex/config.toml is a different setting and remains supported. See the Codex configuration reference and the migration notes for the retired untrusted policy, and start Codex with --strict-config to get an error for configuration fields your installed version does not recognize.

Configuration interaction: The approvals guide says that explicitly setting on-request overrides the approval policy derived from a project’s trust_level = "untrusted" entry. That includes passing --ask-for-approval on-request on the command line. If you rely on that project setting to require approval for commands that would otherwise run without it, do not override it with on-request. Check the active permissions with /status or /permissions before continuing. This tutorial passes explicit flags because it uses a disposable sample project, not one marked untrusted.

A separate --dangerously-bypass-approvals-and-sandbox (--yolo) flag turns both protections off; the documentation says to use it only inside an externally hardened environment.

Inside a running session, /permissions lets you choose what Codex can do without asking first (see the approvals and security guide), and /status shows the current model, approval mode, and token usage. The developer commands reference covers CLI commands and slash commands. To be certain which sandbox is active, restart with explicit flags.

Keep the sandbox on and read each command request before you approve it. Skipping approvals and the sandbox just to avoid prompts removes the protection that limits what the agent can change.

Create the ASP.NET Core project

Start in a new folder so the sample does not touch an existing application. These PowerShell commands create a solution, a controller-based Web API project without OpenAPI support (the tutorial’s Program.cs does not use it), an xUnit test project, and a .gitignore. No database is needed. Run them yourself in PowerShell, not through Codex, because they restore packages from the network.

PowerShell
mkdir CodexProductsTutorial
cd CodexProductsTutorial

dotnet new sln -n CodexProductsTutorial
dotnet new webapi --use-controllers --no-openapi --framework net10.0 -n ProductsApi
dotnet new xunit --framework net10.0 -n ProductsApi.Tests
dotnet new gitignore

dotnet add .\ProductsApi.Tests\ProductsApi.Tests.csproj reference .\ProductsApi\ProductsApi.csproj
dotnet sln add .\ProductsApi\ProductsApi.csproj .\ProductsApi.Tests\ProductsApi.Tests.csproj

The .NET 10 SDK creates a CodexProductsTutorial.slnx solution file, and dotnet build and dotnet test work from the repository root. With --no-openapi, the generated ProductsApi.csproj contains no OpenAPI package reference. The controller template does create sample files that this tutorial replaces, so delete them:

PowerShell
foreach ($file in @(
  '.\ProductsApi\Controllers\WeatherForecastController.cs',
  '.\ProductsApi\WeatherForecast.cs',
  '.\ProductsApi\ProductsApi.http',
  '.\ProductsApi.Tests\UnitTest1.cs'
)) {
  if (Test-Path $file) { Remove-Item $file }
}

Template contents can change between SDK versions. If a path does not exist, list the files with Get-ChildItem .\ProductsApi -Recurse -File and delete only the sample files you recognize. The generated test project references xUnit 2.x packages. If your generated test project differs, open ProductsApi.Tests.csproj and confirm that its package versions support the APIs used in the examples. After the files from Step 3 and Step 4 are in place, the build should complete successfully if the SDK and package restore are configured correctly.

Now create a Git checkpoint so every later change shows up clearly in a diff:

PowerShell
git init
git add .
git commit -m "Baseline: generated template"

If the folder is already a repository, skip git init. If Git asks who you are, set user.name and user.email first.

Step 1: Inspect the project before modifying it

Start Codex from the repository folder with a read-only sandbox, so it can look but not edit (the flags are explained in Control what Codex can do):

PowerShell
codex --sandbox read-only --ask-for-approval on-request

A read-only sandbox cannot write files, while dotnet build and dotnet test write generated output (the obj and bin folders). So this first prompt does not ask Codex to build anything, and it makes Codex separate three different kinds of build information.

Prompt for Codex
Inspect this repository before making any changes. Do not create, edit, delete, or format files. This session is read-only, so do not run dotnet build or dotnet test.

Report:
1. The target framework, project templates, and relevant package references.
2. The current folder and file structure.
3. Build status, labeled as exactly one of: (a) supplied by me, (b) observed by you in this session with the exact command and output, or (c) unknown. If a command fails because of a sandbox or permission restriction, say so; that does not show the project fails to compile.
4. Existing application conventions and any generated template code that may be relevant.
5. What is missing for a small Products Web API and automated tests.
6. Risks, uncertainties, and any assumptions you would need to make.

End with a short implementation plan and wait for my approval before editing files.

Read the report carefully. It should name net10.0 and list the ProductsApi and ProductsApi.Tests projects. In a read-only session the build status should come back as “unknown” or “supplied by me” (you can paste the baseline build output yourself). Treat any claim that Codex built the project in this session with suspicion. If a repository already has useful code or conventions, preserve them instead of replacing them with a generic template.

When you are ready for edits, exit and start a new session with write access limited to the workspace:

PowerShell
codex --sandbox workspace-write --ask-for-approval on-request

You can also use /permissions inside a session, but restarting with explicit flags makes the active settings unambiguous. Because network access is off by default in this mode, run package commands yourself in a normal PowerShell window, as this tutorial does. Enabling network access for the sandbox is possible in the Codex configuration, but it widens what the agent can reach, so leave it off for this sample. Also commit changes yourself: .git is read-only inside the workspace sandbox.

Step 2: Plan the implementation

Once the inspection is accurate, ask for a small plan and settle any design questions before authorizing edits.

Prompt for Codex
Prepare a small implementation plan for a Products API targeting .NET 10.

Requirements:
- Use controller-based ASP.NET Core Web API endpoints.
- Implement GET /api/products, GET /api/products/{id}, and POST /api/products.
- Use a Product model, a request DTO, an IProductService interface, and a simple in-memory service registered through dependency injection.
- Validate that the product name is present and no longer than 120 characters, and that price is between 0.01 and 1,000,000.
- Return 404 when a product ID is not found, 201 Created for a successful POST, and automatic 400 responses for invalid request models.
- Do not add a database, authentication system, new production package, or unrelated refactor.
- Add focused service unit tests and a small set of HTTP integration tests.

List the files to create or edit, explain the responsibility of each file, identify any additional test package required, and state the build/test commands you propose to run. Flag uncertainties instead of inventing a requirement. Do not edit files until I approve the plan.

This step makes the scope explicit. The in-memory service is deliberately limited: data disappears when the app stops, so it is not a replacement for a database. It keeps the tutorial focused on the Codex workflow instead of database setup.

Step 3: Build the Products API with Codex CLI in ASP.NET Core

After you review and approve the plan, give Codex the implementation task below. This is the step where OpenAI Codex with ASP.NET Core does the heavy lifting, so keep the scope tight.

Prompt for Codex
Implement the approved Products API plan in the current repository.

Follow the existing project's naming and formatting conventions. Keep changes limited to the approved scope. Delete the generated WeatherForecast sample controller and model if they exist, because they are not part of this task. Use DataAnnotations for the request DTO and the [ApiController] behavior for invalid request models. Register IProductService with its in-memory implementation in Program.cs. Use a thread-safe in-memory collection because the API can receive concurrent requests.

Do not add database access, authentication, secrets, unrelated packages, unrelated formatting changes, or generated documentation files unless the approved plan requires them. Do not commit changes.

After editing, summarize each changed file and why it changed. Then run dotnet build if command execution is available and permitted. Report the exact command and actual result. If you cannot run it, say so. Do not describe the implementation as production-ready.

Codex may produce a different but valid design. The files below are a reference target you can compare against. If Codex’s version differs, ask it to explain the difference and check the result against the requirements; do not assume either version is automatically right.

File: ProductsApi/Program.cs

Replace the generated startup code with this version. It contains no OpenAPI registration or mapping, matching the --no-openapi project template.

C#
using ProductsApi.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddProblemDetails();
builder.Services.AddSingleton<IProductService, InMemoryProductService>();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler();
}

app.MapControllers();
app.Run();

public partial class Program { }

Outside Development, UseExceptionHandler() handles unexpected exceptions without exposing a stack trace. With no arguments it relies on the Problem Details service that AddProblemDetails() registers; if that registration is missing, the app fails at startup with an InvalidOperationException that names the missing configuration. In Development the handler is skipped on purpose, so ASP.NET Core’s default Developer Exception Page can show full exception details to you, the developer. That page must never be exposed to public users.

When testing the handler with a deliberately throwing endpoint, a Production-environment request that accepts JSON received a generic 500 Problem Details response. A request with Accept: text/html received a 500 with an empty body, so do not assume every client gets JSON. This is separate from request validation: invalid input is rejected earlier by the [ApiController] behavior and never throws. The sample has no endpoint that throws, so the handler is not exercised by the tests, and it is not a complete production error-handling design. See Microsoft’s error handling documentation.

The final partial declaration makes the entry-point type accessible to the test project, so WebApplicationFactory<Program> can start the app in a test host. The service is a singleton because the sample keeps its data for the lifetime of the process.

File: ProductsApi/Models/Product.cs

The model is the data the API returns. It is a small immutable record, not a database entity.

C#
namespace ProductsApi.Models;

public sealed record Product(int Id, string Name, decimal Price);

File: ProductsApi/Contracts/CreateProductRequest.cs

The request DTO (data transfer object) defines what a client may send. It keeps client-supplied fields separate from the server-generated ID.

C#
using System.ComponentModel.DataAnnotations;

namespace ProductsApi.Contracts;

public sealed class CreateProductRequest
{
    [Required]
    [StringLength(120, MinimumLength = 1)]
    public string Name { get; init; } = string.Empty;

    [Range(typeof(decimal), "0.01", "1000000",
        ParseLimitsInInvariantCulture = true,
        ConvertValueInInvariantCulture = true)]
    public decimal Price { get; init; }
}

Because the controller has [ApiController], ASP.NET Core automatically returns 400 Bad Request when these rules fail. [Required] rejects a missing, empty, or whitespace-only name. A request that omits name or price ends up with the default value (an empty string or zero), so the same rules reject it. The price limits are strings, and ParseLimitsInInvariantCulture = true makes "0.01" mean the same thing on every computer, including ones that use a comma as the decimal separator. ConvertValueInInvariantCulture = true controls how a value that arrives as text is converted before the range check; here the property is already a decimal, so it is mostly a safeguard. The price range is a tutorial rule, not a claim about real product catalogs. Validation attributes check the shape of input; they do not make an API secure on their own.

Two edge cases to know about. The length check runs on the name as sent, before the service trims it, so a name padded with spaces can be rejected even though its trimmed form would fit. And the range rule does not limit decimal places, so a price such as 1.999 is accepted as is. A real catalog would add a rule for that.

File: ProductsApi/Services/IProductService.cs

The interface lets the controller depend on a small contract instead of a storage implementation.

C#
using ProductsApi.Contracts;
using ProductsApi.Models;

namespace ProductsApi.Services;

public interface IProductService
{
    IReadOnlyList<Product> GetAll();
    Product? GetById(int id);
    Product Create(CreateProductRequest request);
}

File: ProductsApi/Services/InMemoryProductService.cs

This service uses a concurrent dictionary and an incrementing ID. It avoids database setup, but the data is in memory only: it is lost when the app stops, and separate application instances do not share it.

C#
using System.Collections.Concurrent;
using ProductsApi.Contracts;
using ProductsApi.Models;

namespace ProductsApi.Services;

public sealed class InMemoryProductService : IProductService
{
    private readonly ConcurrentDictionary<int, Product> _products = new();
    private int _nextId;

    public IReadOnlyList<Product> GetAll() =>
        _products.Values.OrderBy(product => product.Id).ToArray();

    public Product? GetById(int id) =>
        _products.TryGetValue(id, out var product) ? product : null;

    public Product Create(CreateProductRequest request)
    {
        var id = Interlocked.Increment(ref _nextId);
        var product = new Product(id, request.Name.Trim(), request.Price);
        _products[id] = product;
        return product;
    }
}

Interlocked.Increment gives each call a unique ID even when requests arrive at the same time, within the limits of this example (a single process and an int counter). Name.Trim() removes leading and trailing spaces before storing the name, and GetAll returns products in ascending ID order.

File: ProductsApi/Controllers/ProductsController.cs

The controller maps HTTP requests to service calls. CreatedAtAction makes a successful create return a Location header that points to the new product. The [ProducesResponseType] attributes document the main expected status codes for API tooling and for readers (including 415 for a POST without a JSON content type); they do not enforce or change runtime behavior.

C#
using Microsoft.AspNetCore.Mvc;
using ProductsApi.Contracts;
using ProductsApi.Models;
using ProductsApi.Services;

namespace ProductsApi.Controllers;

[ApiController]
[Route("api/[controller]")]
[Produces("application/json")]
public sealed class ProductsController : ControllerBase
{
    private readonly IProductService _products;

    public ProductsController(IProductService products)
    {
        _products = products;
    }

    [HttpGet]
    [ProducesResponseType(StatusCodes.Status200OK)]
    public ActionResult<IReadOnlyList<Product>> GetAll()
    {
        return Ok(_products.GetAll());
    }

    [HttpGet("{id:int}")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public ActionResult<Product> GetById(int id)
    {
        var product = _products.GetById(id);
        return product is null ? NotFound() : Ok(product);
    }

    [HttpPost]
    [ProducesResponseType(StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status415UnsupportedMediaType)]
    public ActionResult<Product> Create([FromBody] CreateProductRequest request)
    {
        var product = _products.Create(request);
        return CreatedAtAction(nameof(GetById), new { id = product.Id }, product);
    }
}

These three routes cover the main flow of a small API. There is no update or delete, persistence, authentication, authorization, paging, or business-specific rule. Add those only when real requirements call for them. Responses behave as follows: a missing product gives 404 with a Problem Details body, because MVC converts error results from [ApiController] controllers into Problem Details by default; a non-numeric ID such as /api/products/abc matches no route, so it never reaches a controller and gives an empty 404 (that case would need app.UseStatusCodePages() to get a body); a POST without a JSON Content-Type gives 415; and malformed JSON gives 400. Run dotnet build now; it should complete successfully.

How the code fits together

The diagram below follows one request through the sample. The client sends HTTP to the controller, the controller calls the service interface, dependency injection supplies the in-memory implementation, and the implementation reads or writes a dictionary. The response travels back the same way.

Request flow through the Products API A client sends an HTTP request to ProductsController. The controller calls IProductService. Dependency injection resolves that interface to InMemoryProductService, which reads and writes a ConcurrentDictionary held in memory. Client (script or test) HTTP request (JSON) ProductsController validation runs before the action calls IProductService resolved by DI InMemoryProductService reads and writes ConcurrentDictionary Solid: call. Dashed: injected by DI.
Request flow through the sample. The controller never knows whether the service stores data in memory or in a database.
RoutePurposeTypical response
GET /api/productsLists products in ascending ID order.200 OK with a JSON array.
GET /api/products/1Looks up a product by ID.200 OK or 404 Not Found.
POST /api/productsValidates and creates a product.201 Created, or 400 Bad Request if the request is invalid.

[ApiController] turns on API-specific behavior, including automatic 400 responses for invalid model state. [Route("api/[controller]")] takes the route prefix from the controller name, so ProductsController maps to api/products. The Location header the API returns uses the controller’s class-name casing (/api/Products/1); ASP.NET Core routing is case-insensitive, so both forms work. For more context, see Microsoft’s documentation on creating web APIs with ASP.NET Core and model validation.

When every file is in place, your solution should look like this (generated bin, obj, and Properties folders omitted):

Folder tree
CodexProductsTutorial/
  CodexProductsTutorial.slnx
  .gitignore
  ProductsApi/
    ProductsApi.csproj
    Program.cs
    Controllers/ProductsController.cs
    Contracts/CreateProductRequest.cs
    Models/Product.cs
    Services/IProductService.cs
    Services/InMemoryProductService.cs
  ProductsApi.Tests/
    ProductsApi.Tests.csproj
    ProductServiceTests.cs
    ProductsApiIntegrationTests.cs

Step 4: Build and test the ASP.NET Core Web API with Codex

Ask for tests after the implementation exists. Request unit tests for the service logic and a few integration tests that use the real HTTP pipeline. The difference matters: calling a service method directly does not prove that routing, JSON binding, and automatic validation work together.

Prompt for Codex
Add focused automated tests for the Products API.

1. Add unit tests for InMemoryProductService that verify a created product is stored, its name is trimmed, products are listed in ID order, concurrent creates receive unique IDs, and a missing ID returns null.
2. Add HTTP integration tests using WebApplicationFactory<Program> and HttpClient.
3. Verify that GET /api/products returns 200 OK, and that POST with a valid product returns 201 Created and the returned product can be retrieved.
4. Verify that GET for an unknown ID returns 404 Not Found.
5. Verify that an empty name, a whitespace-only name, a 121-character name, a price of zero, and a price just above 1,000,000 return 400 Bad Request through the real API pipeline.
6. Verify that a 120-character name, a price of exactly 0.01, and a price of exactly 1,000,000 are accepted with 201 Created.
7. Use the existing test framework and target framework. Explain any new package before adding it. Delete the template's UnitTest1.cs. Do not test private implementation details or assert only that methods were called. Do not assume the product list is empty, because the tests share one app instance.

Run dotnet test if command execution is available and permitted. Report the actual result and any failures; do not invent a passing test count.

The integration tests need the ASP.NET Core testing package, which provides WebApplicationFactory and an in-memory test server. Run this yourself in PowerShell; the version pattern keeps the package on the same major version as .NET 10. The dotnet package add form is new in the .NET 10 SDK; on older SDKs, use the verb-first dotnet add package form instead:

PowerShell
dotnet package add Microsoft.AspNetCore.Mvc.Testing --version "10.0.*" --project .\ProductsApi.Tests\ProductsApi.Tests.csproj

Open ProductsApi.Tests.csproj and confirm the package reference shows a 10.x version.

File: ProductsApi.Tests/ProductServiceTests.cs

These unit tests call the service directly, with no HTTP involved. The concurrency test creates 100 products in parallel and checks that no two share an ID. One test like this cannot prove thread safety in every situation, but it would catch an unsynchronized counter in this example.

C#
using ProductsApi.Contracts;
using ProductsApi.Services;
using Xunit;

namespace ProductsApi.Tests;

public sealed class ProductServiceTests
{
    [Fact]
    public void Create_TrimsNameAndStoresProduct()
    {
        var service = new InMemoryProductService();
        var request = new CreateProductRequest { Name = " Keyboard ", Price = 49.99m };

        var product = service.Create(request);

        Assert.Equal("Keyboard", product.Name);
        Assert.Equal(product, service.GetById(product.Id));
    }

    [Fact]
    public void GetAll_ReturnsProductsInIdOrder()
    {
        var service = new InMemoryProductService();
        var first = service.Create(new CreateProductRequest { Name = "Keyboard", Price = 49.99m });
        var second = service.Create(new CreateProductRequest { Name = "Mouse", Price = 19.99m });

        var products = service.GetAll();

        Assert.Equal(new[] { first.Id, second.Id }, products.Select(product => product.Id));
    }

    [Fact]
    public void Create_WhenCalledConcurrently_AssignsUniqueIds()
    {
        var service = new InMemoryProductService();
        var request = new CreateProductRequest { Name = "Keyboard", Price = 49.99m };

        Parallel.For(0, 100, _ => service.Create(request));

        var ids = service.GetAll().Select(product => product.Id).ToArray();
        Assert.Equal(100, ids.Distinct().Count());
    }

    [Fact]
    public void GetById_WhenProductDoesNotExist_ReturnsNull()
    {
        var service = new InMemoryProductService();

        var product = service.GetById(999999);

        Assert.Null(product);
    }
}

File: ProductsApi.Tests/ProductsApiIntegrationTests.cs

These tests send real HTTP requests to an in-memory test server. They check routing, JSON binding, validation, and status codes without opening a network port. All integration tests share one app instance, so tests that create products do not assume the list is initially empty or that a newly created product has a particular ID. The missing-product test uses an ID that this sample test suite never creates. Prices in the theories are passed as strings and parsed with the invariant culture, because attribute arguments cannot be decimal values. The Location assertion ignores case because the header uses the controller’s class-name casing.

C#
using System.Globalization;
using System.Net;
using System.Net.Http.Json;
using Microsoft.AspNetCore.Mvc.Testing;
using ProductsApi.Contracts;
using ProductsApi.Models;
using Xunit;

namespace ProductsApi.Tests;

public sealed class ProductsApiIntegrationTests
    : IClassFixture<WebApplicationFactory<Program>>, IDisposable
{
    private readonly HttpClient _client;

    public ProductsApiIntegrationTests(WebApplicationFactory<Program> factory)
    {
        _client = factory.CreateClient();
    }

    public void Dispose() => _client.Dispose();

    [Fact]
    public async Task GetAllProducts_ReturnsOk()
    {
        var response = await _client.GetAsync("/api/products");

        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
    }

    [Fact]
    public async Task GetMissingProduct_ReturnsNotFound()
    {
        var response = await _client.GetAsync("/api/products/999999");

        Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
    }

    [Fact]
    public async Task PostValidProduct_ReturnsCreatedAndCanBeRetrieved()
    {
        var request = new CreateProductRequest { Name = "Keyboard", Price = 49.99m };

        var response = await _client.PostAsJsonAsync("/api/products", request);

        Assert.Equal(HttpStatusCode.Created, response.StatusCode);
        Assert.NotNull(response.Headers.Location);

        var created = await response.Content.ReadFromJsonAsync<Product>();
        Assert.NotNull(created);
        Assert.EndsWith($"/api/products/{created!.Id}", response.Headers.Location!.ToString(),
            StringComparison.OrdinalIgnoreCase);

        var getResponse = await _client.GetAsync($"/api/products/{created.Id}");
        Assert.Equal(HttpStatusCode.OK, getResponse.StatusCode);
    }

    [Fact]
    public async Task PostNameLongerThan120Characters_ReturnsBadRequest()
    {
        var request = new CreateProductRequest { Name = new string('x', 121), Price = 10m };

        var response = await _client.PostAsJsonAsync("/api/products", request);

        Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
    }

    [Fact]
    public async Task PostNameOfExactly120Characters_ReturnsCreated()
    {
        var request = new CreateProductRequest { Name = new string('x', 120), Price = 10m };

        var response = await _client.PostAsJsonAsync("/api/products", request);

        Assert.Equal(HttpStatusCode.Created, response.StatusCode);
    }

    [Theory]
    [InlineData("", "10.00")]
    [InlineData("   ", "10.00")]
    [InlineData("Mouse", "0.00")]
    [InlineData("Mouse", "1000000.01")]
    public async Task PostInvalidProduct_ReturnsBadRequest(string name, string price)
    {
        var request = new CreateProductRequest
        {
            Name = name,
            Price = decimal.Parse(price, CultureInfo.InvariantCulture)
        };

        var response = await _client.PostAsJsonAsync("/api/products", request);

        Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
    }

    [Theory]
    [InlineData("0.01")]
    [InlineData("1000000")]
    public async Task PostBoundaryPrice_ReturnsCreated(string price)
    {
        var request = new CreateProductRequest
        {
            Name = "Boundary item",
            Price = decimal.Parse(price, CultureInfo.InvariantCulture)
        };

        var response = await _client.PostAsJsonAsync("/api/products", request);

        Assert.Equal(HttpStatusCode.Created, response.StatusCode);
    }
}

The xUnit template supplies the test SDK and runner. The extra ASP.NET Core package supplies the test host. For current setup details, see Microsoft’s guides to xUnit unit testing and ASP.NET Core integration tests.

Run everything from the repository root:

PowerShell
dotnet build
dotnet test

With exactly the files above, the suite defines 4 unit-test facts and 5 integration-test facts, plus 6 integration-test theory cases (4 invalid-input cases and 2 price-boundary cases), for 15 test cases in total. This count comes from the source code; use your own dotnet test summary as the final authority. If a test fails, read the failure message and stack trace before asking Codex to change anything, then continue to Step 5.

Run and manually check the API

Start the app in one PowerShell window and leave it running:

PowerShell
dotnet run --project .\ProductsApi\ProductsApi.csproj --no-launch-profile -- --urls http://localhost:5080

Arguments after -- go to the application, and ASP.NET Core reads --urls as a configuration setting. Because --no-launch-profile skips launchSettings.json, the app defaults to the Production environment if neither DOTNET_ENVIRONMENT nor ASPNETCORE_ENVIRONMENT is set. With WebApplication.CreateBuilder, DOTNET_ENVIRONMENT takes precedence over ASPNETCORE_ENVIRONMENT. The sample serves plain HTTP on localhost only to keep the walkthrough short; a real service would use HTTPS. In a second window, create a product. A successful create returns HTTP 201 with the product body.

PowerShell
$body = '{"name":"Codingvila Keyboard","price":49.99}'
$product = Invoke-RestMethod `
  -Uri 'http://localhost:5080/api/products' `
  -Method Post `
  -ContentType 'application/json' `
  -Body $body

$product

The response should be an object with id, name (“Codingvila Keyboard”), and price (49.99). On a freshly started app the id should be 1. The JSON property names are lowercase because ASP.NET Core uses camelCase by default. Now fetch it by ID:

PowerShell
Invoke-RestMethod -Uri "http://localhost:5080/api/products/$($product.id)"

PowerShell treats HTTP 400 and 404 responses as errors. This helper separates three situations: success, an HTTP error response from the API, and a failure with no HTTP response at all (for example, the server is not running). It is written for both Windows PowerShell 5.1 and PowerShell 7.

PowerShell
function Test-ApiCall {
  param([string]$Label, [scriptblock]$Call)
  try {
    $null = & $Call
    "${Label}: success status, no HTTP error"
  }
  catch {
    $response = $_.Exception.Response
    if ($null -ne $response) {
      "${Label}: HTTP $([int]$response.StatusCode) returned by the server"
    }
    else {
      "${Label}: no HTTP response received - $($_.Exception.Message)"
    }
  }
}

Test-ApiCall 'Invalid body (expect 400)' {
  Invoke-RestMethod -Uri 'http://localhost:5080/api/products' `
    -Method Post -ContentType 'application/json' `
    -Body '{"name":"","price":0}'
}

Test-ApiCall 'Unknown ID (expect 404)' {
  Invoke-RestMethod -Uri 'http://localhost:5080/api/products/999999'
}

With the server running, the two calls should report HTTP 400 and HTTP 404. If the server is stopped, you see the “no HTTP response” message instead, which is a connection problem, not an API result. The same requests were sent with curl against the running API and returned 201, 200, 404, and 400 as described. Stop the server with Ctrl+C when you finish. Restarting the app clears the in-memory data.

Step 5: Diagnose build and test failures

When a build or test fails, give Codex the actual error instead of a paraphrase. Include the command you ran and enough output to show the first relevant error. Remove secrets, personal data, private repository URLs, and sensitive file contents first.

Prompt for Codex
The command below failed in my local environment. Analyze the actual output before editing any files.

Command:
PASTE THE EXACT COMMAND HERE

Output:
PASTE THE RELEVANT ERROR AND STACK TRACE HERE

Please:
1. Separate facts directly shown in the output from hypotheses.
2. Identify the smallest likely root cause and point to the relevant file or line if the evidence supports it.
3. Explain one minimal fix and any trade-offs.
4. Do not change unrelated files or upgrade packages just to silence an error.
5. Ask before making a change that expands the approved scope.
6. After the fix, rerun the relevant build or test command if execution is available and permitted. Report the exact result and any remaining failures.

Do not claim that the issue is fixed unless the relevant check succeeds.

Typical causes in a project like this include an SDK mismatch, a missing project reference or package, a namespace error, a route mismatch, wrong JSON property names, and a test that asserts something the implementation never promised. If Codex ran a command in a restricted sandbox, a permission or network error may come from the sandbox rather than from your code.

Step 6: Review the Git changes before accepting them

Passing builds and tests are only part of the review. Inspect the whole diff for unrelated edits, deleted files, weakened validation, hardcoded credentials, broad permission changes, and dependencies the task did not need.

PowerShell
git status --short
git diff --check
git diff --stat
git diff

If you used the baseline commit above, git diff shows changes to tracked files. New files appear in git status as untracked, so open them directly. git diff --check only finds whitespace problems; it does not judge correctness or security. Keep or discard changes deliberately, and never run a destructive reset unless you know exactly which work it will erase.

Codex also has a built-in review feature. Type /review inside a session to have it review your working tree, or run codex review --uncommitted from the terminal, which reviews staged, unstaged, and untracked changes. Other targets include --base <branch> and --commit <SHA>; the developer commands reference lists them. Treat the result as a second opinion, not a replacement for reading the diff yourself. For a more controlled review, use a custom prompt like this one:

Prompt for Codex
Review the current uncommitted Git changes for the Products API, including new untracked files. Do not edit files or commit changes.

Prioritize:
- Incorrect behavior, routing, validation, status codes, and null handling.
- Missing or misleading tests and edge cases.
- Security risks, exposed secrets, unsafe defaults, and accidental authorization assumptions.
- Unnecessary packages, unrelated file changes, and compatibility issues.
- Maintainability and consistency with the approved plan.

For each finding, include severity, file and line when available, evidence from the code, impact, and a suggested fix. Separate confirmed defects from possible risks. If you find no issues, state that the review found none; do not claim that the code is proven secure or production-ready. Do not make changes.

Before accepting a change, use this checklist:

  • The changes contain only the expected files.
  • API behavior and validation rules match the requirements.
  • Build and test results come from commands run after the final edit.
  • No secrets, local settings, generated output, or unrelated files were added.
  • A human assessed every finding, and unresolved risks are written down.

Optional: Add project instructions with AGENTS.md

Codex reads AGENTS.md files before doing any work, which makes them a good place for stable conventions and the checks to run. They are guidance, not a guarantee that every line is followed, and they do not replace a task prompt, code review, or access controls. According to the official AGENTS.md guide, Codex builds its instruction chain when a session starts:

  • Global: in your Codex home folder (~/.codex by default) it uses AGENTS.override.md if present, otherwise AGENTS.md.
  • Project: from the project root (typically the Git root) down to your current folder, it checks each folder for AGENTS.override.md, then AGENTS.md, and includes at most one file per folder.
  • Merge: files are joined from the root down, so files closer to your current folder come later and override earlier guidance. Combined size is capped (32 KiB by default).

For this sample, put one file in the repository root. The /init slash command can generate a starter file. This example is guidance, not a required format:

Markdown
# Project guidance

- Target .NET 10 unless an approved issue says otherwise.
- Run `dotnet build` and `dotnet test` from the repository root.
- Do not add OpenAPI packages or any production dependency without explaining why it is needed.
- Keep API request DTOs separate from response/domain models where useful.
- Never place credentials, access tokens, or connection strings in source code.
- Keep changes focused on the current task.
- Report checks that could not be executed instead of claiming they passed.

If instructions look stale, the guide says to restart Codex in the target folder, because Codex rebuilds the chain for each run and at the start of each interactive session. Empty files are ignored. Keep the instructions short, specific, and consistent with the sample’s real commands.

Common mistakes and troubleshooting

ProblemWhat to check
dotnet is not recognizedInstall the .NET SDK, open a fresh terminal, and run dotnet --info.
codex is not recognizedCheck the installer output and PATH, then follow the current installation instructions in the Codex repository. codex doctor reports installation, configuration, and authentication problems once the command runs.
Codex rejects or ignores a setting in your configurationSearch ~/.codex/config.toml (and any project .codex/config.toml) for approval_policy = "untrusted", which is no longer supported, or on-failure, which is deprecated, and replace them with on-request or never. Do not confuse this with trust_level = "untrusted" on a project entry, which is still supported. Start with --strict-config to get an error for unrecognized fields, compare against the current configuration reference, and prefer explicit --sandbox and --ask-for-approval flags while you debug.
The template rejects an optionRun dotnet new webapi --help and use only options your SDK supports.
The project still references OpenAPIOpen ProductsApi.csproj and, if the reference is present, remove it with dotnet remove .\ProductsApi\ProductsApi.csproj package Microsoft.AspNetCore.OpenApi.
The test project cannot find ProgramConfirm the API ends with public partial class Program { } and the test project references the API project.
Integration tests cannot find WebApplicationFactoryConfirm the Microsoft.AspNetCore.Mvc.Testing reference (version 10.x) is in the test project, then rebuild.
The app fails at startup mentioning UseExceptionHandlerWith no arguments, UseExceptionHandler() needs AddProblemDetails() registered. Keep both lines, or configure the handler with options instead.
An invalid POST returns 201 instead of 400Confirm the controller has [ApiController], the DTO has validation attributes, and the test goes through HTTP.
A POST returns 415 Unsupported Media TypeSend Content-Type: application/json, as -ContentType 'application/json' does in the PowerShell examples.
PowerShell reports no HTTP responseThe request never reached the API. Check that the server is running and the port matches, then retry.
Codex reports a build failure in a read-only sessionThe sandbox may have blocked writes to obj and bin. Run the build yourself, or restart with --sandbox workspace-write.
Codex cannot restore packagesNetwork access is off by default in the sandbox. Run dotnet restore or dotnet package add yourself in a normal terminal when using the .NET 10 SDK. On .NET 9 SDKs and earlier, use dotnet add package.
Codex cannot commit.git is read-only in the workspace sandbox. Commit from your own terminal after reviewing the diff.
Codex proposes a sweeping rewriteReject the broad change, restate the scope, ask for the smallest change, and compare against your baseline commit.
Codex seems to ignore your AGENTS.mdStart a new session in the project folder, confirm the file is not empty, and check for an AGENTS.override.md in a nearer folder or in your Codex home folder.
A build passes but behavior is wrongWrite a test that captures the expected behavior and run it. Compilation alone does not prove correctness.

Security and responsible use

Keep the agent’s access in proportion to the task. Use a disposable sample or a branch when you try a new workflow, keep the default sandbox and approvals, and read every command request before you approve it. Prompts and boundaries vary by client, operating system, and managed workspace settings, so check the approvals and security guide.

Do not paste API keys, access tokens, production credentials, customer data, or private logs into a prompt. Avoid committing authentication caches or files that hold secrets. Check new package names, maintainers, versions, licenses, and advisories before you accept a dependency change. Treat any web content or issue text the agent reads as untrusted, because it can contain instructions aimed at the AI.

From this sample to a production service

This sample is a learning project, not a production service. It keeps data in memory, has no authentication, serves plain HTTP in the manual run, and its tests have never run against a real database. Before shipping a real API, plan for:

  • authentication and per-resource authorization on every write endpoint
  • durable storage, migrations, and tests against the real database engine you will use
  • concurrency control, so simultaneous updates do not silently overwrite each other
  • HTTPS redirection, HSTS, and your hosting platform’s TLS setup
  • rate limiting, structured logging, health checks, and monitoring
  • input and output limits, paging for list endpoints, and a documented error contract
  • a CI pipeline that runs dotnet build and dotnet test on every change
  • security testing and dependency review

As a starting point for the transport and availability items, here is an optional Program.cs example with additional middleware. It uses built-in ASP.NET Core middleware only (no new packages). It is not part of the tested sample: it was reviewed against Microsoft’s documentation but not compiled, so add it to a branch, run dotnet build and dotnet test, and adjust the limits to your traffic.

C#
using Microsoft.AspNetCore.RateLimiting;
using ProductsApi.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddProblemDetails();
builder.Services.AddHealthChecks();
builder.Services.AddSingleton<IProductService, InMemoryProductService>();
builder.Services.AddRateLimiter(options =>
{
    options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
    options.AddFixedWindowLimiter("api", limiter =>
    {
        limiter.PermitLimit = 100;
        limiter.Window = TimeSpan.FromMinutes(1);
        limiter.QueueLimit = 0;
    });
});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler();
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseRouting();
app.UseRateLimiter();

app.MapHealthChecks("/health");
app.MapControllers().RequireRateLimiting("api");

app.Run();

public partial class Program { }

What each addition does, and its limits: UseHsts() and UseHttpsRedirection() push browsers and clients toward HTTPS, but they only help when your host actually serves HTTPS (with no HTTPS port configured, the redirect middleware logs a warning and does not redirect). Reverse-proxy note: this example does not configure forwarded headers. If TLS terminates at a reverse proxy or ingress controller, configure ASP.NET Core to process forwarded headers from trusted proxies before HSTS and HTTPS redirection. Otherwise the application may treat an externally secure HTTPS request as HTTP and cause incorrect redirects. Trust only the proxies and networks that suit your hosting environment, and never accept arbitrary forwarded headers. The exact configuration depends on your hosting topology; see Microsoft’s proxy and load balancer guidance. AddHealthChecks() with MapHealthChecks("/health") exposes a basic liveness endpoint for load balancers; it checks nothing about dependencies until you register checks. UseRouting() is called explicitly because Microsoft documents that UseRateLimiter() must run after routing when endpoint-specific policies such as RequireRateLimiting("api") are used. The fixed-window limiter above is one shared bucket for all callers, not a per-client limit, so a real service should partition by user or IP address and account for reverse proxies. See Microsoft’s documentation on rate limiting middleware, health checks, and enforcing HTTPS.

Validation attributes alone are not a security design. Because .NET 8 and .NET 9 reach end of support on November 10, 2026, start new services on .NET 10.

Limitations and a recommended Codex workflow

Codex works best when the task has a clear outcome, a small set of files, and a way to check success. As an AI coding agent for C# developers, it can speed up repository exploration, routine implementation, test scaffolding, and a first-pass review. It can still miss business context, invent an API, misread an error, write tests that only mirror the implementation, or overlook deployment and security limits.

The loop below is the whole workflow in one picture. Notice the “No” path: a failed check sends you back to a small fix, not to a bigger prompt.

Codex workflow for a C# change Start a task, inspect read-only, plan and approve, implement one change, then run build and tests. If checks do not pass, return to implementing. If they pass, review the Git diff, then accept or discard the change. Start task 1. Inspect (read-only) 2. Plan and approve 3. Implement one change 4. Run build and tests Checks pass? No Yes 5. Review the Git diff Accept or discard
The recommended loop: inspect, plan, implement, verify, review. A failed check (dashed line) sends you back to implementing a smaller fix.
  1. Inspect: ask for evidence about the current repository before any edit (Step 1).
  2. Plan: agree on files, behaviors, limits, and acceptance criteria (Step 2).
  3. Implement: authorize one small change with clear constraints (Step 3).
  4. Verify: run the build, the automated tests, and the relevant manual checks (Step 4 and manual checks).
  5. Review: read the diff and judge correctness and risk yourself (Step 6).
  6. Record: keep a checkpoint commit and write down known limitations before merging.

Do not hand off an irreversible production deployment, a broad permissions change, or a sensitive migration without the right human controls. The developer who merges and ships the code is accountable for it.

Frequently asked questions

Can OpenAI Codex write C# code?

Yes. Codex can work with C# files and the .NET CLI in a local repository when your environment and permissions allow it. You still need to check that the generated code matches your framework, package versions, architecture, and requirements.

Does Codex CLI work with .NET 10?

This tutorial uses the .NET 10 SDK, and Codex runs the same dotnet commands you would run yourself. Install the SDK first, confirm it with dotnet --info, and keep the sandbox settings from Control what Codex can do in mind, because a read-only sandbox blocks builds and the default workspace sandbox has no network access for package restore.

What is an AI coding agent for C# developers?

It is a tool that works inside your repository instead of only completing a line of code. It can read your C# files, propose edits, run commands such as dotnet test, and report results. You stay in charge of scope, permissions, review, and the decision to merge.

Is Codex the same as the OpenAI API?

No. Codex is a coding agent you run against your repository. The OpenAI API (and its official .NET library) is how your own application calls OpenAI models. This tutorial covers the first.

Can Codex help build an ASP.NET Core Web API?

Yes. It can inspect a project, implement controllers or endpoints, add DTOs and services, and run local build or test commands. This tutorial shows a small controller-based API, not a production-ready system. See Step 3 and Step 4 for the full flow.

Can Codex run dotnet build and dotnet test?

Only where the sandbox allows it. A read-only sandbox blocks the writes that a build needs, and the default workspace sandbox has network access off, which can break package restore. Run restore yourself, then let Codex build and test inside the workspace sandbox.

Can Codex generate unit tests for .NET?

It can draft tests with a supported .NET test framework such as xUnit. Review the assertions to make sure they check behavior and meaningful edge cases. For routing and automatic model validation, integration tests complement unit tests.

Can Codex help debug C# build errors?

It can analyze real compiler output and suggest a fix. Include the exact command and the relevant error, then rerun the failing command after the change. A plausible explanation is not proof that the problem is solved.

Does Codex work with Visual Studio?

This walkthrough uses the CLI, which you can run in a terminal alongside Visual Studio; that is different from having a dedicated Visual Studio extension. The current Codex IDE documentation lists the extension for VS Code and compatible editors such as Cursor and Windsurf, plus an integration for JetBrains IDEs (including Rider, which many C# developers use). It does not list a dedicated Visual Studio integration. Check the official Codex IDE documentation for the latest supported editors.

Is Codex suitable for beginners?

It can help beginners understand a repository and learn from proposed changes, especially when prompts ask for explanations. Beginners should still learn to run the compiler, read test failures, understand HTTP status codes, and spot unsafe changes before accepting generated code.

Does AI-generated code need manual review?

Yes. Read the diff, verify behavior with tests and manual checks, inspect dependencies and security-sensitive code, and confirm the result matches the real requirements. A passing build or test suite is useful evidence, but it is not proof of correctness or security.

Can Codex replace a developer?

No. It can assist with coding tasks, but people still define requirements, make architecture decisions, understand business rules, assess risk, review changes, and take responsibility for deployment.

Conclusion

A productive OpenAI Codex CLI workflow for C# is a series of small, reviewable steps, not one request to build an entire application. Inspect the repository, agree on a plan, implement one focused ASP.NET Core feature, run the build and tests, investigate real failures, and read the Git diff before you accept anything.

Try this process first in a non-sensitive repository or on a separate branch. Once you can explain and verify each change yourself, adapt the same prompts to a real ASP.NET Core service while keeping your team’s review, testing, and security requirements in place.

Codingvila provides articles and blogs on web and software development for beginners as well as free Academic projects for final year students in Asp.Net, MVC, C#, Vb.Net, SQL Server, Angular Js, Android, PHP, Java, Python, Desktop Software Application and etc.

If you have any questions, contact us on info.codingvila@gmail.com