创建一个基于 EF Core 和 SQLite 的完整 Demo,展示数据库连接、创建数据库、建表以及增删改查(CRUD)操作

创建一个基于 EF Core 和 SQLite 的完整 Demo,展示数据库连接、创建数据库、建表以及增删改查(CRUD)操作。该 Demo 将:

  1. 使用 EF Core 连接 SQLite 数据库。

  2. 自动创建数据库并定义表(基于 DeviceConfig 和 TestData 实体,适配你的项目)。

  3. 实现增删改查操作,集成仓储模式。

  4. 使用 LoggerHelper 记录操作日志,解决“日志没有保存成功”问题。

  5. 提供测试用例验证功能。

  6. 确保代码高效、稳定,与你的架构一致。


一、Demo 概述

  • 功能:

    • 连接 SQLite 数据库,自动创建 testsystem.db。

    • 定义两个表:DeviceConfigs(设备配置)、TestData(测试数据)。

    • 实现 CRUD 操作(增删改查)。

    • 使用仓储模式解耦数据访问。

    • 集成日志记录(基于 Common 层的 LoggerHelper)。

  • 技术栈:

    • .NET 8.0

    • EF Core 8.0.4

    • SQLite

    • log4net(日志)

    • NUnit(测试)

  • 项目结构:

    SQLiteDemo
    ├── SQLiteDemo.Common
    │   ├── LoggerHelper.cs
    ├── SQLiteDemo.Data
    │   ├── Entities
    │   │   ├── DeviceConfig.cs
    │   │   ├── TestData.cs
    │   ├── Repositories
    │   │   ├── IDeviceConfigRepository.cs
    │   │   ├── ITestDataRepository.cs
    │   │   ├── DeviceConfigRepository.cs
    │   │   ├── TestDataRepository.cs
    │   ├── DataContext.cs
    ├── SQLiteDemo.Tests
    │   ├── DataTests
    │   │   ├── DeviceConfigRepositoryTests.cs
    │   │   ├── TestDataRepositoryTests.cs
    ├── log4net.config

二、代码实现

以下是完整的 Demo 代码,包含数据库连接、建表、CRUD 操作和测试。

1. 项目文件

SQLiteDemo.Common.csproj:

xml


  
    net8.0
  
  
    
    
  

SQLiteDemo.Data.csproj:

xml


  
    net8.0
  
  
    
    
      runtime; build; native; contentfiles; analyzers; buildtransitive
      all
    
    
  

SQLiteDemo.Tests.csproj:

xml


  
    net8.0
  
  
    
    
    
    
    
  

2. Common 层

LoggerHelper.cs:

csharp

// SQLiteDemo.Common/LoggerHelper.cs
using log4net;
using log4net.Config;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Reflection;

namespace SQLiteDemo.Common
{
    public static class LoggerHelper
    {
        private static bool _isInitialized;

        public static ILogger CreateLogger(string categoryName)
        {
            if (!_isInitialized)
            {
                try
                {
                    var repository = LogManager.CreateRepository(Assembly.GetEntryAssembly() ?? Assembly.GetCallingAssembly(), typeof(log4net.Repository.Hierarchy.Hierarchy));
                    XmlConfigurator.Configure(repository, new FileInfo("log4net.config"));
                    Directory.CreateDirectory("logs");
                    _isInitialized = true;
                }
                catch (Exception ex)
                {
                    File.WriteAllText("logger_init_error.txt", ex.ToString());
                    throw;
                }
            }
            return new Log4NetLogger(LogManager.GetLogger(categoryName));
        }

        public static void LogOperation(ILogger logger, string message, string context = "System")
        {
            logger?.LogInformation("[Context: {Context}] {Message}", context, message);
        }

        public static void LogException(ILogger logger, Exception ex, string operation, string context = "System")
        {
            logger?.LogError(ex, "[Context: {Context}] {Operation} failed: {Message}", context, operation, ex.Message);
        }

        public static void Flush()
        {
            LogManager.Flush(1000);
        }

        public static void Shutdown()
        {
            LogManager.Shutdown();
        }
    }

    public class Log4NetLogger : ILogger
    {
        private readonly ILog _log;

        public Log4NetLogger(ILog log)
        {
            _log = log;
        }

        public IDisposable BeginScope(TState state) => null;

        public bool IsEnabled(LogLevel logLevel) => true;

        public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter)
        {
            var message = formatter(state, exception);
            switch (logLevel)
            {
                case LogLevel.Information:
                    _log.Info(message, exception);
                    break;
                case LogLevel.Error:
                    _log.Error(message, exception);
                    break;
                default:
                    _log.Debug(message, exception);
                    break;
            }
        }
    }
}

log4net.config(根目录):

xml



  
    
    
    
    
    
    
      
    
  
  
    
    
  
  • 设置 Copy to Output Directory: Copy always.

3. Data 层

DeviceConfig.cs:

csharp

// SQLiteDemo.Data/Entities/DeviceConfig.cs
using System.ComponentModel.DataAnnotations;

namespace SQLiteDemo.Data.Entities
{
    public class DeviceConfig
    {
        [Key]
        public string DeviceId { get; set; }
        [Required]
        public string Protocol { get; set; }
        public string Host { get; set; } = "127.0.0.1";
        public int Port { get; set; } = 502;
        public DateTime LastUpdated { get; set; } = DateTime.UtcNow;
    }
}

TestData.cs:

csharp

// SQLiteDemo.Data/Entities/TestData.cs
using System.ComponentModel.DataAnnotations;

namespace SQLiteDemo.Data.Entities
{
    public class TestData
    {
        [Key]
        public long Id { get; set; }
        [Required]
        public string DeviceId { get; set; }
        public double Voltage { get; set; }
        public double Temperature { get; set; }
        public DateTime Timestamp { get; set; }
    }
}

DataContext.cs:

csharp

// SQLiteDemo.Data/DataContext.cs
using Microsoft.EntityFrameworkCore;
using SQLiteDemo.Data.Entities;

namespace SQLiteDemo.Data
{
    public class DataContext : DbContext
    {
        public DataContext(DbContextOptions options) : base(options) { }

        public DbSet DeviceConfigs { get; set; }
        public DbSet TestData { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            // 配置主键和索引
            modelBuilder.Entity()
                .HasKey(d => d.DeviceId);
            modelBuilder.Entity()
                .HasKey(t => t.Id);
            modelBuilder.Entity()
                .HasIndex(t => t.DeviceId);
        }
    }
}

IDeviceConfigRepository.cs:

csharp

// SQLiteDemo.Data/Repositories/IDeviceConfigRepository.cs
using SQLiteDemo.Data.Entities;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace SQLiteDemo.Data.Repositories
{
    public interface IDeviceConfigRepository
    {
        Task AddAsync(DeviceConfig config, CancellationToken cancellationToken = default);
        Task GetByIdAsync(string deviceId, CancellationToken cancellationToken = default);
        Task> GetAllAsync(CancellationToken cancellationToken = default);
        Task UpdateAsync(DeviceConfig config, CancellationToken cancellationToken = default);
        Task DeleteAsync(string deviceId, CancellationToken cancellationToken = default);
        Task SaveChangesAsync(CancellationToken cancellationToken = default);
    }
}

DeviceConfigRepository.cs:

csharp

// SQLiteDemo.Data/Repositories/DeviceConfigRepository.cs
using Microsoft.EntityFrameworkCore;
using SQLiteDemo.Data.Entities;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace SQLiteDemo.Data.Repositories
{
    public class DeviceConfigRepository : IDeviceConfigRepository
    {
        private readonly DataContext _context;

        public DeviceConfigRepository(DataContext context)
        {
            _context = context;
        }

        public async Task AddAsync(DeviceConfig config, CancellationToken cancellationToken = default)
        {
            await _context.DeviceConfigs.AddAsync(config, cancellationToken);
        }

        public async Task GetByIdAsync(string deviceId, CancellationToken cancellationToken = default)
        {
            return await _context.DeviceConfigs
                .FirstOrDefaultAsync(c => c.DeviceId == deviceId, cancellationToken);
        }

        public async Task> GetAllAsync(CancellationToken cancellationToken = default)
        {
            return await _context.DeviceConfigs
                .ToListAsync(cancellationToken);
        }

        public async Task UpdateAsync(DeviceConfig config, CancellationToken cancellationToken = default)
        {
            _context.DeviceConfigs.Update(config);
        }

        public async Task DeleteAsync(string deviceId, CancellationToken cancellationToken = default)
        {
            var config = await GetByIdAsync(deviceId, cancellationToken);
            if (config != null)
            {
                _context.DeviceConfigs.Remove(config);
            }
        }

        public async Task SaveChangesAsync(CancellationToken cancellationToken = default)
        {
            await _context.SaveChangesAsync(cancellationToken);
        }
    }
}

ITestDataRepository.cs:

csharp

// SQLiteDemo.Data/Repositories/ITestDataRepository.cs
using SQLiteDemo.Data.Entities;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace SQLiteDemo.Data.Repositories
{
    public interface ITestDataRepository
    {
        Task AddAsync(TestData testData, CancellationToken cancellationToken = default);
        Task GetByIdAsync(long id, CancellationToken cancellationToken = default);
        Task> GetByDeviceIdAsync(string deviceId, CancellationToken cancellationToken = default);
        Task UpdateAsync(TestData testData, CancellationToken cancellationToken = default);
        Task DeleteAsync(long id, CancellationToken cancellationToken = default);
        Task SaveChangesAsync(CancellationToken cancellationToken = default);
    }
}

TestDataRepository.cs:

csharp

// SQLiteDemo.Data/Repositories/TestDataRepository.cs
using Microsoft.EntityFrameworkCore;
using SQLiteDemo.Data.Entities;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace SQLiteDemo.Data.Repositories
{
    public class TestDataRepository : ITestDataRepository
    {
        private readonly DataContext _context;

        public TestDataRepository(DataContext context)
        {
            _context = context;
        }

        public async Task AddAsync(TestData testData, CancellationToken cancellationToken = default)
        {
            await _context.TestData.AddAsync(testData, cancellationToken);
        }

        public async Task GetByIdAsync(long id, CancellationToken cancellationToken = default)
        {
            return await _context.TestData
                .FirstOrDefaultAsync(t => t.Id == id, cancellationToken);
        }

        public async Task> GetByDeviceIdAsync(string deviceId, CancellationToken cancellationToken = default)
        {
            return await _context.TestData
                .Where(t => t.DeviceId == deviceId)
                .ToListAsync(cancellationToken);
        }

        public async Task UpdateAsync(TestData testData, CancellationToken cancellationToken = default)
        {
            _context.TestData.Update(testData);
        }

        public async Task DeleteAsync(long id, CancellationToken cancellationToken = default)
        {
            var testData = await GetByIdAsync(id, cancellationToken);
            if (testData != null)
            {
                _context.TestData.Remove(testData);
            }
        }

        public async Task SaveChangesAsync(CancellationToken cancellationToken = default)
        {
            await _context.SaveChangesAsync(cancellationToken);
        }
    }
}

4. 主程序

Program.cs:

csharp

// SQLiteDemo/Program.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using SQLiteDemo.Common;
using SQLiteDemo.Data;
using SQLiteDemo.Data.Entities;
using SQLiteDemo.Data.Repositories;
using System;
using System.Threading.Tasks;

namespace SQLiteDemo
{
    class Program
    {
        static async Task Main(string[] args)
        {
            // 配置依赖注入
            var services = new ServiceCollection();
            services.AddDbContext(options =>
                options.UseSqlite("Data Source=testsystem.db"));
            services.AddScoped();
            services.AddScoped();
            services.AddSingleton(sp => LoggerHelper.CreateLogger("Demo"));
            var serviceProvider = services.BuildServiceProvider();

            // 确保数据库创建
            using (var scope = serviceProvider.CreateScope())
            {
                var context = scope.ServiceProvider.GetRequiredService();
                await context.Database.EnsureCreatedAsync();
            }

            // 获取仓储和日志
            using (var scope = serviceProvider.CreateScope())
            {
                var deviceRepo = scope.ServiceProvider.GetRequiredService();
                var testDataRepo = scope.ServiceProvider.GetRequiredService();
                var logger = scope.ServiceProvider.GetRequiredService();

                // Demo:增删改查
                try
                {
                    // 增:添加设备配置
                    var config = new DeviceConfig
                    {
                        DeviceId = "PS001",
                        Protocol = "ModbusTcp",
                        Host = "127.0.0.1",
                        Port = 502
                    };
                    await deviceRepo.AddAsync(config);
                    await deviceRepo.SaveChangesAsync();
                    LoggerHelper.LogOperation(logger, "Added device config PS001");

                    // 增:添加测试数据
                    var testData = new TestData
                    {
                        DeviceId = "PS001",
                        Voltage = 5.0,
                        Temperature = 25.0,
                        Timestamp = DateTime.UtcNow
                    };
                    await testDataRepo.AddAsync(testData);
                    await testDataRepo.SaveChangesAsync();
                    LoggerHelper.LogOperation(logger, "Added test data for PS001");

                    // 查:获取设备配置
                    var retrievedConfig = await deviceRepo.GetByIdAsync("PS001");
                    Console.WriteLine($"Retrieved Config: DeviceId={retrievedConfig.DeviceId}, Protocol={retrievedConfig.Protocol}");
                    LoggerHelper.LogOperation(logger, "Retrieved device config PS001");

                    // 查:获取测试数据
                    var testDataList = await testDataRepo.GetByDeviceIdAsync("PS001");
                    foreach (var data in testDataList)
                    {
                        Console.WriteLine($"TestData: Voltage={data.Voltage}, Timestamp={data.Timestamp}");
                    }
                    LoggerHelper.LogOperation(logger, "Retrieved test data for PS001");

                    // 改:更新设备配置
                    retrievedConfig.Port = 503;
                    await deviceRepo.UpdateAsync(retrievedConfig);
                    await deviceRepo.SaveChangesAsync();
                    LoggerHelper.LogOperation(logger, "Updated device config PS001");

                    // 删:删除测试数据
                    await testDataRepo.DeleteAsync(testData.Id);
                    await testDataRepo.SaveChangesAsync();
                    LoggerHelper.LogOperation(logger, "Deleted test data");

                    // 验证删除
                    var deletedData = await testDataRepo.GetByIdAsync(testData.Id);
                    Console.WriteLine(deletedData == null ? "Test data deleted successfully" : "Deletion failed");
                }
                catch (Exception ex)
                {
                    LoggerHelper.LogException(logger, ex, "Demo Operation");
                }
                finally
                {
                    LoggerHelper.Flush();
                    LoggerHelper.Shutdown();
                }
            }
        }
    }
}

5. 测试用例

DeviceConfigRepositoryTests.cs:

csharp

// SQLiteDemo.Tests/DataTests/DeviceConfigRepositoryTests.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Moq;
using NUnit.Framework;
using SQLiteDemo.Common;
using SQLiteDemo.Data;
using SQLiteDemo.Data.Entities;
using SQLiteDemo.Data.Repositories;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace SQLiteDemo.Tests.DataTests
{
    [TestFixture]
    public class DeviceConfigRepositoryTests
    {
        private DataContext _context;
        private IDeviceConfigRepository _repository;
        private ILogger _logger;
        private string _logFile;

        [SetUp]
        public void Setup()
        {
            var options = new DbContextOptionsBuilder()
                .UseSqlite("Data Source=:memory:")
                .Options;
            _context = new DataContext(options);
            _context.Database.OpenConnection();
            _context.Database.EnsureCreated();
            _repository = new DeviceConfigRepository(_context);
            _logger = LoggerHelper.CreateLogger("DeviceConfigRepositoryTests");
            _logFile = Path.Combine("logs", $"{DateTime.Now:yyyy-MM-dd}.log");
            if (File.Exists(_logFile))
            {
                File.Delete(_logFile);
            }
        }

        [TearDown]
        public void TearDown()
        {
            _context.Database.CloseConnection();
            _context.Dispose();
            LoggerHelper.Flush();
            LoggerHelper.Shutdown();
        }

        [Test]
        public async Task AddAndGetAsync_SavesAndRetrievesConfig()
        {
            // Arrange
            var config = new DeviceConfig
            {
                DeviceId = "PS001",
                Protocol = "ModbusTcp",
                Host = "127.0.0.1",
                Port = 502
            };

            // Act
            await _repository.AddAsync(config);
            await _repository.SaveChangesAsync();
            var retrieved = await _repository.GetByIdAsync("PS001");

            // Assert
            Assert.That(retrieved, Is.Not.Null);
            Assert.That(retrieved.DeviceId, Is.EqualTo("PS001"));
            Assert.That(retrieved.Protocol, Is.EqualTo("ModbusTcp"));
            Assert.That(File.Exists(_logFile), Is.True);
        }

        [Test]
        public async Task UpdateAsync_UpdatesConfig()
        {
            // Arrange
            var config = new DeviceConfig
            {
                DeviceId = "PS001",
                Protocol = "ModbusTcp",
                Host = "127.0.0.1",
                Port = 502
            };
            await _repository.AddAsync(config);
            await _repository.SaveChangesAsync();

            // Act
            config.Port = 503;
            await _repository.UpdateAsync(config);
            await _repository.SaveChangesAsync();
            var updated = await _repository.GetByIdAsync("PS001");

            // Assert
            Assert.That(updated.Port, Is.EqualTo(503));
            Assert.That(File.Exists(_logFile), Is.True);
        }

        [Test]
        public async Task DeleteAsync_RemovesConfig()
        {
            // Arrange
            var config = new DeviceConfig
            {
                DeviceId = "PS001",
                Protocol = "ModbusTcp"
            };
            await _repository.AddAsync(config);
            await _repository.SaveChangesAsync();

            // Act
            await _repository.DeleteAsync("PS001");
            await _repository.SaveChangesAsync();
            var deleted = await _repository.GetByIdAsync("PS001");

            // Assert
            Assert.That(deleted, Is.Null);
            Assert.That(File.Exists(_logFile), Is.True);
        }
    }
}

TestDataRepositoryTests.cs:

csharp

// SQLiteDemo.Tests/DataTests/TestDataRepositoryTests.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Moq;
using NUnit.Framework;
using SQLiteDemo.Common;
using SQLiteDemo.Data;
using SQLiteDemo.Data.Entities;
using SQLiteDemo.Data.Repositories;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace SQLiteDemo.Tests.DataTests
{
    [TestFixture]
    public class TestDataRepositoryTests
    {
        private DataContext _context;
        private ITestDataRepository _repository;
        private ILogger _logger;
        private string _logFile;

        [SetUp]
        public void Setup()
        {
            var options = new DbContextOptionsBuilder()
                .UseSqlite("Data Source=:memory:")
                .Options;
            _context = new DataContext(options);
            _context.Database.OpenConnection();
            _context.Database.EnsureCreated();
            _repository = new TestDataRepository(_context);
            _logger = LoggerHelper.CreateLogger("TestDataRepositoryTests");
            _logFile = Path.Combine("logs", $"{DateTime.Now:yyyy-MM-dd}.log");
            if (File.Exists(_logFile))
            {
                File.Delete(_logFile);
            }
        }

        [TearDown]
        public void TearDown()
        {
            _context.Database.CloseConnection();
            _context.Dispose();
            LoggerHelper.Flush();
            LoggerHelper.Shutdown();
        }

        [Test]
        public async Task AddAndGetAsync_SavesAndRetrievesTestData()
        {
            // Arrange
            var testData = new TestData
            {
                DeviceId = "PS001",
                Voltage = 5.0,
                Temperature = 25.0,
                Timestamp = DateTime.UtcNow
            };

            // Act
            await _repository.AddAsync(testData);
            await _repository.SaveChangesAsync();
            var retrieved = await _repository.GetByIdAsync(testData.Id);

            // Assert
            Assert.That(retrieved, Is.Not.Null);
            Assert.That(retrieved.Voltage, Is.EqualTo(5.0));
            Assert.That(File.Exists(_logFile), Is.True);
        }

        [Test]
        public async Task GetByDeviceIdAsync_RetrievesCorrectData()
        {
            // Arrange
            var testData = new TestData
            {
                DeviceId = "PS001",
                Voltage = 5.0,
                Temperature = 25.0,
                Timestamp = DateTime.UtcNow
            };
            await _repository.AddAsync(testData);
            await _repository.SaveChangesAsync();

            // Act
            var results = await _repository.GetByDeviceIdAsync("PS001");

            // Assert
            Assert.That(results.Count, Is.EqualTo(1));
            Assert.That(results[0].DeviceId, Is.EqualTo("PS001"));
            Assert.That(File.Exists(_logFile), Is.True);
        }
    }
}

三、运行和验证

  1. 创建项目:

    bash

    dotnet new sln -n SQLiteDemo
    dotnet new classlib -n SQLiteDemo.Common -f net8.0
    dotnet new classlib -n SQLiteDemo.Data -f net8.0
    dotnet new nunit -n SQLiteDemo.Tests -f net8.0
    dotnet new console -n SQLiteDemo -f net8.0
    dotnet sln add SQLiteDemo.Common SQLiteDemo.Data SQLiteDemo.Tests SQLiteDemo
  2. 添加依赖:

    bash

    cd SQLiteDemo.Common
    dotnet add package log4net
    dotnet add package Microsoft.Extensions.Logging.Abstractions
    cd ../SQLiteDemo.Data
    dotnet add package Microsoft.EntityFrameworkCore.Sqlite
    dotnet add package Microsoft.EntityFrameworkCore.Design
    dotnet add reference ../SQLiteDemo.Common
    cd ../SQLiteDemo.Tests
    dotnet add package NUnit
    dotnet add package NUnit3TestAdapter
    dotnet add package Microsoft.NET.Test.Sdk
    dotnet add package Microsoft.EntityFrameworkCore.InMemory
    dotnet add reference ../SQLiteDemo.Data
    cd ../SQLiteDemo
    dotnet add reference ../SQLiteDemo.Data
  3. 复制代码:

    • 将上述代码放入对应文件。

    • 添加 log4net.config 到根目录。

  4. 运行程序:

    bash

    cd SQLiteDemo
    dotnet run
    • 输出示例:

      Retrieved Config: DeviceId=PS001, Protocol=ModbusTcp
      TestData: Voltage=5, Timestamp=2025-05-31T08:39:00
      Test data deleted successfully
    • 检查 testsystem.db 和 logs/2025-05-31.log.

  5. 运行测试:

    bash

    cd SQLiteDemo.Tests
    dotnet test

四、解决日志问题

问题:日志未保存(“日志没有保存成功”)。 修复:

  1. 验证 log4net.config:

    • 确保文件存在且复制到输出目录。

    • 检查路径:

      csharp

      Console.WriteLine(File.Exists("log4net.config") ? "Config found" : "Config missing");
  2. 检查权限:

    bash

    mkdir logs
    chmod -R 777 logs
  3. 调试初始化:

    • 若失败,查看 logger_init_error.txt:

      csharp

      if (File.Exists("logger_init_error.txt"))
          Console.WriteLine(File.ReadAllText("logger_init_error.txt"));

五、与 SemiconductorTestSystem 集成

  1. 复用实体:

    • DeviceConfig 和 TestData 与你的项目一致,可直接替换 SemiconductorTestSystem.Data.Entities。

  2. 仓储模式:

    • IDeviceConfigRepository 和 ITestDataRepository 可集成到 SemiconductorTestSystem.Data.Repositories。

  3. DataContext:

    • 更新 SemiconductorTestSystem.Data.DataContext:

      csharp

      services.AddDbContext(options =>
          options.UseSqlite("Data Source=testsystem.db"));
  4. 日志:

    • 使用现有 LoggerHelper,确保 log4net.config 配置一致。


六、高效、稳定实践

  1. 异步操作:

    • 所有数据库操作使用 async/await。

  2. 事务管理:

    csharp

    using var transaction = await _context.Database.BeginTransactionAsync();
    await _context.DeviceConfigs.AddAsync(config);
    await _context.SaveChangesAsync();
    await transaction.CommitAsync();
  3. 索引优化:

    • TestData.DeviceId 已添加索引,提高查询效率。

  4. 异常处理:

    csharp

    catch (DbUpdateException ex)
    {
        LoggerHelper.LogException(logger, ex, "Database operation");
        throw;
    }

七、调试建议

  1. 数据库连接:

    • 验证 testsystem.db:

      csharp

      Console.WriteLine(File.Exists("testsystem.db") ? "Database exists" : "Database missing");
    • 检查连接字符串:

      csharp

      Console.WriteLine("Connection: Data Source=testsystem.db");
  2. 数据验证:

    • 查询表:

      csharp

      var configs = await _context.DeviceConfigs.ToListAsync();
      configs.ForEach(c => Console.WriteLine(c.DeviceId));
  3. 日志验证:

    • 检查日志:

      csharp

      var logContent = File.ReadAllText(_logFile);
      Console.WriteLine(logContent);

八、注意事项

  1. 环境:

    • 确保 .NET 8.0 SDK:

      bash

      dotnet --version
    • 安装 SQLite:

      bash

      sudo apt-get install sqlite3  # Linux
  2. 依赖:

    • 验证 NuGet 包:

      bash

      dotnet restore
  3. 测试:

    • 使用 SQLite :memory: 避免文件依赖:

      csharp

      .UseSqlite("Data Source=:memory:")

九、排查问题

若有问题,提供:

  • 错误信息(堆栈跟踪)。

  • logs/ 或 logger_init_error.txt。

  • 环境(OS、.NET、IDE)。

  • 数据库文件(testsystem.db)。

示例问题:

  • 问题:数据库未创建。

  • 排查:

    • 验证 EnsureCreatedAsync:

      csharp

      Console.WriteLine(await _context.Database.EnsureCreatedAsync() ? "Created" : "Failed");
    • 检查连接:

      csharp

      await _context.Database.OpenConnectionAsync();

请确认是否需要:

  • 扩展功能(如批量插入)。

  • 集成 SemiconductorTestSystem 的具体模块。

  • 其他数据库(如 SQL Server)。

希望这个 Demo 能满足你的需求!

你可能感兴趣的:(框架搭建,jvm,oracle,数据库)