Implementing AI-Powered Code Generation and Assistance in .NET Applications

Summary: This post explores how to integrate AI-powered code generation and assistance capabilities into .NET applications. Learn how to leverage large language models to create tools that can generate code, provide intelligent code completion, and assist developers throughout the development lifecycle.

Introduction

Artificial Intelligence has revolutionized many aspects of software development, and one of the most impactful applications is AI-powered code generation and assistance. These tools can significantly enhance developer productivity by automating repetitive coding tasks, providing intelligent suggestions, and even generating entire code blocks or functions based on natural language descriptions.

In this post, we’ll explore how to implement AI-powered code generation and assistance capabilities in .NET applications. We’ll cover everything from integrating with large language models (LLMs) to building specialized tools for code completion, refactoring, documentation generation, and more. By the end of this article, you’ll have the knowledge to create your own AI-powered development tools that can boost productivity and code quality.

Understanding AI-Powered Code Generation

Before diving into implementation, let’s understand the key concepts behind AI-powered code generation and assistance.

What is AI-Powered Code Generation?

AI-powered code generation refers to the use of artificial intelligence, particularly large language models, to automatically generate code based on various inputs such as:

  1. Natural Language Descriptions: Converting plain English requirements into functional code
  2. Code Comments: Generating implementation based on function signatures and comments
  3. Partial Code: Completing code snippets or functions based on context
  4. Test Cases: Creating implementations that satisfy given test cases
  5. Design Patterns: Applying common design patterns to specific use cases

Types of AI Code Assistance

AI code assistance can take many forms, including:

  • Code Completion: Suggesting the next lines of code as you type
  • Code Generation: Creating entire functions or classes based on descriptions
  • Code Refactoring: Suggesting improvements to existing code
  • Bug Detection: Identifying potential issues in code
  • Documentation Generation: Creating documentation from code or vice versa
  • Test Generation: Creating unit tests for existing code
  • Code Explanation: Providing natural language explanations of complex code

Benefits and Limitations

AI-powered code tools offer several benefits:

  • Increased Productivity: Automating repetitive coding tasks
  • Reduced Boilerplate: Generating common patterns and structures
  • Knowledge Sharing: Providing access to best practices
  • Learning Aid: Helping developers learn new languages or frameworks
  • Consistency: Maintaining coding standards across projects

However, they also have limitations:

  • Code Quality Varies: Generated code may not always follow best practices
  • Context Understanding: AI may miss project-specific context
  • Security Concerns: Generated code might introduce vulnerabilities
  • Dependency on Training Data: Limited by what the model has been trained on
  • Need for Review: Human review is still essential

Setting Up the Development Environment

Let’s start by setting up our development environment for building AI-powered code tools.

Prerequisites

To follow along with this tutorial, you’ll need:

  • Visual Studio 2022 or Visual Studio Code
  • .NET 8 SDK
  • An Azure subscription
  • Access to Azure OpenAI Service or OpenAI API
  • Basic understanding of C# and .NET development

Creating a New Project

Let’s create a new .NET project for our AI code assistant:

bash

dotnet new console -n AICodeAssistant
cd AICodeAssistant

Installing Required Packages

Add the necessary packages to your project:

bash

dotnet add package Azure.AI.OpenAI
dotnet add package Microsoft.Extensions.Configuration
dotnet add package Microsoft.Extensions.Configuration.Json
dotnet add package Microsoft.Extensions.Configuration.EnvironmentVariables
dotnet add package Microsoft.Extensions.DependencyInjection
dotnet add package Microsoft.Extensions.Logging
dotnet add package Microsoft.Extensions.Logging.Console
dotnet add package Microsoft.CodeAnalysis.CSharp

Setting Up Configuration

Create an appsettings.json file to store your configuration:

json

{
  "AzureOpenAI": {
    "Endpoint": "https://your-openai-service.openai.azure.com/",
    "Key": "your-openai-api-key",
    "DeploymentName": "your-gpt-4-deployment"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning"
    }
  }
}

Creating a Configuration Helper

Let’s create a helper class to load our configuration:

csharp

// Configuration/ConfigurationHelper.cs
using Microsoft.Extensions.Configuration;

namespace AICodeAssistant.Configuration
{
    public static class ConfigurationHelper
    {
        public static IConfiguration GetConfiguration( )
        {
            return new ConfigurationBuilder()
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                .AddEnvironmentVariables()
                .Build();
        }
    }
}

Building Core AI Code Generation Services

Now, let’s implement the core services for AI-powered code generation.

Creating the AI Service

First, let’s create a service to interact with the AI model:

csharp

// Services/AIService.cs
using Azure;
using Azure.AI.OpenAI;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System.Text;

namespace AICodeAssistant.Services
{
    public class AIService
    {
        private readonly OpenAIClient _client;
        private readonly string _deploymentName;
        private readonly ILogger<AIService> _logger;

        public AIService(IConfiguration configuration, ILogger<AIService> logger)
        {
            _logger = logger;
            
            var endpoint = new Uri(configuration["AzureOpenAI:Endpoint"]);
            var key = configuration["AzureOpenAI:Key"];
            _deploymentName = configuration["AzureOpenAI:DeploymentName"];
            
            _client = new OpenAIClient(endpoint, new AzureKeyCredential(key));
        }

        public async Task<string> GenerateCompletionAsync(
            string prompt, 
            float temperature = 0.2f, 
            int maxTokens = 2000)
        {
            try
            {
                _logger.LogInformation("Generating completion for prompt");
                
                var chatCompletionsOptions = new ChatCompletionsOptions
                {
                    Temperature = temperature,
                    MaxTokens = maxTokens,
                    NucleusSamplingFactor = 0.95f, // Top-p
                    FrequencyPenalty = 0,
                    PresencePenalty = 0
                };
                
                // Add system message
                chatCompletionsOptions.Messages.Add(new ChatMessage(ChatRole.System, 
                    "You are an expert .NET developer assistant specialized in generating high-quality, " +
                    "secure, and efficient C# code. Provide only the code without explanations unless " +
                    "specifically asked for explanations."));
                
                // Add user message
                chatCompletionsOptions.Messages.Add(new ChatMessage(ChatRole.User, prompt));
                
                // Get completion
                var response = await _client.GetChatCompletionsAsync(_deploymentName, chatCompletionsOptions);
                var completion = response.Value;
                
                return completion.Choices[0].Message.Content;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error generating completion");
                throw;
            }
        }

        public async Task<string> GenerateStreamingCompletionAsync(
            string prompt, 
            Action<string> onPartialResponse,
            float temperature = 0.2f, 
            int maxTokens = 2000)
        {
            try
            {
                _logger.LogInformation("Generating streaming completion for prompt");
                
                var chatCompletionsOptions = new ChatCompletionsOptions
                {
                    Temperature = temperature,
                    MaxTokens = maxTokens,
                    NucleusSamplingFactor = 0.95f, // Top-p
                    FrequencyPenalty = 0,
                    PresencePenalty = 0
                };
                
                // Add system message
                chatCompletionsOptions.Messages.Add(new ChatMessage(ChatRole.System, 
                    "You are an expert .NET developer assistant specialized in generating high-quality, " +
                    "secure, and efficient C# code. Provide only the code without explanations unless " +
                    "specifically asked for explanations."));
                
                // Add user message
                chatCompletionsOptions.Messages.Add(new ChatMessage(ChatRole.User, prompt));
                
                // Get streaming completion
                var response = await _client.GetChatCompletionsStreamingAsync(_deploymentName, chatCompletionsOptions);
                var responseBuilder = new StringBuilder();
                
                await foreach (var update in response)
                {
                    if (update.ContentUpdate != null)
                    {
                        responseBuilder.Append(update.ContentUpdate);
                        onPartialResponse(update.ContentUpdate);
                    }
                }
                
                return responseBuilder.ToString();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error generating streaming completion");
                throw;
            }
        }
    }
}

Creating the Code Generation Service

Now, let’s create a specialized service for code generation:

csharp

// Services/CodeGenerationService.cs
using Microsoft.Extensions.Logging;
using System.Text;

namespace AICodeAssistant.Services
{
    public class CodeGenerationService
    {
        private readonly AIService _aiService;
        private readonly ILogger<CodeGenerationService> _logger;

        public CodeGenerationService(AIService aiService, ILogger<CodeGenerationService> logger)
        {
            _aiService = aiService;
            _logger = logger;
        }

        public async Task<string> GenerateCodeFromDescriptionAsync(
            string description, 
            string language = "csharp", 
            string framework = ".NET 8")
        {
            _logger.LogInformation("Generating code from description");
            
            var prompt = new StringBuilder();
            prompt.AppendLine($"Generate {language} code for the following description:");
            prompt.AppendLine();
            prompt.AppendLine(description);
            prompt.AppendLine();
            prompt.AppendLine($"Use {framework} and follow best practices for clean, efficient, and secure code.");
            prompt.AppendLine("Include appropriate error handling, comments, and documentation.");
            
            return await _aiService.GenerateCompletionAsync(prompt.ToString(), 0.2f);
        }

        public async Task<string> CompleteCodeSnippetAsync(string partialCode, string language = "csharp")
        {
            _logger.LogInformation("Completing code snippet");
            
            var prompt = new StringBuilder();
            prompt.AppendLine($"Complete the following {language} code snippet:");
            prompt.AppendLine("```");
            prompt.AppendLine(partialCode);
            prompt.AppendLine("```");
            prompt.AppendLine();
            prompt.AppendLine("Provide the complete implementation following best practices for clean, efficient, and secure code.");
            
            return await _aiService.GenerateCompletionAsync(prompt.ToString(), 0.2f);
        }

        public async Task<string> GenerateUnitTestsAsync(string codeToTest, string testFramework = "xUnit")
        {
            _logger.LogInformation("Generating unit tests");
            
            var prompt = new StringBuilder();
            prompt.AppendLine($"Generate {testFramework} unit tests for the following C# code:");
            prompt.AppendLine("```");
            prompt.AppendLine(codeToTest);
            prompt.AppendLine("```");
            prompt.AppendLine();
            prompt.AppendLine("Provide comprehensive test coverage with appropriate test cases, assertions, and mocking where necessary.");
            
            return await _aiService.GenerateCompletionAsync(prompt.ToString(), 0.3f);
        }

        public async Task<string> RefactorCodeAsync(string codeToRefactor, string refactoringGoal)
        {
            _logger.LogInformation("Refactoring code");
            
            var prompt = new StringBuilder();
            prompt.AppendLine("Refactor the following C# code:");
            prompt.AppendLine("```");
            prompt.AppendLine(codeToRefactor);
            prompt.AppendLine("```");
            prompt.AppendLine();
            prompt.AppendLine($"Refactoring goal: {refactoringGoal}");
            prompt.AppendLine();
            prompt.AppendLine("Provide the refactored code with improvements that address the refactoring goal while maintaining the original functionality.");
            
            return await _aiService.GenerateCompletionAsync(prompt.ToString(), 0.2f);
        }

        public async Task<string> GenerateDocumentationAsync(string codeToDocument)
        {
            _logger.LogInformation("Generating documentation");
            
            var prompt = new StringBuilder();
            prompt.AppendLine("Generate comprehensive XML documentation comments for the following C# code:");
            prompt.AppendLine("```");
            prompt.AppendLine(codeToDocument);
            prompt.AppendLine("```");
            prompt.AppendLine();
            prompt.AppendLine("Include detailed descriptions, parameter explanations, return value documentation, and exception documentation where appropriate.");
            
            return await _aiService.GenerateCompletionAsync(prompt.ToString(), 0.2f);
        }

        public async Task<string> ExplainCodeAsync(string codeToExplain)
        {
            _logger.LogInformation("Explaining code");
            
            var prompt = new StringBuilder();
            prompt.AppendLine("Explain the following C# code in detail:");
            prompt.AppendLine("```");
            prompt.AppendLine(codeToExplain);
            prompt.AppendLine("```");
            prompt.AppendLine();
            prompt.AppendLine("Provide a comprehensive explanation including:");
            prompt.AppendLine("1. Overall purpose of the code");
            prompt.AppendLine("2. Breakdown of key components and their functions");
            prompt.AppendLine("3. Any design patterns or important techniques used");
            prompt.AppendLine("4. Potential performance considerations");
            prompt.AppendLine("5. Any security implications");
            
            return await _aiService.GenerateCompletionAsync(prompt.ToString(), 0.3f, 3000);
        }