.NET
C#
local data storage
programming
software development

How to store data locally in .NET C

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

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

Related reading
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
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.