ai newsMo., 19. Okt.
EilmeldungThe Nx Malware Ran Claude Code With --dangerously-skip-permissions. Could It on Yours?

Using Grok 4.7 with C#

xAI just announced that Grok 4.7 so I wanted to try their model with C#. This isn't another incremental update—it's a model specifically architected for advanced reasoning, long-horizon problem solving, software engineering, and agentic workflows. For .NET developers building enterprise application

dev.toMontag 21:37 · 19.10.15 Min. Lesezeit
Bild: dev.to

Posted on Oct 19 • Originally published at taswar.zeytinsoft.com

xAI just announced that Grok 4.7 so I wanted to try their model with C#. This isn't another incremental update—it's a model specifically architected for advanced reasoning, long-horizon problem solving, software engineering, and agentic workflows. For.NET developers building enterprise applications with AI capabilities, here's what actually matters and how to put it to work.

Grok 4.7 is xAI's latest model release, built from the ground up for scenarios where you need an AI that can think through multi-step problems, use tools effectively, and maintain context across extended tasks. The key differentiators:

The model was also evaluated on CADGenBench (generating executable CAD geometry from design requirements), reflecting xAI's broader engineering focus beyond just code generation. But for.NET developers, the software engineering and knowledge work capabilities are what matter most right now.

Deploy grok-4.7 in a supported Microsoft Foundry region. Copy the resource endpoint ( https://<resource>.services.ai.azure.com ) and the deployment name (which may differ from grok-4.7 ) from the Foundry portal. You need permission to deploy the model and a role that allows calling the deployed endpoint. The examples below use the official OpenAI.NET SDK pointed at the documented /openai/v1 Foundry endpoint, authenticating with Microsoft Entra ID so no API keys live in source.

With the.NET 10 SDK installed, create a project, add the SDK packages, and store the endpoint and deployment name in user secrets:

dotnet new console -n Grok-4.7-Demo-Csharp -f net10.0 cd Grok-4.7-Demo-Csharp dotnet add package OpenAI dotnet add package Azure.Identity dotnet add package Microsoft.Extensions.Configuration.UserSecrets dotnet user-secrets init dotnet user-secrets set "Foundry:Endpoint" "https://YOUR-RESOURCE.services.ai.azure.com" dotnet user-secrets set "Foundry:Deployment" "YOUR-DEPLOYMENT-NAME" Enter fullscreen mode Exit fullscreen mode Authentication goes through Microsoft Entra ID via DefaultAzureCredential, so there are no keys to store or leak. For local development, sign in once with az login (Visual Studio and VS Code sign-ins also work); when the app runs in Azure, the same credential resolves managed identity automatically. Your identity needs a role that grants inference access—for example, Cognitive Services User —on the Foundry resource, and the Entra token scope for this API is https://ai.azure.com/.default.

User secrets here hold only non-secret configuration (endpoint and deployment name) and remain a local-development convenience, not a production config store. See the Grok authentication guide for more detail.

Create FoundryChat.cs next to Program.cs. This shared factory builds a configured ChatClient the whole program reuses:

#pragma warning disable OPENAI001 // The Entra ID authenticationPolicy constructor is an evaluation API. using System.ClientModel.Primitives; using Azure.Identity; using Microsoft.Extensions.Configuration; using OpenAI; using OpenAI.Chat; public static class FoundryChat { public static ChatClient CreateClient () { var config = new ConfigurationBuilder (). AddUserSecrets ( typeof ( FoundryChat ). Assembly ).

Build (); var endpoint = config [ "Foundry:Endpoint" ]?? throw new InvalidOperationException ( "Set Foundry:Endpoint with dotnet user-secrets." ); var deployment = config [ "Foundry:Deployment" ]?? throw new InvalidOperationException ( "Set Foundry:Deployment with dotnet user-secrets." ); // DefaultAzureCredential resolves az login, Visual Studio, or managed identity. var tokenPolicy = new BearerTokenPolicy ( new DefaultAzureCredential (), "https://ai.azure.com/.default" ); var options = new OpenAIClientOptions { Endpoint = new Uri ( $" { endpoint. TrimEnd ( '/' )} /openai/v1" ) }; return new ChatClient ( model: deployment, authenticationPolicy: tokenPolicy, options: options ); } } Enter fullscreen mode Exit fullscreen mode The OPENAI001 pragma is required because the OpenAI SDK marks its custom- authenticationPolicy constructor as an evaluation API—it is the supported way to attach an Entra bearer token to a non-OpenAI endpoint. Setting Endpoint to.../openai/v1 lets the SDK append chat/completions itself; this v1 surface needs no api-version query parameter.

The five examples below form a single Program.cs, not five standalone files. Replace the generated Program.cs with the shared setup block that follows, then append each use-case function (Use Cases 1–5) to the same file; keep FoundryChat.cs unchanged. The program picks a case from the command line— dotnet run -- 2 runs Use Case 2 and dotnet run -- all runs them in sequence, defaulting to Use Case 1 when you pass no argument.

using System.Diagnostics; using System.Text.Json; using OpenAI.Chat; var selectedCase = args. FirstOrDefault ()?. ToLowerInvariant ()??

"1"; if ( selectedCase is not ( "1" or "2" or "3" or "4" or "5" or "all" )) { Console. WriteLine ( "Usage: dotnet run -- [1|2|3|4|5|all]" ); return; } var chat = FoundryChat. CreateClient (); // A shared deadline for the whole run; Ctrl+C cancels in-flight requests too. using var cancellation = new CancellationTokenSource ( TimeSpan.

FromMinutes ( 10 )); Console. CancelKeyPress += ( _, e ) => { e. Cancel = true; cancellation.

Cancel (); }; string [] cases = selectedCase == "all"? [ "1", "2", "3", "4", "5" ]: [ selectedCase ]; foreach ( var useCase in cases ) { Console. WriteLine ( $"\n--- Use case { useCase } ---" ); try { switch ( useCase ) { case "1": await RunSpecDraftAsync (); break; case "2": await RunRefactoringReviewAsync (); break; case "3": await RunToolCallingAsync (); break; case "4": await RunImpactAssessmentAsync (); break; case "5": await RunCompileFixLoopAsync (); break; } } catch ( OperationCanceledException ) { Console. Error.

WriteLine ( "Cancelled, or the demo's ten-minute limit was reached." ); Environment. ExitCode = 1; break; } catch ( Exception ex ) { // Demo boundary: report the failure but let 'all' continue to the next case. Console.

Error. WriteLine ( $"Use case { useCase } failed: { ex. Message } " ); Environment.

ExitCode = 1; } } Enter fullscreen mode Exit fullscreen mode Run from the Grok-4.7-Demo-Csharp directory with dotnet run -- <case>. These examples send paid requests to your deployed model. If you see 401/403, check your Entra sign-in ( az login ) and role assignment; for 404, verify the resource endpoint and deployment name (not the model catalog ID); for 429, reduce traffic or retry with backoff.

Each use-case block below is a function appended to this same file; the two helpers at the end of Use Case 5 go last.

Use a constrained prompt and review the resulting document with your team rather than treating generated architecture as an approved design.

async Task RunSpecDraftAsync () { var requirements = """ Multi - location inventory system: 10 K concurrent users, updates visible within two seconds, SQL Server migration, SAP REST integration, audit trail and a six - month delivery timeline. The team has limited NoSQL experience. """; ChatCompletion completion = await chat.

CompleteChatAsync ( [ new SystemChatMessage ( "Draft a technical specification with assumptions, architecture trade-offs, data model, APIs, reliability, security, rollout phases and open questions. Do not invent verified benchmarks." ), new UserChatMessage ( requirements ) ], cancellationToken: cancellation. Token ); await File.

WriteAllTextAsync ( "inventory-system-spec.md", completion. Content [ 0 ]. Text ); Console.

WriteLine ( "Review inventory-system-spec.md with the engineering team." ); } Enter fullscreen mode Exit fullscreen mode Output Case 1 --- Use case 1 --- Review inventory-system-spec.md with the engineering team. Enter fullscreen mode Exit fullscreen mode Use Case 2: Two-stage refactoring review The first completion analyzes code; the second uses that analysis to propose changes. The embedded code is deliberately input data, not a compilable payment implementation.

async Task RunRefactoringReviewAsync () { var existingCode = """ // InventoryService.cs (excerpt) var current = await GetStockLevel ( sku ); if ( current >= quantity ) await UpdateStockLevel ( sku, current - quantity ); // OrderManager.cs (excerpt) order. Status = "Cancelled"; await SaveOrder ( order ); await SendCancellationEmail ( order ); """; ChatCompletion analysis = await chat. CompleteChatAsync ( [ new SystemChatMessage ( "Review the supplied C# excerpts.

Identify concurrency, consistency and validation issues; distinguish facts from assumptions." ), new UserChatMessage ( existingCode ) ], cancellationToken: cancellation. Token ); var findings = analysis. Content [ 0 ].

Text; Console. WriteLine ( findings ); ChatCompletion plan = await chat. CompleteChatAsync ( [ new SystemChatMessage ( "Propose a reviewable refactoring plan, test cases and a migration strategy.

Ask for missing domain details instead of inventing them." ), new UserChatMessage ( $"Excerpts:\n { existingCode } \n\nReview:\n { findings } " ) ], cancellationToken: cancellation. Token ); Console. WriteLine ( "\nRefactoring proposal:\n" + plan.

Content [ 0 ]. Text ); } Enter fullscreen mode Exit fullscreen mode I have omitted the output of case 2 since it was pretty long, so you should try it yourself. This is a planning aid, not an automatic repository refactor.

A human must inspect proposed patches and run tests.

The SDK surfaces tool requests as completion.ToolCalls with a ToolCalls finish reason; never parse assistant prose for a string like tool_use. This small example looks up local, hard-coded notes, not live web search. Replace the allowlisted lookup with a real, trusted data source before claiming current research results. Keep the tool result tied to its tool_call_id and bound the number of rounds.

async Task RunToolCallingAsync () { var messages = new List < ChatMessage > { new SystemChatMessage ( "Use lookup_note for supplied local notes. Say explicitly when information is unavailable; do not claim live web access." ), new UserChatMessage ( "What should I consider when adding distributed tracing to a.NET service?" ) }; var options = new ChatCompletionOptions { Tools = { ChatTool. CreateFunctionTool ( functionName: "lookup_note", functionDescription: "Look up a local note about tracing or sampling", functionParameters: BinaryData.

FromString ( """ { "type": "object", "properties": { "topic": { "type": "string", "description": "tracing or sampling" } }, "required": [ "topic" ] } """)) } }; for ( var round = 0; round < 4; round ++) { ChatCompletion completion = await chat. CompleteChatAsync ( messages, options, cancellation. Token ); if ( completion.

FinishReason!= ChatFinishReason. ToolCalls ) { Console. WriteLine ( completion.

Content [ 0 ]. Text ); return; } messages. Add ( new AssistantChatMessage ( completion )); // Preserve assistant tool calls and their IDs. foreach ( var call in completion.

ToolCalls ) { var result = "Unknown tool or topic"; if ( call. FunctionName == "lookup_note" ) { using var arguments = JsonDocument. Parse ( call.

FunctionArguments ); var topic = arguments. RootElement. GetProperty ( "topic" ).

GetString (); result = topic switch { "tracing" => "Local note: use OpenTelemetry spans and propagate trace context across HTTP calls.", "sampling" => "Local note: measure cost and choose a sampling strategy before production rollout.", _ => "No local note exists for this topic." }; } messages. Add ( new ToolChatMessage ( call. Id, result )); } } Console.

WriteLine ( "Stopped after four tool rounds; inspect the conversation before retrying." ); } Enter fullscreen mode Exit fullscreen mode Output Case 3 --- Use case 3 --- Use OpenTelemetry spans and propagate trace context across HTTP calls. Local notes do not cover further.NET-specific setup (exporters, sampling, instrumentation packages, or collector config). In practice, also consider: - **Instrumentation:** ASP.NET Core, HttpClient, and other outbound clients so incoming and outgoing calls create spans automatically. - **Context propagation:** W3C `traceparent` / `tracestate` (or your platform’s equivalent) on every hop so traces stay connected. - **Sampling and volume:** Head- or tail-based sampling so high-traffic services do not overwhelm storage. - **Export path:** An OTLP exporter to a collector or backend, plus resource attributes (service name, version, environment). - **Operational impact:** Span cardinality, sensitive data in attributes, and the cost of always-on tracing in hot paths.

Enter fullscreen mode Exit fullscreen mode Remember to treat tool arguments as untrusted input. Do not let a model execute arbitrary shell commands or read arbitrary paths; apply authorization, timeouts and size limits in a real agent.

A large context window is a ceiling, not a reason to upload every file. This example uses the local console project's Program.cs so it runs without a separate repository; point it at your own reviewed files when using it for a real change.

async Task RunImpactAssessmentAsync () { var path = "Program.cs"; var code = await File. ReadAllTextAsync ( path, cancellation. Token ); if ( code.

Length > 40_000 ) throw new InvalidOperationException ( "Select a smaller, relevant excerpt before sending it." ); var proposedChange = "Add structured logging for each external API request."; ChatCompletion completion = await chat. CompleteChatAsync ( [ new SystemChatMessage ( "Assess the impact of a proposed C# change. List directly affected code, likely dependencies, test cases and unknowns.

Do not claim to have read files not supplied." ), new UserChatMessage ( $"Change: { proposedChange } \nFile: { path } \n``` {% endraw %} csharp \ n { code } \ n {% raw %} ``` ") ], cancellationToken: cancellation. Token ); await File. WriteAllTextAsync ( "impact-assessment.md", completion.

Content [ 0 ]. Text ); Console. WriteLine ( "Review impact-assessment.md; only the supplied file was analyzed." ); } Enter fullscreen mode Exit fullscreen mode Output Case 4 --- Use case 4 --- Review impact-assessment.md; only the supplied file was analyzed.

Enter fullscreen mode Exit fullscreen mode For an actual repository-wide assessment, explicitly select relevant files, exclude credentials and generated artifacts, and stay within your deployment's token and rate limits. The Grok guide documents a 500,000-token context window; both input and output consume it.

This is the honest version of "agentic self-correction." The model generates code, your harness compiles it and feeds any errors back, and the loop repeats until the build is clean or a round limit is hit. The compiler is the verifier—Grok 4.7 does the reasoning and the patching, but the loop, the build step, and the stopping rule are code you own, not something the model does on its own. The generated code is compiled, never executed, in a throwaway class library.

async Task RunCompileFixLoopAsync () { // Isolated scratch class library. Generated code is built, never run. var workDir = Path. Combine ( Path.

GetTempPath (), "grok-codegen-" + Guid. NewGuid (). ToString ( "N" )[..

8 ]); Directory. CreateDirectory ( workDir ); await RunDotnetAsync ( workDir, "new classlib --output. --framework net10.0" ); var generatedPath = Path. Combine ( workDir, "Generated.cs" ); var task = """ Write a single C # file with a public static class ` Temperature ` exposing ` CelsiusToFahrenheit ( double c ) ` and ` FahrenheitToCelsius ( double f ) `, both returning double.

Return only the file contents. """; var messages = new List < ChatMessage > { new SystemChatMessage ( "You write C# that compiles on.NET 10. When given compiler errors, return the complete corrected file, not a diff.

Output only C# source." ), new UserChatMessage ( task ) }; for ( var round = 1; round <= 4; round ++) { ChatCompletion completion = await chat. CompleteChatAsync ( messages, cancellationToken: cancellation. Token ); var source = StripFences ( completion.

Content [ 0 ]. Text ); await File. WriteAllTextAsync ( generatedPath, source, cancellation.

Token ); var ( compiled, output ) = await RunDotnetAsync ( workDir, "build --nologo -clp:ErrorsOnly" ); if ( compiled ) { Console. WriteLine ( $"Compiled on round { round }. Review { generatedPath } and add tests before use." ); return; } Console.

WriteLine ( $"Round { round } did not compile; sending errors back." ); messages. Add ( new AssistantChatMessage ( source )); messages. Add ( new UserChatMessage ( $"That file did not compile.

Fix it and return the complete file.\n\nCompiler output:\n { output } " )); } Console. WriteLine ( "Stopped after four rounds without a clean build; inspect the last attempt manually." ); } static string StripFences ( string text ) { var trimmed = text. Trim (); if (! trimmed.

StartsWith ( "``` {% endraw %} ")) return trimmed; var firstNewline = trimmed. IndexOf ( '\n' ); var body = firstNewline < 0? trimmed: trimmed [( firstNewline + 1 )..]; var closingFence = body. LastIndexOf ( " {% raw %} ``` ", StringComparison.Ordinal); return ( closingFence < 0? body: body [.. closingFence ]).

Trim (); } static async Task <( bool compiled, string output )> RunDotnetAsync ( string workDir, string arguments ) { using var process = Process. Start ( new ProcessStartInfo ( "dotnet", arguments ) { WorkingDirectory = workDir, RedirectStandardOutput = true, RedirectStandardError = true })!; var stdout = await process. StandardOutput.

ReadToEndAsync (); var stderr = await process. StandardError. ReadToEndAsync (); await process.

WaitForExitAsync (); return ( process. ExitCode == 0, stdout + stderr ); } Enter fullscreen mode Exit fullscreen mode Output - Case 5 --- Use case 5 --- Compiled on round 1. Review /tmp/grok-codegen-8208d923/Generated.cs and add tests before use. public static class Temperature { public static double CelsiusToFahrenheit(double c) => c * 9.0 / 5.0 + 32.0; public static double FahrenheitToCelsius(double f) => (f - 32.0) * 5.0 / 9.0; } Enter fullscreen mode Exit fullscreen mode Building untrusted source is not a full sandbox—MSBuild can run build logic such as source generators and build targets—so run this in a disposable or sandboxed environment, cap the rounds (as above), and add a per-call timeout for anything beyond a demo.

The loop stops on the first clean build; a human still reviews and tests the result before it goes anywhere near production.

That is the last block. Your single Program.cs now holds the setup-and-dispatch code, all five use-case functions, and the two local helpers—run any case with dotnet run -- <case>, or dotnet run -- all for the full sequence.

Use larger reasoning models where they add value; check current pricing before sending large repositories or repeated multi-step calls. Model outputs are suggestions, not verified specifications, current documentation, or executable patches. Review security and architecture decisions and test generated code before production use. Foundry supports both Global Standard and US Data Zone Standard deployments; verify current region availability and data-residency requirements for your organization.

Grok 4.7 on Microsoft Foundry can assist with technical writing, architecture reviews, tool-assisted workflows and change-impact analysis. The workable.NET pattern is simple: deploy the model, point the OpenAI SDK at the Foundry resource's /openai/v1 endpoint with your deployment name, authenticate with Microsoft Entra ID, and verify every output against your own codebase and requirements.

Building AI features in C#? I write about practical, no-hype prompt engineering and Azure AI patterns for.NET developers. Check out Prompt Engineering for.NET Developers — free, no Python required. You can also subscribe for more posts.

Quelle: dev.to (dev.to). Original öffnen