Welcome to the cutting edge. In this comprehensive article, I show how I combine AI tools and prompt engineering techniques to create a fully functional, advanced C# database solution using EF Core, Dapper, and ADO.NET. This article covers not only the architecture and benchmarks but also provides the full C# repository class, SQL database scripts, and the exact prompts I used to generate each part.
This is the ultimate article for using AI as your coding partner, from zero to production-ready code.
Vibe Coding Workflow: Multi-Prompt Strategy
Instead of relying on a single prompt, I break the problem down into focused layers. Here’s how I explain the AI step by step.
Prompt 1. Generate Base Architecture
- Prompt: Design a clean architecture C# solution for a multi-layered .NET app that uses EF Core for CRUD, Dapper for read-heavy queries, and ADO.NET for bulk operations. Include project structure, folder layout, and key interfaces.
- AI Result: The AI provided a project structure with Presentation (Web API), Application (Services, Use Cases), Domain (Entities, Interfaces), Infrastructure (Repositories, Data Context), and Tests. Solid foundation.
Prompt 2. Write Repository Layer
- Prompt: Generate a C# repository class called AdvancedStudentRepository that implements IStudentRepository, integrating EF Core compiled queries, Dapper queries, and ADO.NET TVPs, with async/await, cancellation tokens, ILogger logging, and DI constructor.
- AI Result: See the full class below.
Full Example C# Class: AdvancedStudentRepository
using System.Data;
using System.Data.SqlClient;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using Dapper;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
public class AdvancedStudentRepository : IStudentRepository
{
private readonly AppDbContext _context;
private readonly IDbConnection _connection;
private readonly ILogger<AdvancedStudentRepository> _logger;
private static readonly Func<AppDbContext, int, Task<Student?>> _compiledGetById =
EF.CompileAsyncQuery((AppDbContext ctx, int id) => ctx.Students.FirstOrDefault(s => s.Id == id));
public AdvancedStudentRepository(AppDbContext context, string connectionString, ILogger<AdvancedStudentRepository> logger)
{
_context = context;
_connection = new SqlConnection(connectionString);
_logger = logger;
}
public async Task<Student?> GetStudentByIdEfCoreAsync(int id, CancellationToken cancellationToken)
{
try
{
return await _compiledGetById(_context, id);
}
catch (Exception ex)
{
_logger.LogError(ex, "EF Core error fetching student with ID {Id}", id);
throw;
}
}
public async Task<IEnumerable<Student>> GetActiveStudentsDapperAsync(CancellationToken cancellationToken)
{
try
{
const string sql = "SELECT * FROM Students WHERE IsActive = 1";
return await _connection.QueryAsync<Student>(sql);
}
catch (Exception ex)
{
_logger.LogError(ex, "Dapper error fetching active students");
throw;
}
}
public async Task<IEnumerable<Student>> GetStudentsByIdsAdoNetAsync(List<int> ids, CancellationToken cancellationToken)
{
try
{
var table = new DataTable();
table.Columns.Add("Id", typeof(int));
ids.ForEach(id => table.Rows.Add(id));
using var cmd = new SqlCommand("GetStudentsByIds", (SqlConnection)_connection)
{
CommandType = CommandType.StoredProcedure
};
cmd.Parameters.AddWithValue("@Ids", table);
var students = new List<Student>();
await _connection.OpenAsync(cancellationToken);
using var reader = await cmd.ExecuteReaderAsync(cancellationToken);
while (await reader.ReadAsync(cancellationToken))
{
students.Add(new Student
{
Id = reader.GetInt32(reader.GetOrdinal("Id")),
FirstName = reader.GetString(reader.GetOrdinal("FirstName")),
LastName = reader.GetString(reader.GetOrdinal("LastName")),
IsActive = reader.GetBoolean(reader.GetOrdinal("IsActive"))
});
}
await _connection.CloseAsync();
return students;
}
catch (Exception ex)
{
_logger.LogError(ex, "ADO.NET error fetching students by IDs");
throw;
}
}
}
This class brings together EF Core, Dapper, and ADO.NET in one seamless, production-ready design.
Prompt 3. Provide SQL Database Scripts
Prompt: Write SQL Server scripts to create the Students table and define dbo.IdList table type, and create a stored procedure GetStudentsByIds that accepts a TVP and returns matching students.
AI Result
CREATE TABLE Students (
Id INT PRIMARY KEY,
FirstName NVARCHAR(100),
LastName NVARCHAR(100),
IsActive BIT
);
CREATE TYPE dbo.IdList AS TABLE (Id INT);
CREATE PROCEDURE GetStudentsByIds @Ids dbo.IdList READONLY
AS
BEGIN
SELECT * FROM Students WHERE Id IN (SELECT Id FROM @Ids);
END;
Now the ADO.NET TVP queries work end-to-end.
Prompt 4. Build Benchmark Harness
- Prompt: Create a BenchmarkDotNet project comparing EF Core, Dapper, and ADO.NET execution times for the same student query. Include setup, teardown, and formatted results.
- Result: I now have a benchmark harness providing real metrics.
Prompt 5. Generate Documentation + Deployment Guide
- Prompt: Write a README.md explaining the architecture, setup steps, benchmarks, when to use EF Core vs Dapper vs ADO.NET, and Docker deployment instructions."
- Result: The AI delivered a clear, shareable README covering all major points.
Final Takeaway
By using smart, layered prompt engineering, I was able to generate a full-stack, production-grade C# database solution that includes.
- Clean architecture
- Full repository class
- Supporting SQL scripts
- Benchmarks and diagnostics
- Deployment and documentation