整合 Sugar ORM 连接 SQLite 数据库到 WPF 折线图项目

一、核心目标

在原有 WPF 折线图项目基础上,通过Sugar ORM连接 SQLite 数据库,实现:

  1. 从 SQLite 读取历史销量数据渲染折线图

  2. 追加数据时同时写入数据库,实现数据持久化

二、前置准备

1. 安装依赖包

通过 NuGet 安装以下包:

包名 作用
SqlSugarCore Sugar ORM 核心(支持 SQLite)
System.Data.SQLite SQLite 数据库驱动
Microsoft.Data.Sqlite 可选(替代 System.Data.SQLite)

2. SQLite 数据库准备

(1)创建数据库文件

在项目根目录创建SalesData.db文件(也可让 Sugar 自动创建)。

(2)创建销量表

定义销量实体类,Sugar 会自动生成数据表:

复制代码
using SqlSugar;
using System;
​
namespace LineChart2.Model
{
    /// <summary>
    /// 销量数据表实体
    /// </summary>
    [SugarTable("SalesRecord")]
    public class SalesRecord
    {
        /// <summary>
        /// 主键(自增)
        /// </summary>
        [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
        public int Id { get; set; }
​
        /// <summary>
        /// 年份(2023/2024)
        /// </summary>
        [SugarColumn(ColumnName = "Year")]
        public int SalesYear { get; set; }
​
        /// <summary>
        /// 月份(1-12)
        /// </summary>
        public int Month { get; set; }
​
        /// <summary>
        /// 销量数值
        /// </summary>
        public double SalesValue { get; set; }
​
        /// <summary>
        /// 创建时间
        /// </summary>
        public DateTime CreateTime { get; set; } = DateTime.Now;
    }
}

三、Sugar ORM 初始化配置

创建数据库帮助类,封装 Sugar 连接和操作:

复制代码
using SqlSugar;
using LineChart2.Model;
using System;
using System.IO;
​
namespace LineChart2.Helper
{
    public class DbHelper
    {
        // Sugar数据库连接实例(单例)
        public static SqlSugarClient Db { get; private set; }
​
        static DbHelper()
        {
            // 初始化连接
            InitDb();
        }
​
        /// <summary>
        /// 初始化SQLite连接
        /// </summary>
        private static void InitDb()
        {
            // 获取数据库文件路径(项目根目录)
            string dbPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "SalesData.db");
​
            Db = new SqlSugarClient(new ConnectionConfig
            {
                ConnectionString = $"Data Source={dbPath};", // SQLite连接字符串
                DbType = DbType.Sqlite, // 数据库类型
                IsAutoCloseConnection = true, // 自动关闭连接
                InitKeyType = InitKeyType.Attribute // 从特性读取主键/自增配置
            });
​
            // 初始化数据表(不存在则创建)
            Db.CodeFirst.InitTables(typeof(SalesRecord));
​
            // 开启日志(调试用,生产可关闭)
            Db.Aop.OnLogExecuting = (sql, pars) =>
            {
                Console.WriteLine($"SQL:{sql} \r\n参数:{string.Join(",", pars.Select(p => p.ParameterName + "=" + p.Value))}");
            };
        }
​
        #region 销量数据操作封装
        /// <summary>
        /// 批量插入销量数据
        /// </summary>
        public static void BatchInsertSalesData(SalesRecord[] records)
        {
            Db.Insertable(records).ExecuteCommand();
        }
​
        /// <summary>
        /// 插入单条销量数据
        /// </summary>
        public static int InsertSalesData(SalesRecord record)
        {
            return Db.Insertable(record).ExecuteReturnIdentity();
        }
​
        /// <summary>
        /// 根据年份查询销量数据
        /// </summary>
        public static List<SalesRecord> GetSalesDataByYear(int year)
        {
            return Db.Queryable<SalesRecord>()
                     .Where(r => r.SalesYear == year)
                     .OrderBy(r => r.Month)
                     .ToList();
        }
​
        /// <summary>
        /// 查询所有年份的销量数据
        /// </summary>
        public static List<SalesRecord> GetAllSalesData()
        {
            return Db.Queryable<SalesRecord>()
                     .OrderBy(r => r.SalesYear)
                     .ThenBy(r => r.Month)
                     .ToList();
        }
        #endregion
    }
}

四、修改折线图控件,整合数据库操作

1. 重构 UCLineChart.xaml.cs

替换原有硬编码数据,改为从数据库读取 / 写入:

复制代码
using LiveCharts;
using LiveCharts.Defaults;
using LiveCharts.Wpf;
using LineChart2.Helper;
using LineChart2.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
​
namespace LineChart2
{
    public partial class UCLineChart : UserControl
    {
        public SeriesCollection SeriesCollection { get; set; }
        public string[] Labels { get; set; }
        public Func<double, string> YFormatter { get; set; }
​
        // 模拟销量数据
        private readonly Random _random = new Random();
        // 当前最大月份(用于追加数据)
        private int _currentMaxMonth = 5;
​
        public UCLineChart()
        {
            InitializeComponent();
​
            // 初始化图表数据(优先从数据库读取)
            InitChartDataFromDb();
​
            // Y轴格式化(显示整数)
            YFormatter = value => $"{value:N0}";
​
            DataContext = this;
        }
​
        /// <summary>
        /// 从数据库初始化图表数据
        /// </summary>
        private void InitChartDataFromDb()
        {
            SeriesCollection = new SeriesCollection();
​
            // 1. 查询所有销量数据
            var allSalesData = DbHelper.GetAllSalesData();
​
            // 2. 如果数据库无数据,初始化默认数据并写入数据库
            if (!allSalesData.Any())
            {
                InitDefaultDataAndWriteToDb();
                allSalesData = DbHelper.GetAllSalesData();
            }
​
            // 3. 按年份分组构建折线系列
            var groupedData = allSalesData.GroupBy(r => r.SalesYear);
            foreach (var group in groupedData)
            {
                int year = group.Key;
                var values = group.OrderBy(r => r.Month).Select(r => r.SalesValue).ToList();
​
                // 创建折线系列
                LineSeries series = new LineSeries
                {
                    Title = $"{year}年",
                    Values = new ChartValues<double>(values),
                    LineSmoothness = 0.3,
                    StrokeThickness = 2,
                    PointGeometrySize = 8
                };
​
                // 不同年份设置不同颜色
                series.Stroke = year == 2024 ? Brushes.DodgerBlue : Brushes.OrangeRed;
                series.PointForeground = year == 2024 ? Brushes.DodgerBlue : Brushes.OrangeRed;
                series.PointGeometry = year == 2024 ? DefaultGeometries.Circle : DefaultGeometries.Square;
​
                SeriesCollection.Add(series);
            }
​
            // 4. 构建X轴标签(1-当前最大月份)
            _currentMaxMonth = allSalesData.Max(r => r.Month);
            Labels = Enumerable.Range(1, _currentMaxMonth).Select(m => $"{m}月").ToArray();
        }
​
        /// <summary>
        /// 初始化默认数据并写入数据库
        /// </summary>
        private void InitDefaultDataAndWriteToDb()
        {
            List<SalesRecord> defaultData = new List<SalesRecord>();
​
            // 2024年1-5月数据
            double[] sales2024 = { 120, 150, 110, 180, 160 };
            for (int i = 0; i < sales2024.Length; i++)
            {
                defaultData.Add(new SalesRecord
                {
                    SalesYear = 2024,
                    Month = i + 1,
                    SalesValue = sales2024[i]
                });
            }
​
            // 2023年1-5月数据
            double[] sales2023 = { 100, 130, 140, 150, 130 };
            for (int i = 0; i < sales2023.Length; i++)
            {
                defaultData.Add(new SalesRecord
                {
                    SalesYear = 2023,
                    Month = i + 1,
                    SalesValue = sales2023[i]
                });
            }
​
            // 批量写入数据库
            DbHelper.BatchInsertSalesData(defaultData.ToArray());
        }
​
        // 追加数据按钮点击事件
        private void BtnAddData_Click(object sender, RoutedEventArgs e)
        {
            // 1. 月份+1
            _currentMaxMonth++;
            int newMonth = _currentMaxMonth;
​
            // 2. 生成随机销量(100-200)
            double sales2024 = _random.Next(100, 200);
            double sales2023 = _random.Next(100, 200);
​
            // 3. 写入数据库
            DbHelper.InsertSalesData(new SalesRecord { SalesYear = 2024, Month = newMonth, SalesValue = sales2024 });
            DbHelper.InsertSalesData(new SalesRecord { SalesYear = 2023, Month = newMonth, SalesValue = sales2023 });
​
            // 4. 追加数据点到图表
            SeriesCollection[0].Values.Add(sales2024);
            SeriesCollection[1].Values.Add(sales2023);
​
            // 5. 更新X轴标签
            Array.Resize(ref Labels, Labels.Length + 1);
            Labels[Labels.Length - 1] = $"{newMonth}月";
​
            // 6. 刷新绑定(触发UI更新)
            chart.AxisX[0].Labels = Labels;
​
            // 更新按钮文本(追加7月、8月...)
            (sender as Button).Content = $"追加{newMonth + 1}月数据";
        }
    }
}

2. 修复原代码 Bug

原代码中Labels数组扩容时使用了错误的方法,已替换为Array.Resize

复制代码
// 错误写法
// Labels[Labels.Length - 1] = "6月";
​
// 正确写法
Array.Resize(ref Labels, Labels.Length + 1);
Labels[Labels.Length - 1] = $"{newMonth}月";

五、关键功能说明

1. 数据库初始化逻辑

  • 首次运行时,若数据库无数据,自动插入 2023/2024 年 1-5 月默认数据

  • 后续运行时,从数据库读取数据渲染图表,保证数据持久化

2. 数据追加逻辑

  • 点击按钮时,生成随机销量数据并写入数据库

  • 同时更新图表数据,实现 "数据库 + UI" 双向同步

  • 按钮文本动态更新(追加 6 月→追加 7 月→...)

3. Sugar ORM 核心操作

操作 核心代码示例
初始化连接 new SqlSugarClient(new ConnectionConfig { ... })
自动建表 Db.CodeFirst.InitTables(typeof(SalesRecord))
批量插入 Db.Insertable(records).ExecuteCommand()
条件查询 Db.Queryable<SalesRecord>().Where(r => r.SalesYear == year).ToList()
排序查询 OrderBy(r => r.Month)

六、运行验证

  1. 启动项目,首次运行会自动创建SalesData.db文件,并插入默认数据

  2. 图表会显示 2023/2024 年 1-5 月销量折线

  3. 点击 "追加 6 月数据" 按钮:

    • 数据库会新增 2023/2024 年 6 月销量记录

    • 图表自动追加 6 月数据点,X 轴标签更新为 "6 月"

    • 按钮文本变为 "追加 7 月数据",重复点击可持续追加

七、扩展优化建议

1. 异常处理

添加 try-catch 捕获数据库操作异常:

复制代码
try
{
    DbHelper.InsertSalesData(new SalesRecord { ... });
}
catch (Exception ex)
{
    MessageBox.Show($"数据写入失败:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}

2. 分页查询(大数据量)

若销量数据量大,可分页读取:

复制代码
// 分页查询2024年数据(第1页,每页10条)
var pageData = Db.Queryable<SalesRecord>()
                 .Where(r => r.SalesYear == 2024)
                 .OrderBy(r => r.Month)
                 .ToPageList(1, 10);

3. 数据更新 / 删除

扩展 DbHelper 添加更新 / 删除方法:

复制代码
/// <summary>
/// 更新销量数据
/// </summary>
public static bool UpdateSalesData(SalesRecord record)
{
    return Db.Updateable(record).ExecuteCommand() > 0;
}
​
/// <summary>
/// 删除指定月份数据
/// </summary>
public static bool DeleteSalesData(int year, int month)
{
    return Db.Deleteable<SalesRecord>()
             .Where(r => r.SalesYear == year && r.Month == month)
             .ExecuteCommand() > 0;
}

4. 多表关联(进阶)

若需要关联其他表(如产品表),可使用 Sugar 的联表查询:

复制代码
// 关联产品表查询销量
var joinData = Db.Queryable<SalesRecord, Product>((s, p) => new JoinQueryInfos(
                 JoinType.Inner, s.ProductId == p.Id))
               .Select((s, p) => new { s.Month, s.SalesValue, p.ProductName })
               .ToList();

八、项目结构梳理

复制代码
LineChart2/
├─ Helper/
│  └─ DbHelper.cs          // Sugar ORM数据库操作封装
├─ Model/
│  └─ SalesRecord.cs       // 销量数据实体类
├─ UCLineChart.xaml        // 折线图用户控件XAML
├─ UCLineChart.xaml.cs     // 折线图逻辑(整合数据库)
├─ MainWindow.xaml         // 主窗口
└─ SalesData.db            // SQLite数据库文件(运行后生成)

通过以上改造,原有的 WPF 折线图项目实现了与 SQLite 数据库的无缝整合,数据不再局限于内存,而是通过 Sugar ORM 实现了持久化存储和动态读写。

相关推荐
、BeYourself14 小时前
PGvector :在 Spring AI 中实现向量数据库存储与相似性搜索
数据库·人工智能·spring·springai
a1879272183114 小时前
MySQL 硬件优化和操作系统优化
数据库·mysql·优化·raid·numa·sysbench·系统参数
只想早点退休的90后14 小时前
sql面试题分享
数据库·sql
枫叶丹414 小时前
【Qt开发】Qt系统(三)->事件过滤器
java·c语言·开发语言·数据库·c++·qt
不会c嘎嘎14 小时前
mysql -- 使用CAPI访问mysql服务器
服务器·数据库·mysql
Zzzzmo_14 小时前
【MySQL】数据类型 及 表的操作
数据库·mysql
L16247614 小时前
linux系统中YUM安装MySQL数据库详细教程
linux·数据库·mysql
残风也想永存14 小时前
【MySQL】事务管理
数据库·mysql
wengad14 小时前
mongoDB安全漏洞CVE-2025-14847修复方案
数据库·mongodb