Skip to content

Introduction to Databases in CSharp

While saving data to files is simple and effective for small projects, it becomes less practical as applications grow.

Use a database, especially if …

  • multiple users need access
  • you must ensure data integrity
  • you want to query data efficiently

A relational database stores data in tables that have relationships (for example: students enrolled in courses).

Each table consists of rows and columns. The language used to interact with relational databases is SQL (Structured Query Language).


C# provides several mechanisms to interact with databases, each with its own abstraction level and trade-offs.

  • What it is: The fundamental data access technology in .NET.
  • How it works: You manually write SQL commands and use classes like SqlConnection, SqlCommand, and SqlDataReader.
  • Advantages:
    • Full control over SQL and performance.
    • No external dependencies beyond .NET.
  • Disadvantages:
    • Verbose code.
    • Easy to introduce SQL injection if not careful.
    • Requires more boilerplate and manual mapping of results to objects.
  • What it is: A lightweight abstraction built on top of ADO.NET.
  • How it works: You still write SQL queries, but Dapper maps query results directly to C# objects.
  • Advantages:
    • Extremely fast and simple.
    • Minimal setup.
    • Keeps SQL visibility.
  • Disadvantages:
    • Still requires SQL writing.
    • No automatic schema management or tracking of relationships.
  • What it is: A high-level Object-Relational Mapper (ORM).
  • How it works: You work with C# classes (entities), and EF Core handles the SQL generation and mapping automatically.
  • Advantages:
    • Very productive and easy to use.
    • Supports migrations, LINQ queries, and complex relationships.
    • Tracks changes to objects automatically.
  • Disadvantages:
    • More complex under the hood.
    • Can lead to slower queries.
    • Less control over SQL.
    • Harder to debug for beginners.

We’ll begin with ADO.NET because it helps you understand what’s really happening when an application interacts with a database.

  • SqlConnection - manages the connection to the database.
  • SqlCommand - represents a SQL statement or stored procedure.
  • SqlDataReader - reads rows returned by a query.
  • SqlParameter - prevents SQL injection by safely passing parameters.

To use these classes, install the MySQL Connector/NET NuGet package:

Terminal window
dotnet add package MySql.Data

Note: Depending on the database you need a different connector.


using System;
using MySql.Data.MySqlClient;
class Program
{
static void Main()
{
string connectionString = "Server=localhost;Database=SchoolDb;User ID=root;Password=yourpassword;";
using (var connection = new MySqlConnection(connectionString))
{
connection.Open();
Console.WriteLine("Connection established successfully!");
}
}
}

This example connects to a local MySql database on localhost. If you’re using SQLite, you can use Microsoft.Data.Sqlite and a file-based connection string such as: Data Source=school.db


The following samples show how to perform simple CRUD (Create, Read, Update, Delete) operations.

public void AddStudent(Student student)
{
using var connection = new MySqlConnection(_connectionString);
connection.Open();
string sql = "INSERT INTO Students (Id, Name, Email) VALUES (@Id, @Name, @Email)";
using var command = new MySqlCommand(sql, connection);
command.Parameters.AddWithValue("@Id", student.Id);
command.Parameters.AddWithValue("@Name", student.Name);
command.Parameters.AddWithValue("@Email", student.Email);
command.ExecuteNonQuery();
}
public Student? GetById(string id)
{
using var connection = new MySqlConnection(_connectionString);
connection.Open();
string sql = "SELECT Id, Name, Email FROM Students WHERE Id = @Id";
using var command = new MySqlCommand(sql, connection);
command.Parameters.AddWithValue("@Id", id);
using var reader = command.ExecuteReader();
if (reader.Read())
{
return new Student
{
Id = reader.GetString("Id"),
Name = reader.GetString("Name"),
Email = reader.GetString("Email")
};
}
return null;
}
public void Update(Student student)
{
using var connection = new MySqlConnection(_connectionString);
connection.Open();
string sql = "UPDATE Students SET Name = @Name, Email = @Email WHERE Id = @Id";
using var command = new MySqlCommand(sql, connection);
command.Parameters.AddWithValue("@Id", student.Id);
command.Parameters.AddWithValue("@Name", student.Name);
command.Parameters.AddWithValue("@Email", student.Email);
command.ExecuteNonQuery();
}
public void Delete(string id)
{
using var connection = new MySqlConnection(_connectionString);
connection.Open();
string sql = "DELETE FROM Students WHERE Id = @Id";
using var command = new MySqlCommand(sql, connection);
command.Parameters.AddWithValue("@Id", id);
command.ExecuteNonQuery();
}
public IEnumerable<Student> GetAll()
{
var students = new List<Student>();
using var connection = new MySqlConnection(_connectionString);
connection.Open();
string sql = "SELECT Id, Name, Email FROM Students";
using var command = new MySqlCommand(sql, connection);
using var reader = command.ExecuteReader();
while (reader.Read())
{
students.Add(new Student
{
Id = reader.GetString("Id"),
Name = reader.GetString("Name"),
Email = reader.GetString("Email")
});
}
return students;
}

TechnologyAbstraction LevelManual SQLAuto MappingBest For
ADO.NETLow✅ Yes❌ NoFull control
DapperMedium✅ Yes✅ YesFast, simple apps. Using SQL directly
Entity Framework CoreHigh❌ No✅ YesProductivity, larger projects