-
SQLite本地数据库——C#编写桌面应用数据存储
第一部分:C#基础入门
SQLite本地数据库——桌面应用数据存储
实例介绍
SQLite是轻量级文件型数据库的代表,完美适配桌面应用(如WPF、WinForms、控制台):无需安装服务、单文件存储、跨平台支持、ACID兼容。本节通过“个人待办事项管理”场景,带你vb.net教程C#教程python教程SQL教程access 2010教程
掌握SQLite的EF Core集成、本地文件操作、数据备份/恢复、事务处理,让你快速实现桌面应用的本地数据存储,解决“数据存在哪里”的核心问题。
需求分析
设计一个本地待办事项管理工具,核心需求:
1.实体模型:待办项包含Id(主键)、Title(标题)、IsCompleted(完成状态)、CreateTime(创建时间);
2.CRUD操作:添加、查询(全部/按状态)、更新(完成状态)、删除待办项;
3.本地存储:数据库文件存放在用户目录(避免权限问题);
4.数据安全:支持手动备份/恢复数据库文件;
5.事务支持:批量添加待办项时用事务保证原子性;
6.跨平台兼容:同一代码可在Windows/macOS/Linux上运行。
代码实现
前置条件:
安装包:Microsoft.EntityFrameworkCore.Sqlite、Microsoft.Extensions.DependencyInjection;
目标框架:.NET 6+(跨平台支持)。
-
实体类定义
csharp
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
/// <summary>
/// 待办事项实体
/// </summary>
public class TodoItem {
/// <summary>
/// 主键:自增ID
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
/// <summary>
/// 待办标题:非空,最大长度100
/// </summary>
[Required]
[MaxLength(100)]
public string Title { get; set; } = string.Empty;
/// <summary>
/// 完成状态:默认未完成
/// </summary>
public bool IsCompleted { get; set; } = false;
/// <summary>
/// 创建时间:自动生成
/// </summary>
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime CreateTime { get; set; }
}
-
DbContext配置(SQLite专属)
csharp
using Microsoft.EntityFrameworkCore;
using System.IO;
/// <summary>
/// 待办事项数据库上下文
/// </summary>
public class TodoDbContext : DbContext {
/// <summary>
/// 数据库文件路径:用户目录下的todo.db(避免程序目录权限问题)
/// </summary>
private readonly string _dbPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
"todo.db"
);
/// <summary>
/// 待办项集合
/// </summary>
public DbSet<TodoItem> TodoItems { get; set; }
/// <summary>
/// 配置SQLite连接
/// </summary>
protected override void OnConfiguring(DbContextOptionsBuilder options) {
// SQLite连接字符串:Data Source指定文件路径
options.UseSqlite($"Data Source={_dbPath}")
.LogTo(Console.WriteLine) // 输出SQL日志(调试用)
.EnableSensitiveDataLogging(); // 显示敏感数据(调试用)
}
/// <summary>
/// 实体映射配置(SQLite特有优化)
/// </summary>
protected override void OnModelCreating(ModelBuilder model) {
model.Entity<TodoItem>(entity => {
// SQLite自增主键配置(可选,EF Core自动适配,但明确指定更稳妥)
entity.Property(e => e.Id)
.ValueGeneratedOnAdd();
// 创建时间默认值:SQLite的datetime('now')函数
entity.Property(e => e.CreateTime)
.HasDefaultValueSql("datetime('now')");
// 索引:优化按完成状态查询
entity.HasIndex(e => e.IsCompleted);
});
}
}
-
待办事项服务类(CRUD+备份/恢复)
csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using System.IO;
/// <summary>
/// 待办事项业务服务
/// </summary>
public class TodoService {
private readonly TodoDbContext _dbContext;
private readonly string _dbPath;
private readonly string _backupPath;
public TodoService(TodoDbContext dbContext) {
_dbContext = dbContext;
_dbPath = ((Microsoft.EntityFrameworkCore.Storage.RelationalConnection)_dbContext.Database.GetDbConnection()).ConnectionString.Split('=')[1];
_backupPath = Path.ChangeExtension(_dbPath, ".backup.db");
}
/// <summary>
/// 添加待办项
/// </summary>
public async Task AddTodoAsync(string title) {
var todo = new TodoItem { Title = title };
await _dbContext.TodoItems.AddAsync(todo);
await _dbContext.SaveChangesAsync();
Console.WriteLine($"✅ 添加待办:ID={todo.Id},标题={todo.Title}");
}
/// <summary>
/// 批量添加待办项(事务保证原子性)
/// </summary>
public async Task BulkAddTodosAsync(List<string> titles) {
using var transaction = await _dbContext.Database.BeginTransactionAsync();
try {
foreach (var title in titles) {
await _dbContext.TodoItems.AddAsync(new TodoItem { Title = title });
}
await _dbContext.SaveChangesAsync();
await transaction.CommitAsync();
Console.WriteLine($"✅ 批量添加成功:共{titles.Count}条");
} catch (Exception ex) {
await transaction.RollbackAsync();
Console.WriteLine($"❌ 批量添加失败:{ex.Message}");
}
}
/// <summary>
/// 查询所有待办项
/// </summary>
public async Task<List<TodoItem>> GetAllTodosAsync() {
return await _dbContext.TodoItems.AsNoTracking().ToListAsync();
}
/// <summary>
/// 更新待办项状态
/// </summary>
public async Task UpdateTodoStatusAsync(int id, bool isCompleted) {
var todo = await _dbContext.TodoItems.FindAsync(id);
if (todo == null) {
Console.WriteLine($"❌ 待办项ID={id}不存在");
return;
}
todo.IsCompleted = isCompleted;
await _dbContext.SaveChangesAsync();
Console.WriteLine($"✅ 更新待办:ID={id},状态={isCompleted}");
}
/// <summary>
/// 删除待办项
/// </summary>
public async Task DeleteTodoAsync(int id) {
var todo = await _dbContext.TodoItems.FindAsync(id);
if (todo == null) {
Console.WriteLine($"❌ 待办项ID={id}不存在");
return;
}
_dbContext.TodoItems.Remove(todo);
await _dbContext.SaveChangesAsync();
Console.WriteLine($"✅ 删除待办:ID={id}");
}
/// <summary>
/// 备份数据库
/// </summary>
public void BackupDatabase() {
// 关闭当前DbContext连接(避免文件被占用)
_dbContext.Database.CloseConnection();
File.Copy(_dbPath, _backupPath, overwrite: true);
_dbContext.Database.OpenConnection();
Console.WriteLine($"✅ 备份成功:{_backupPath}");
}
/// <summary>
/// 恢复数据库
/// </summary>
public void RestoreDatabase() {
if (!File.Exists(_backupPath)) {
Console.WriteLine($"❌ 备份文件不存在:{_backupPath}");
return;
}
_dbContext.Database.CloseConnection();
File.Copy(_backupPath, _dbPath, overwrite: true);
_dbContext.Database.OpenConnection();
Console.WriteLine($"✅ 恢复成功:{_dbPath}");
}
}
-
测试代码(桌面应用场景模拟)
csharp
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
class Program {
static async Task Main() {
// 依赖注入配置
var services = new ServiceCollection()
.AddDbContext<TodoDbContext>()
.AddScoped<TodoService>()
.BuildServiceProvider();
var todoService = services.GetRequiredService<TodoService>();
// 1. 添加单个待办
await todoService.AddTodoAsync("学习SQLite");
// 2. 批量添加待办
await todoService.BulkAddTodosAsync(new List<string> { "写代码", "测试功能", "备份数据" });
// 3. 查询所有待办
var allTodos = await todoService.GetAllTodosAsync();
Console.WriteLine(" 操作完成!");
}
}
- 实体类关键代码
- [Key] + [DatabaseGenerated(Identity)]:SQLite的自增主键需要此配置,确保Id自动生成且唯一; [Required] + [MaxLength]:约束Title非空且长度不超过100,避免无效数据; [DatabaseGenerated(Computed)]:标记CreateTime由数据库自动生成,配合后续的默认值配置。
- DbContext关键代码 UseSqlite连接字符串:Data Source=todo.db指定数据库文件路径,SQLite会自动创建该文件(如果不存在); HasDefaultValueSql("datetime('now')"):SQLite的日期函数,插入时自动填充当前时间; HasIndex(e => e.IsCompleted):为完成状态字段创建索引,加速按状态查询的效率。
- 服务类关键代码 BulkAddTodosAsync:使用事务保证批量添加的原子性,要么全部成功要么全部回滚; BackupDatabase:先关闭DbContext连接(避免文件锁定),再复制数据库文件到备份路径; RestoreDatabase:同理关闭连接,用备份文件替换原文件,实现数据恢复; AsNoTracking:查询时关闭跟踪,减少内存占用,提升只读场景性能。
- 测试代码关键逻辑 依赖注入:模拟桌面应用的依赖注入场景,解耦服务与DbContext; 批量添加:演示事务的使用,确保数据一致性; 备份/恢复:模拟用户手动操作,解决数据丢失问题。 基础知识拓展
- SQLite核心特性 文件型数据库:整个数据库存储在单个文件中,便于传输和备份; ACID兼容:支持原子性、一致性、隔离性、持久性,保证数据安全; 跨平台:支持Windows/macOS/Linux/Android/iOS,同一文件可在不同系统上使用; 轻量级:库文件仅几百KB,运行时无需安装服务; SQL支持:兼容大部分标准SQL语法,如SELECT/INSERT/UPDATE/DELETE、索引、事务等。
- 桌面应用最佳实践 数据库文件位置:存放在用户目录(如Environment.SpecialFolder.UserProfile),避免程序目录的权限问题; 备份策略:定期自动备份+手动备份,备份文件可压缩存储; 并发处理:SQLite默认是文件级锁,多线程写入时建议用单线程队列或异步锁; 性能优化: 为查询频繁的字段创建索引; 批量操作时用事务减少IO次数; 只读查询用AsNoTracking关闭跟踪; 安全防护:用参数化查询(EF Core自动支持)防止SQL注入,敏感数据加密存储。
- SQLite局限性 并发写入限制:单文件锁导致高并发写入性能下降,适合低并发场景; 大数据量处理:单个文件过大(如超过1GB)时,查询和备份速度变慢; 功能限制:不支持存储过程、触发器(部分支持)、外键约束(需手动开启)。 总结 SQLite是桌面应用本地存储的首选方案:轻量级、无依赖、跨平台、易备份。通过EF Core集成,你可以用熟悉的LINQ语法操作SQLite,无需编写复杂的SQL语句。本节的待办事项案例覆盖了CRUD、事务、备份/恢复等核心场景,完全满足桌面应用的日常需求。 关键Takeaway:桌面应用使用SQLite时,重点关注文件位置、备份策略、事务原子性和性能优化。掌握这些技巧,你可以快速构建稳定、可靠的本地数据存储功能。
本站原创,转载请注明出处:https://www.xin3721.com/ArticlecSharp/c49445.html
最新更新
C#异步方法最佳实践——避免死锁
SQLite本地数据库——C#编写桌面应用数据
C#操作MySQL数据库——跨数据库适配
C#操作MySQL数据库——跨数据库适配
操作MySQL数据库——跨数据库适配
EF Core性能优化——C#编写查询缓存与索引
EF Core迁移——c#开发数据库版本控制
EF Core关联查询——C#编写订单与用户数据
EF Core CRUD——c#处理用户信息管理
EF Core基础映射——C#编写实体与表关联
SQL SERVER中递归
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
uniapp/H5 获取手机桌面壁纸 (静态壁纸)
[前端] DNS解析与优化
为什么在js中需要添加addEventListener()?
JS模块化系统
js通过Object.defineProperty() 定义和控制对象
这是目前我见过最好的跨域解决方案!
减少回流与重绘
减少回流与重绘
如何使用KrpanoToolJS在浏览器切图
performance.now() 与 Date.now() 对比










