.NET
C#
local data storage
programming
software development

How to store data locally in .NET C

Data Structures & Algorithms practice on Codemia

Step through 300+ coding interview problems with animated visualisers that show the data structure changing as the code runs. The Blind 75 are free.

Practice algorithms

Introduction

.NET C# offers several options for local data storage, from simple file I/O to embedded databases. The right choice depends on data complexity, size, and whether you need querying capabilities. Simple key-value settings use Properties.Settings or JSON files. Structured data uses SQLite or LiteDB. Binary serialization works for object graphs. This guide covers each approach with practical examples.

File-Based Storage (Text/JSON)

The simplest approach for small amounts of data:

csharp
1using System.IO;
2using System.Text.Json;
3
4// Write JSON to a file
5var data = new { Name = "Alice", Score = 95, Level = 3 };
6string json = JsonSerializer.Serialize(data, new JsonSerializerOptions { WriteIndented = true });
7File.WriteAllText("data.json", json);
8
9// Read JSON from a file
10string content = File.ReadAllText("data.json");
11var loaded = JsonSerializer.Deserialize<GameData>(content);
12
13public class GameData
14{
15    public string Name { get; set; }
16    public int Score { get; set; }
17    public int Level { get; set; }
18}

For plain text or CSV:

csharp
1// Write lines
2File.WriteAllLines("log.txt", new[] { "Line 1", "Line 2", "Line 3" });
3
4// Append a line
5File.AppendAllText("log.txt", "New entry\n");
6
7// Read all lines
8string[] lines = File.ReadAllLines("log.txt");

Application Settings (Properties.Settings)

Built-in settings for WinForms and WPF applications:

csharp
1// In Visual Studio: Project → Properties → Settings
2// Add a setting: "Username" (string), "MaxRetries" (int)
3
4// Write settings
5Properties.Settings.Default.Username = "Alice";
6Properties.Settings.Default.MaxRetries = 3;
7Properties.Settings.Default.Save();
8
9// Read settings
10string user = Properties.Settings.Default.Username;
11int retries = Properties.Settings.Default.MaxRetries;

Settings are stored in the user's AppData folder as XML. User-scoped settings persist between sessions; application-scoped settings are read-only at runtime.

SQLite (Structured Data)

For relational data with SQL querying:

bash
dotnet add package Microsoft.Data.Sqlite
csharp
1using Microsoft.Data.Sqlite;
2
3// Create or open a database file
4using var connection = new SqliteConnection("Data Source=app.db");
5connection.Open();
6
7// Create a table
8var createCmd = connection.CreateCommand();
9createCmd.CommandText = @"
10    CREATE TABLE IF NOT EXISTS Users (
11        Id INTEGER PRIMARY KEY AUTOINCREMENT,
12        Name TEXT NOT NULL,
13        Email TEXT UNIQUE,
14        CreatedAt TEXT DEFAULT CURRENT_TIMESTAMP
15    )";
16createCmd.ExecuteNonQuery();
17
18// Insert data
19var insertCmd = connection.CreateCommand();
20insertCmd.CommandText = "INSERT INTO Users (Name, Email) VALUES ($name, $email)";
21insertCmd.Parameters.AddWithValue("$name", "Alice");
22insertCmd.Parameters.AddWithValue("$email", "[email protected]");
23insertCmd.ExecuteNonQuery();
24
25// Query data
26var selectCmd = connection.CreateCommand();
27selectCmd.CommandText = "SELECT Id, Name, Email FROM Users";
28using var reader = selectCmd.ExecuteReader();
29while (reader.Read())
30{
31    Console.WriteLine($"{reader.GetInt64(0)}: {reader.GetString(1)} ({reader.GetString(2)})");
32}

SQLite with Entity Framework Core

For an ORM approach:

bash
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
csharp
1using Microsoft.EntityFrameworkCore;
2
3public class AppDbContext : DbContext
4{
5    public DbSet<User> Users { get; set; }
6
7    protected override void OnConfiguring(DbContextOptionsBuilder options)
8        => options.UseSqlite("Data Source=app.db");
9}
10
11public class User
12{
13    public int Id { get; set; }
14    public string Name { get; set; }
15    public string Email { get; set; }
16}
17
18// Usage
19using var db = new AppDbContext();
20db.Database.EnsureCreated();
21
22db.Users.Add(new User { Name = "Alice", Email = "[email protected]" });
23db.SaveChanges();
24
25var users = db.Users.Where(u => u.Name.Contains("Ali")).ToList();

LiteDB (NoSQL Document Store)

A lightweight embedded NoSQL database:

bash
dotnet add package LiteDB
csharp
1using LiteDB;
2
3public class User
4{
5    public int Id { get; set; }
6    public string Name { get; set; }
7    public string Email { get; set; }
8    public List<string> Tags { get; set; }
9}
10
11using var db = new LiteDatabase("app.db");
12var users = db.GetCollection<User>("users");
13
14// Insert
15users.Insert(new User { Name = "Alice", Email = "[email protected]", Tags = new() { "admin" } });
16
17// Query
18var admins = users.Find(u => u.Tags.Contains("admin")).ToList();
19
20// Update
21var user = users.FindOne(u => u.Name == "Alice");
22user.Email = "[email protected]";
23users.Update(user);

LiteDB stores data as BSON documents in a single file, requires no server, and supports LINQ queries.

Isolated Storage (UWP/Sandbox)

For sandboxed apps like UWP:

csharp
1// UWP local settings
2var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
3localSettings.Values["username"] = "Alice";
4string name = localSettings.Values["username"] as string;
5
6// UWP local file
7var folder = Windows.Storage.ApplicationData.Current.LocalFolder;
8var file = await folder.CreateFileAsync("data.txt", CreationCollisionOption.ReplaceExisting);
9await Windows.Storage.FileIO.WriteTextAsync(file, "Hello");

Common Pitfalls

  • Hardcoding file paths: Using absolute paths like C:\data\app.db breaks on other machines. Use Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) or AppContext.BaseDirectory for portable paths.
  • Not disposing database connections: SQLite connections and LiteDatabase instances must be disposed. Use using statements or using var declarations to prevent file locks and data corruption.
  • Concurrent file access without locking: Multiple threads writing to the same JSON or text file can cause data corruption. Use lock, SemaphoreSlim, or a database (SQLite has built-in concurrency handling) for multi-threaded scenarios.
  • Storing sensitive data in plain text: Passwords, API keys, and tokens should not be stored in JSON or text files. Use System.Security.Cryptography.ProtectedData (Windows DPAPI) or the platform's secure storage API.
  • Using BinaryFormatter for serialization: BinaryFormatter is deprecated in .NET 8+ due to security vulnerabilities. Use System.Text.Json, MessagePack, or protobuf-net for binary serialization instead.

Summary

  • Use JSON files (System.Text.Json + File.WriteAllText) for simple structured data
  • Use Properties.Settings for WinForms/WPF application preferences
  • Use SQLite (with or without EF Core) for relational data with SQL querying
  • Use LiteDB for document-oriented NoSQL storage in a single file
  • Store files in LocalApplicationData for portable paths across machines
  • Never store sensitive data in plain text — use platform-specific secure storage

Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Data Structures & Algorithms practice on Codemia

Step through 300+ coding interview problems with animated visualisers that show the data structure changing as the code runs. The Blind 75 are free.

Practice algorithms