VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > c#编程 >
  • C#操作SQLite本地数据库——桌面应用数据存储

第一部分:C#基础入门
10.SQLite本地数据库——桌面应用数据存储
实例介绍
你有没有做过这样的桌面小工具?比如本地记事本、个人通讯录、任务管理器,或者小型图片浏览器。这些工具不需要联网,数据只存在本地,但要求关闭应用后数据不丢失,而且查询要快。
举个真实例子:我之前做过一个本地任务管理工具,用户可以添加、修改、删除任务,标记完成状态。一开始用JSON文件存储数据,结果任务多了(比如1000条以上),查询和排序变得很慢;而且修改数据时要整个文件读写,容易丢失数据。后来换成SQLite,不仅查询速度提升了10倍以上,还支持事务操作,数据安全多了。
这一节,我们就用这个任务管理工具的场景,带你掌握SQLite在桌面应用中的核vb.net教程C#教程python教程SQL教程access 2010教程心用法:数据持久化、CRUD操作、事务支持、跨平台适配,让你快速上手桌面应用的本地数据存储。
需求分析
桌面应用选择SQLite的核心原因:轻量级(只有一个文件)、无需安装服务、跨平台、查询快。我们的任务管理工具需要解决以下问题:
1.数据持久化:关闭应用后任务数据不丢失;
2.任务CRUD:添加、修改、删除、查询任务(按状态、截止日期筛选);
3.事务安全:批量操作(比如删除多个任务)时,要么全部成功,要么全部失败;
4.跨平台运行:在Windows、macOS、Linux上都能使用;
5.性能优化:本地查询快,即使有1000条任务也不卡顿;
6.路径安全:数据库文件存放在用户可读写的目录(避免程序目录无权限问题)。
目标:用最少的代码实现一个稳定、高效的本地任务管理工具,数据存储部分完全基于SQLite。
代码实现
前置条件:.NET 6+、EF Core 7+;需安装NuGet包:Microsoft.EntityFrameworkCore.Sqlite;
桌面应用类型:WPF、WinForms、MAUI(跨平台桌面)均可,本节以通用控制台示例为主,适配所有桌面框架。
准备工作:安装依赖与配置
步骤1:安装SQLite Provider
bash
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
步骤2:定义任务实体与DbContext
csharp

	// 任务实体
	public class TaskItem
	{
	public int Id { get; set; } // 主键,自增
	public string Title { get; set; } = null!; // 任务标题
	public string? Description { get; set; } // 任务描述(可选)
	public DateTime DueDate { get; set; } // 截止日期
	public bool IsCompleted { get; set; } // 是否完成
	public DateTime CreatedAt { get; set; } = DateTime.UtcNow; // 创建时间(UTC)
	}
	
	// SQLite DbContext配置
	public class TaskDbContext : DbContext
	{
	public DbSet<TaskItem> Tasks { get; set; } = null!; // 任务表
	
	protected override void OnConfiguring(DbContextOptionsBuilder options)
	{
	// 关键:数据库文件路径选择用户可读写的目录
	var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
	var dbDir = Path.Combine(appDataPath, "TaskManager");
	Directory.CreateDirectory(dbDir); // 确保目录存在
	var dbPath = Path.Combine(dbDir, "TaskManager.db");
	
	// 配置SQLite连接字符串:Data Source指定文件路径
	options.UseSqlite($"Data Source={dbPath}")
	.LogTo(Console.WriteLine) // 日志输出生成的SQL(调试用)
	.EnableSensitiveDataLogging(); // 显示敏感数据(如参数值,生产环境关闭)
	}
	
	protected override void OnModelCreating(ModelBuilder modelBuilder)
	{
	// 对TaskItem的DueDate字段创建索引,加快按截止日期查询的速度
	modelBuilder.Entity<TaskItem>()
	.HasIndex(t => t.DueDate)
	.HasDatabaseName("IX_Tasks_DueDate");
	}
	}

场景1:初始化数据库(创建表)
错误写法:手动写SQL创建表,容易出错且难以维护
csharp

	// ❌错误:手动执行SQL创建表,字段变更时需要手动修改
	public async Task CreateTableWrong()
	{
	using var db = new TaskDbContext();
	await db.Database.ExecuteSqlRawAsync(@"
	CREATE TABLE IF NOT EXISTS Tasks (
	Id INTEGER PRIMARY KEY AUTOINCREMENT,
	Title TEXT NOT NULL,
	Description TEXT,
	DueDate DATETIME NOT NULL,
	IsCompleted BOOLEAN NOT NULL,
	CreatedAt DATETIME NOT NULL
	)
	");
	}

正确写法:用EF Core迁移自动创建表
bash

	# 步骤1:生成迁移(记录表结构变更)
	dotnet ef migrations add InitialCreate --context TaskDbContext
	
	# 步骤2:应用迁移(创建表)
	dotnet ef database update --context TaskDbContext

执行结果:
生成Migrations文件夹,包含迁移文件;
在用户AppData目录下创建TaskManager.db文件;
自动创建Tasks表和__EFMigrationsHistory表(记录迁移历史)。
场景2:任务CRUD操作(核心功能)
2.1 添加任务
csharp

	// ✅正确:添加任务,EF Core自动处理插入逻辑
	public async Task AddTaskAsync(string title, string? description, DateTime dueDate)
	{
	var task = new TaskItem
	{
	Title = title,
	Description = description,
	DueDate = dueDate,
	IsCompleted = false // 默认未完成
	};
	
	using var db = new TaskDbContext();
	await db.Tasks.AddAsync(task); // 标记实体为新增
	await db.SaveChangesAsync(); // 执行插入SQL
	}

2.2 查询任务(按状态筛选)
csharp

	// ✅正确:查询未完成的任务,按截止日期排序
	public async Task<List<TaskItem>> GetPendingTasksAsync()
	{
	using var db = new TaskDbContext();
	return await db.Tasks
	.Where(t => !t.IsCompleted) // 筛选未完成
	.OrderBy(t => t.DueDate) // 按截止日期升序
	.ToListAsync(); // 执行查询
	}

2.3 修改任务(标记为完成)
csharp

	// ✅正确:修改任务状态,EF Core自动生成更新SQL
	public async Task MarkTaskAsCompletedAsync(int taskId)
	{
	using var db = new TaskDbContext();
	var task = await db.Tasks.FindAsync(taskId); // 根据主键快速查询
	if (task != null)
	{
	task.IsCompleted = true; // 修改实体状态
	await db.SaveChangesAsync(); // 执行更新SQL
	}
	}

2.4 删除任务
csharp

	// ✅正确:删除任务,EF Core自动生成删除SQL
	public async Task DeleteTaskAsync(int taskId)
	{
	using var db = new TaskDbContext();
	var task = await db.Tasks.FindAsync(taskId);
	if (task != null)
	{
	db.Tasks.Remove(task); // 标记实体为删除
	await db.SaveChangesAsync(); // 执行删除SQL
	}
	}

场景3:事务操作(批量删除任务)
错误写法:无事务,批量操作时容易数据不一致
csharp

	// ❌错误:批量删除时无事务,部分删除成功部分失败,导致数据混乱
	public async Task BatchDeleteWrong(List<int> taskIds)
	{
	using var db = new TaskDbContext();
	foreach (var id in taskIds)
	{
	var task = await db.Tasks.FindAsync(id);
	if (task != null)
	{
	db.Tasks.Remove(task);
	await db.SaveChangesAsync(); // 每次删除都提交,无事务
	}
	}
	}

正确写法:用事务保证批量操作原子性
csharp

	// ✅正确:事务操作,要么全部删除成功,要么全部回滚
	public async Task BatchDeleteTasksAsync(List<int> taskIds)
	{
	using var db = new TaskDbContext();
	using var transaction = await db.Database.BeginTransactionAsync(); // 开启事务
	
	try
	{
	// 一次性查询所有要删除的任务
	var tasks = await db.Tasks.Where(t => taskIds.Contains(t.Id)).ToListAsync();
	db.Tasks.RemoveRange(tasks); // 批量标记为删除
	await db.SaveChangesAsync(); // 执行删除SQL
	await transaction.CommitAsync(); // 提交事务
	}
	catch (Exception ex)
	{
	await transaction.RollbackAsync(); // 回滚事务
	Console.WriteLine($"批量删除失败:{ex.Message}");
	throw; // 重新抛出异常,让上层处理
	}
	}
场景4:跨平台适配(Windows/macOS/Linux)
正确写法:利用SQLite跨平台特性,代码无需修改
csharp 
	// ✅正确:跨平台运行,数据库文件路径自动适配不同系统
	public string GetDbPathCrossPlatform()
	{
	string appDataPath;
	if (OperatingSystem.IsWindows())
	{
	appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
	}
	else if (OperatingSystem.IsMacOS())
	{
	appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Application Support");
	}
	else // Linux
	{
	appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config");
	}
	var dbDir = Path.Combine(appDataPath, "TaskManager");
	Directory.CreateDirectory(dbDir);
	return Path.Combine(dbDir, "TaskManager.db");
	}

说明:.NET 6+提供OperatingSystem.IsWindows()等方法,可轻松适配不同系统的目录结构,SQLite数据库文件在各系统下都能正常读写。
逐行讲解
准备工作:DbContext配置
1.数据库路径选择:Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)获取用户AppData目录(Windows),避免程序目录无写入权限的问题;
2.Directory.CreateDirectory(dbDir):确保目录存在,防止文件创建失败;
3.UseSqlite连接字符串:Data Source=TaskManager.db指定数据库文件路径,SQLite会自动创建该文件(如果不存在);
4.OnModelCreating:对DueDate字段创建索引,加快按截止日期查询的速度(索引是性能优化的关键)。
场景1:迁移初始化
dotnet ef migrations add InitialCreate:生成迁移文件,记录当前实体的表结构;
dotnet ef database update:应用迁移,创建数据库和表,自动处理字段类型、主键、索引等;
迁移的优势:字段变更时只需修改实体,重新生成迁移即可,无需手动写SQL,维护成本低。
场景2:CRUD操作
AddAsync:标记实体为新增状态,EF Core会生成INSERT SQL;
FindAsync:根据主键查询,比Where更快(主键有默认索引);
SaveChangesAsync:执行所有待处理的变更(新增、修改、删除),异步方法不阻塞UI线程(桌面应用关键)。
场景3:事务操作
BeginTransactionAsync:开启事务,所有后续操作都在事务内执行;
CommitAsync:提交事务,所有操作生效;
RollbackAsync:发生异常时回滚事务,所有操作取消,保证数据一致性;
RemoveRange:批量标记实体为删除,比循环删除更高效(减少数据库交互次数)。
场景4:跨平台适配
OperatingSystem.IsWindows():.NET 6+新增的跨平台判断方法,无需引用第三方库;
各系统目录:Windows用AppData,macOS用Library/Application Support,Linux用~/.config,都是用户可读写的目录。
基础知识拓展

  1. SQLite核心特性
特性 说明 对桌面应用的价值
文件型数据库 整个数据库就是一个文件,方便备份(复制文件即可)、移动 桌面应用用户可以轻松备份数据,无需复杂操作
无服务器 不需要安装数据库服务(如SQL Server需要安装服务) 桌面应用无需额外安装依赖,下载即可使用
跨平台 支持Windows、macOS、Linux、iOS、Android 一次编写,多平台运行,降低开发成本
动态数据类型 弱类型,列可以存储任意类型的数据(EF Core会做类型检查) 灵活,适合快速开发小型工具
ACID支持 事务的原子性、一致性、隔离性、持久性 保证数据安全,批量操作不会出现数据混乱
轻量级 库文件小(Windows下约1MB),内存占用低 桌面应用启动快,不占用过多资源
  1. 桌面应用SQLite最佳实践
    1.目录选择:优先用用户可读写的目录(如AppData),避免程序目录(无写入权限);
    2.备份策略:定期复制数据库文件到备份目录,或提供手动备份功能(比如“导出数据”按钮);
    3.加密敏感数据:如果存储密码、隐私信息,用SQLCipher加密数据库(EF Core支持:安装Microsoft.EntityFrameworkCore.Sqlite.Core和SQLitePCLRaw.bundle_e_sqlcipher);
    4.性能优化:
    o对常用查询字段创建索引(如DueDate、IsCompleted);
    o用分页查询(Skip/Take)避免一次性加载大量数据;
    o关闭不必要的日志(生产环境去掉LogTo);
    5.连接池:EF Core默认管理连接池,不需要手动配置,但要注意不要长时间持有DbContext(用using语句及时释放);
    6.数据量限制:SQLite适合小到中等数据量(10万条以内),桌面应用一般不会超过这个量级,足够使用。
  2. SQLite vs 其他本地存储
    存储方式 优点 缺点 适用场景
    SQLite 支持SQL、事务、查询快、跨平台 数据量过大时性能下降(百万条以上) 桌面应用、小型工具、移动应用
    JSON文件 轻量、易读、无需依赖 查询慢、不支持事务、数据量大时解析慢 小数据量(如配置文件)
    XML文件 结构清晰、易扩展 查询慢、不支持事务、解析耗时 配置文件、小数据量存储
    CSV文件 简单、易导出 不支持复杂查询、无结构、易出错 数据导出/导入
  3. 常见坑点与解决
    权限问题:数据库文件存放在程序目录导致无写入权限 → 改用用户AppData目录;
    数据丢失:未用事务导致批量操作部分失败 → 用事务保证原子性;
    性能低下:未创建索引导致查询慢 → 对常用查询字段创建索引;
    跨平台路径错误:硬编码目录路径 → 用OperatingSystem类适配不同系统目录;
    敏感数据泄露:未加密数据库 → 用SQLCipher加密。
    总结
    SQLite是桌面应用本地数据存储的首选方案,结合EF Core可以快速开发稳定、高效的数据存储功能。本节的核心要点:
    1.路径选择:用用户可读写的目录(如AppData),避免权限问题;
    2.迁移初始化:用EF Core迁移自动创建数据库和表,降低维护成本;
    3.CRUD操作:用EF Core的异步方法(AddAsync、SaveChangesAsync),不阻塞UI线程;
    4.事务安全:批量操作时用事务保证数据一致性;
    5.跨平台适配:用.NET 6+的跨平台API,一次编写,多平台运行;
    6.性能优化:对常用查询字段创建索引,用分页查询避免加载大量数据。
    掌握这些技巧,你就能轻松实现桌面应用的本地数据存储,让你的工具更稳定、更高效。

本站原创,转载请注明出处:https://www.xin3721.com/ArticlecSharp/c49458.html


相关教程