当前位置: 首页 > article >正文

青少年编程与数学 02-020 C#程序设计基础 13课题、数据访问

青少年编程与数学 02-020 C#程序设计基础 13课题、数据访问

  • 一、使用数据库
    • 1. 使用ADO.NET连接数据库
      • 连接SQL Server示例
      • 连接其他数据库
    • 2. 使用Entity Framework (EF Core)
      • 安装EF Core
      • 示例代码
    • 3. 数据绑定到WinForms控件
      • DataGridView绑定
      • 简单控件绑定
    • 4. 使用本地数据库(SQLite)
      • 安装SQLite
      • 示例代码
    • 5. 最佳实践
    • 6. 配置文件存储连接字符串
  • 二、使用SQL Server数据库
    • 1. 优势分析
    • 2. 劣势与挑战
    • 3. 适用场景评价
    • 4. 技术实现建议
    • 5. 替代方案对比
    • 6. 结论
  • 三、.NET Framework ADO.NET 详解
    • (一)ADO.NET 核心组件
      • 1. 两大核心组件集
      • 2. 核心对象模型
    • (二)详细使用指南
      • 1. 连接数据库 (SqlConnection)
      • 2. 执行命令 (SqlCommand)
        • 基本CRUD操作
        • 参数化查询的重要性
      • 3. 数据读取器 (SqlDataReader)
      • 4. 数据适配器与数据集 (SqlDataAdapter + DataSet)
    • (三)高级主题
      • 1. 事务处理
      • 2. 存储过程调用
      • 3. 批量操作
      • 4. 异步操作
    • (四)性能优化技巧
    • (五)安全实践
    • (六)ADO.NET vs Entity Framework
    • (七)实际应用示例 - WinForms数据绑定
  • 四、.NET 8 ADO.NET 详解
    • (一)现代 ADO.NET 架构
      • 1. 组件模型升级
      • 2. 核心命名空间
    • (二)连接管理与配置
      • 1. 现代连接字符串配置
      • 2. 高级连接选项
    • (三)命令执行增强
      • 1. 参数化查询现代化
      • 2. 批量操作优化
    • (四)数据读取高级技术
      • 1. 现代DataReader模式
      • 2. 结果集流式处理
    • (五)事务与隔离级别
      • 1. 现代事务管理
      • 2. 分布式事务支持
    • (六)安全增强特性
      • 1. Always Encrypted 集成
      • 2. Azure AD 集成
    • (七)性能优化技巧
      • 1. 连接池优化配置
      • 2. 命令准备与重用
    • (八)诊断与监控
      • 1. 活动追踪配置
      • 2. 获取性能统计
    • (九)与 Entity Framework Core 集成
      • 1. 混合使用原始SQL
      • 2. 批量操作集成
    • (十)迁移指南
      • 从传统 ADO.NET 迁移步骤:
    • 小结
  • 五、综合示例
    • 1. 项目设置
      • 1.1 创建项目
    • 2. 完整代码实现
      • 2.1 数据库帮助类 (`Data/DatabaseHelper.cs`)
      • 2.2 主窗体 (`MainForm.cs`)
      • 2.3 产品详情窗体 (`ProductDetailForm.cs`)
      • 2.4 分类管理窗体 (`CategoryForm.cs`)
      • 2.5 订单处理窗体 (`OrderForm.cs`)
    • 3. 主要特点
    • 4. 与传统ADO.NET的区别
  • 总结

摘要:本文深入探讨了在C# WinForms应用程序中使用ADO.NET和Entity Framework Core进行数据库操作的方法。详细介绍了ADO.NET的核心组件、使用方法、性能优化和安全实践,以及Entity Framework Core的安装和使用。文章还提供了数据绑定到WinForms控件的示例,并展示了.NET 8中ADO.NET的现代化改进和性能优化技巧。通过一个完整的WinForms应用程序示例,展示了如何使用ADO.NET进行数据库操作,为开发者提供了实用的技术参考。

关键词:C# WinForms、ADO.NET、Entity Framework Core、数据库操作、数据绑定、安全实践、.NET 8

AI助手:DeepSeek、Kimi


一、使用数据库

在C# WinForms应用程序中使用数据库是常见的需求,下面我将介绍几种主要的方法:

1. 使用ADO.NET连接数据库

ADO.NET是.NET框架中访问数据库的核心技术。

连接SQL Server示例

using System.Data.SqlClient;// 连接字符串
string connectionString = "Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password=myPassword;";// 执行查询
using (SqlConnection connection = new SqlConnection(connectionString))
{connection.Open();// 执行SQL命令string sql = "SELECT * FROM Customers";using (SqlCommand command = new SqlCommand(sql, connection)){using (SqlDataReader reader = command.ExecuteReader()){while (reader.Read()){Console.WriteLine(reader["CustomerName"].ToString());}}}
}

连接其他数据库

对于MySQL、Oracle等数据库,需要使用相应的提供程序(如MySql.Data.MySqlClient)。

2. 使用Entity Framework (EF Core)

EF Core是一个流行的ORM框架,可以简化数据库操作。

安装EF Core

通过NuGet包管理器安装:

  • Microsoft.EntityFrameworkCore
  • Microsoft.EntityFrameworkCore.SqlServer (或其他数据库提供程序)

示例代码

// 定义模型
public class Customer
{public int Id { get; set; }public string Name { get; set; }public string Email { get; set; }
}// 创建DbContext
public class AppDbContext : DbContext
{public DbSet<Customer> Customers { get; set; }protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){optionsBuilder.UseSqlServer("your_connection_string");}
}// 使用示例
using (var context = new AppDbContext())
{// 查询var customers = context.Customers.Where(c => c.Name.Contains("John")).ToList();// 添加var newCustomer = new Customer { Name = "Alice", Email = "alice@example.com" };context.Customers.Add(newCustomer);context.SaveChanges();// 更新var customer = context.Customers.First();customer.Email = "newemail@example.com";context.SaveChanges();
}

3. 数据绑定到WinForms控件

DataGridView绑定

// 使用ADO.NET DataTable
DataTable dataTable = new DataTable();
using (SqlConnection connection = new SqlConnection(connectionString))
{SqlDataAdapter adapter = new SqlDataAdapter("SELECT * FROM Customers", connection);adapter.Fill(dataTable);
}dataGridView1.DataSource = dataTable;// 使用EF Core
dataGridView1.DataSource = dbContext.Customers.ToList();

简单控件绑定

// 绑定到TextBox
textBoxName.DataBindings.Add("Text", dataTable, "CustomerName");

4. 使用本地数据库(SQLite)

对于小型应用程序,SQLite是一个不错的选择。

安装SQLite

通过NuGet安装:

  • Microsoft.Data.Sqlite
  • Microsoft.EntityFrameworkCore.Sqlite

示例代码

public class AppDbContext : DbContext
{public DbSet<Customer> Customers { get; set; }protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){optionsBuilder.UseSqlite("Data Source=mydatabase.db");}
}

5. 最佳实践

  1. 连接管理:始终使用using语句确保连接和命令对象被正确释放
  2. 异常处理:捕获和处理数据库异常(SqlException等)
  3. 参数化查询:防止SQL注入
    string sql = "SELECT * FROM Customers WHERE Id = @Id";
    command.Parameters.AddWithValue("@Id", customerId);
    
  4. 异步操作:对于长时间运行的数据库操作,使用异步方法
    var customers = await dbContext.Customers.ToListAsync();
    
  5. 配置管理:将连接字符串存储在app.config或appsettings.json中

6. 配置文件存储连接字符串

在app.config中:

<configuration><connectionStrings><add name="MyDb" connectionString="Server=.;Database=MyDb;Integrated Security=True;" providerName="System.Data.SqlClient" /></connectionStrings>
</configuration>

在代码中读取:

string connectionString = ConfigurationManager.ConnectionStrings["MyDb"].ConnectionString;

以上是在C# WinForms应用程序中使用数据库的主要方法。根据项目规模和需求,可以选择ADO.NET直接操作或使用ORM框架如EF Core来简化开发。

二、使用SQL Server数据库

在C# WinForms应用程序中使用SQL Server数据库是一种常见且强大的组合,以下是全面评价:

1. 优势分析

高性能与稳定性

  • SQL Server是成熟的商业数据库,特别适合处理大量数据和复杂查询
  • 对于企业级WinForms应用,能够提供可靠的性能表现
  • 支持存储过程、触发器等高级功能,可将业务逻辑部分放在数据库层

与.NET生态完美集成

  • ADO.NET原生支持SQL Server,提供最优化的访问方式
  • Entity Framework对SQL Server的支持最全面,功能实现最完整
  • Visual Studio提供SQL Server的直接工具支持(Server Explorer等)

开发效率高

  • 丰富的可视化工具(SQL Server Management Studio)
  • LINQ to SQL和Entity Framework可显著减少数据访问层代码量
  • 数据集设计器(DataSet Designer)可快速创建类型化数据集

安全特性完善

  • 集成Windows身份验证(Integrated Security)
  • 细粒度的权限控制
  • 数据加密功能完善

企业级功能支持

  • 事务处理能力强
  • 支持分布式查询
  • 完善的备份恢复机制

2. 劣势与挑战

部署复杂度

  • 需要安装SQL Server(Express版可减轻此问题)
  • 相比SQLite等文件数据库,部署更复杂
  • 需要管理数据库服务器

成本考量

  • 标准版和企业版成本较高(但Express版免费)
  • 需要专门的数据库服务器硬件

不适合小型应用

  • 对于单机小型应用可能"杀鸡用牛刀"
  • 简单的数据存储需求使用SQLite或Access可能更合适

版本兼容性

  • 不同SQL Server版本间有时存在兼容性问题
  • 连接字符串配置可能需要调整

3. 适用场景评价

✅ 推荐使用场景

  • 企业级内部管理系统(ERP、CRM等)
  • 多用户并发访问的系统
  • 需要处理复杂业务逻辑和大量数据的应用
  • 已有SQL Server基础设施的环境

❌ 不推荐场景

  • 单机小型工具类应用
  • 需要简单部署的共享软件
  • 移动端或需要离线使用的应用

4. 技术实现建议

架构选择

  • 对于简单应用:ADO.NET + DataSet
  • 对于中型应用:Entity Framework Core
  • 对于复杂企业应用:Repository模式 + Dapper

连接管理最佳实践

// 使用配置文件的连接字符串
string connStr = ConfigurationManager.ConnectionStrings["MyDB"].ConnectionString;// 使用using确保资源释放
using (SqlConnection conn = new SqlConnection(connStr))
{await conn.OpenAsync();// 数据库操作
}

性能优化建议

  • 使用异步方法(async/await)避免UI冻结
  • 合理使用连接池
  • 对常用查询添加索引
  • 考虑使用存储过程处理复杂逻辑

安全建议

  • 永远使用参数化查询防止SQL注入
  • 不要硬编码连接字符串
  • 实施最小权限原则

5. 替代方案对比

特性SQL ServerSQLiteMySQLPostgreSQL
部署难度
性能
适合规模中大型小型中大中大
.NET支持最佳
成本中高(Express免费)免费免费免费

6. 结论

C# WinForms + SQL Server组合在适合的场景下是非常强大的选择,特别是对于企业级Windows桌面应用开发。它提供了性能、可靠性和开发效率的良好平衡,但开发者需要考虑部署复杂度和成本因素。对于不适用SQL Server的场景,可以考虑SQLite(单机简单应用)或其他替代方案。

三、.NET Framework ADO.NET 详解

ADO.NET 是 .NET Framework 中用于数据访问的核心组件,为 C# WinForms 应用程序提供了一套强大而灵活的数据库操作方式。下面我将从基础到高级全面解析 ADO.NET。

(一)ADO.NET 核心组件

1. 两大核心组件集

  • .NET Framework 数据提供程序 (专用于特定数据库类型)

    • SQL Server 提供程序: System.Data.SqlClient
    • OLE DB 提供程序: System.Data.OleDb
    • ODBC 提供程序: System.Data.Odbc
    • Oracle 提供程序: System.Data.OracleClient (已过时,推荐使用 Oracle 官方提供程序)
  • DataSet (独立于数据库的内存数据容器)

2. 核心对象模型

连接对象 (Connection) → 命令对象 (Command)↓
数据读取器 (DataReader) ← 执行查询↓
数据适配器 (DataAdapter) → DataSet/DataTable

(二)详细使用指南

1. 连接数据库 (SqlConnection)

// 基本连接字符串格式
string connStr = "Server=服务器名;Database=数据库名;User Id=用户名;Password=密码;";// 使用Windows身份验证
string connStr = "Server=服务器名;Database=数据库名;Integrated Security=True;";// 创建连接对象
using (SqlConnection connection = new SqlConnection(connStr))
{try{connection.Open();// 执行数据库操作...}catch (SqlException ex){MessageBox.Show($"数据库错误: {ex.Message}");}
}

最佳实践:

  • 始终使用 using 语句确保连接关闭
  • 将连接字符串存储在配置文件中
  • 启用连接池(默认开启)

2. 执行命令 (SqlCommand)

基本CRUD操作
// 插入数据
string insertSql = "INSERT INTO Customers (Name, Email) VALUES (@Name, @Email)";
using (SqlCommand cmd = new SqlCommand(insertSql, connection))
{cmd.Parameters.AddWithValue("@Name", "张三");cmd.Parameters.AddWithValue("@Email", "zhangsan@example.com");int rowsAffected = cmd.ExecuteNonQuery();
}// 更新数据
string updateSql = "UPDATE Customers SET Email = @Email WHERE Id = @Id";
using (SqlCommand cmd = new SqlCommand(updateSql, connection))
{cmd.Parameters.AddWithValue("@Email", "new@example.com");cmd.Parameters.AddWithValue("@Id", 1);cmd.ExecuteNonQuery();
}// 删除数据
string deleteSql = "DELETE FROM Customers WHERE Id = @Id";
using (SqlCommand cmd = new SqlCommand(deleteSql, connection))
{cmd.Parameters.AddWithValue("@Id", 1);cmd.ExecuteNonQuery();
}
参数化查询的重要性
  • 防止SQL注入攻击
  • 提高查询性能(执行计划可重用)
  • 自动处理数据类型转换

3. 数据读取器 (SqlDataReader)

string sql = "SELECT Id, Name, Email FROM Customers";
using (SqlCommand cmd = new SqlCommand(sql, connection))
{using (SqlDataReader reader = cmd.ExecuteReader()){while (reader.Read()){int id = reader.GetInt32(0);           // 按索引获取string name = reader["Name"].ToString(); // 按列名获取string email = reader.GetString(2);     // 使用类型特定方法// 处理数据...}}
}

特点:

  • 只进、只读的快速数据访问
  • 非常节省内存(不缓存整个结果集)
  • 需要保持连接打开状态

4. 数据适配器与数据集 (SqlDataAdapter + DataSet)

// 创建适配器和数据集
SqlDataAdapter adapter = new SqlDataAdapter("SELECT * FROM Customers", connection);
DataSet ds = new DataSet();// 填充数据
adapter.Fill(ds, "Customers");  // "Customers"是DataTable名称// 访问数据
foreach (DataRow row in ds.Tables["Customers"].Rows)
{Console.WriteLine($"{row["Id"]}: {row["Name"]}");
}// 更新数据
DataTable dt = ds.Tables["Customers"];
dt.Rows[0]["Name"] = "修改后的名字";// 配置命令生成器自动生成更新命令
SqlCommandBuilder builder = new SqlCommandBuilder(adapter);
adapter.Update(dt);

DataSet 特点:

  • 断开式数据访问模型
  • 内存中的数据缓存
  • 可以包含多个表和数据关系
  • 支持数据绑定

(三)高级主题

1. 事务处理

using (SqlTransaction transaction = connection.BeginTransaction())
{try{using (SqlCommand cmd = connection.CreateCommand()){cmd.Transaction = transaction;cmd.CommandText = "INSERT INTO Orders...";cmd.ExecuteNonQuery();cmd.CommandText = "UPDATE Inventory...";cmd.ExecuteNonQuery();transaction.Commit();  // 提交事务}}catch{transaction.Rollback();   // 回滚事务throw;}
}

2. 存储过程调用

using (SqlCommand cmd = new SqlCommand("sp_GetCustomerOrders", connection))
{cmd.CommandType = CommandType.StoredProcedure;cmd.Parameters.AddWithValue("@CustomerId", 123);using (SqlDataReader reader = cmd.ExecuteReader()){// 处理结果...}
}

3. 批量操作

// 使用SqlBulkCopy高效导入数据
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection))
{bulkCopy.DestinationTableName = "TargetTable";bulkCopy.WriteToServer(dataTable);  // 可以从DataTable、IDataReader等导入
}

4. 异步操作

// 异步方法
public async Task<List<Customer>> GetCustomersAsync()
{List<Customer> customers = new List<Customer>();using (SqlConnection conn = new SqlConnection(connStr)){await conn.OpenAsync();using (SqlCommand cmd = new SqlCommand("SELECT * FROM Customers", conn)){using (SqlDataReader reader = await cmd.ExecuteReaderAsync()){while (await reader.ReadAsync()){customers.Add(new Customer{Id = reader.GetInt32(0),Name = reader.GetString(1),Email = reader.GetString(2)});}}}}return customers;
}

(四)性能优化技巧

  1. 连接池优化

    • 默认已启用,不要手动开关池
    • 保持连接字符串一致(区分大小写)
  2. 命令对象重用

    • 对于重复执行的命令,重用SqlCommand对象
    • 使用 Prepare() 方法预编译命令
  3. 适当选择数据访问方式

    • 大量只读数据 → DataReader
    • 离线编辑数据 → DataAdapter + DataSet
    • 单值查询 → ExecuteScalar
  4. 批处理操作

    • 合并多个操作为一个SQL语句
    • 使用表值参数(TVP)传递多行数据

(五)安全实践

  1. 防范SQL注入

    • 永远使用参数化查询
    • 不要拼接SQL字符串
    // 错误做法(危险!)
    string sql = "SELECT * FROM Users WHERE Name = '" + userName + "'";// 正确做法
    string sql = "SELECT * FROM Users WHERE Name = @UserName";
    cmd.Parameters.AddWithValue("@UserName", userName);
    
  2. 连接字符串安全

    • 不要硬编码在代码中
    • 使用配置文件和加密技术
  3. 最小权限原则

    • 应用程序使用最小必要权限的数据库账户

(六)ADO.NET vs Entity Framework

特性ADO.NETEntity Framework
抽象级别低级别,接近SQL高级别,面向对象
开发速度较慢,需要更多代码更快,自动生成很多代码
性能更高效,更可控良好,但可能有额外开销
灵活性完全控制SQL有限制,但可以混合使用原始SQL
学习曲线需要了解SQL需要理解ORM概念
适用场景高性能需求,复杂查询快速开发,简单到中等复杂度应用

(七)实际应用示例 - WinForms数据绑定

// 绑定DataGridView
public void LoadCustomersToGrid()
{string sql = "SELECT * FROM Customers";SqlDataAdapter adapter = new SqlDataAdapter(sql, connectionString);DataTable dt = new DataTable();adapter.Fill(dt);dataGridView1.DataSource = dt;// 设置列标题dataGridView1.Columns["Id"].HeaderText = "编号";dataGridView1.Columns["Name"].HeaderText = "姓名";dataGridView1.Columns["Email"].HeaderText = "电子邮箱";
}// 主从表绑定
public void BindMasterDetail()
{DataSet ds = new DataSet();// 主表SqlDataAdapter masterAdapter = new SqlDataAdapter("SELECT * FROM Customers", connectionString);masterAdapter.Fill(ds, "Customers");// 从表SqlDataAdapter detailAdapter = new SqlDataAdapter("SELECT * FROM Orders", connectionString);detailAdapter.Fill(ds, "Orders");// 建立关系DataRelation relation = new DataRelation("CustOrders",ds.Tables["Customers"].Columns["Id"],ds.Tables["Orders"].Columns["CustomerId"]);ds.Relations.Add(relation);// 绑定dataGridView1.DataSource = ds.Tables["Customers"];dataGridView2.DataSource = ds.Tables["Customers"];dataGridView2.DataMember = "CustOrders";  // 显示关联的订单
}

ADO.NET 为 WinForms 数据库开发提供了坚实的基础,理解其核心概念和最佳实践将帮助您构建高效、安全的数据库应用程序。对于新项目,可以考虑结合使用 ADO.NET 和更高级的 ORM 工具以获得更好的开发体验。

四、.NET 8 ADO.NET 详解

ADO.NET 在 .NET 8 中经过现代化改造,保留了核心数据访问能力的同时,引入了诸多改进和新特性。以下是对 .NET 8 中 ADO.NET 的全面详解:

(一)现代 ADO.NET 架构

1. 组件模型升级

  • 模块化设计:通过 NuGet 分发 (Microsoft.Data.SqlClient)
  • 分层架构:
    应用程序
    Microsoft.Data.SqlClient
    TDS协议层
    网络传输
    安全层

2. 核心命名空间

  • Microsoft.Data.SqlClient (替代旧的 System.Data.SqlClient)
  • System.Data.Common (通用接口)
  • Microsoft.Data.SqlClient.AlwaysEncrypted (专门加密支持)

(二)连接管理与配置

1. 现代连接字符串配置

// 推荐的安全连接字符串
var connectionString = new SqlConnectionStringBuilder
{DataSource = "your-server.database.windows.net",InitialCatalog = "your-database",UserID = "your-username",Password = "your-password",Encrypt = SqlConnectionEncryptOption.Mandatory,  // 强制加密TrustServerCertificate = false,                 // 生产环境应为falseConnectTimeout = 15,Pooling = true,                                // 启用连接池MaxPoolSize = 100                              // 连接池大小
}.ConnectionString;

2. 高级连接选项

// 使用连接属性配置
var connection = new SqlConnection(connectionString)
{AccessToken = azureAdToken,  // Azure AD集成RetryLogicProvider = SqlConfigurableRetryFactory.CreateFixedRetryProvider(new SqlRetryLogicOption{NumberOfTries = 3,DeltaTime = TimeSpan.FromSeconds(1)})
};

(三)命令执行增强

1. 参数化查询现代化

// 现代参数化查询(防注入)
var command = new SqlCommand("SELECT * FROM Products WHERE CategoryId = @Category AND Price > @MinPrice", connection);// 强类型参数添加(推荐方式)
command.Parameters.Add(new SqlParameter
{ParameterName = "@Category",SqlDbType = SqlDbType.Int,Value = categoryId
});// 简化方式(自动推断类型)
command.Parameters.AddWithValue("@MinPrice", minimumPrice);

2. 批量操作优化

// 高性能批量插入(使用SqlBulkCopy)
using var bulkCopy = new SqlBulkCopy(connection)
{DestinationTableName = "Products",BatchSize = 1000,BulkCopyTimeout = 30
};// 从DataReader流式传输
await bulkCopy.WriteToServerAsync(dataReader);// 从DataTable批量写入
await bulkCopy.WriteToServerAsync(dataTable);

(四)数据读取高级技术

1. 现代DataReader模式

await using var reader = await command.ExecuteReaderAsync();// 获取列元数据(避免魔法字符串)
var idOrdinal = reader.GetOrdinal("ProductId");
var nameOrdinal = reader.GetOrdinal("ProductName");while (await reader.ReadAsync())
{var product = new Product{// 按序号访问(性能最优)Id = reader.GetInt32(idOrdinal),// 处理可能为NULL的值Name = reader.IsDBNull(nameOrdinal) ? null : reader.GetString(nameOrdinal),// 新型GetFieldValue方法Price = await reader.GetFieldValueAsync<decimal>(2)};
}

2. 结果集流式处理

// 返回IAsyncEnumerable实现流式处理
public async IAsyncEnumerable<Product> StreamLargeProducts()
{await using var reader = await command.ExecuteReaderAsync(CommandBehavior.SequentialAccess);  // 大对象优化while (await reader.ReadAsync()){yield return new Product{Id = reader.GetInt32(0),Name = reader.GetString(1),// 分块读取大文本/二进制数据Description = await reader.GetTextReader(2).ReadToEndAsync()};}
}

(五)事务与隔离级别

1. 现代事务管理

// 使用TransactionScope(推荐)
await using var scope = new TransactionScope(TransactionScopeOption.Required,new TransactionOptions{IsolationLevel = IsolationLevel.Snapshot,Timeout = TransactionManager.DefaultTimeout},TransactionScopeAsyncFlowOption.Enabled);try
{// 多个操作...await connection1.ExecuteNonQueryAsync(cmd1);await connection2.ExecuteNonQueryAsync(cmd2);scope.Complete();  // 提交事务
}
catch
{// 自动回滚
}

2. 分布式事务支持

// 启用MSDTC或使用新式分布式事务
var options = new SqlConnectionOptions
{EnlistDistributedTransaction = true
};await using var conn = new SqlConnection(connectionString, options);

(六)安全增强特性

1. Always Encrypted 集成

// 配置加密列处理
var connectionString = "Server=...; Column Encryption Setting=enabled;";
var command = new SqlCommand("SELECT * FROM Customers WHERE SSN = @SSN", connection);// 自动加密/解密
command.Parameters.AddWithValue("@SSN", "123-45-6789"); // 查询结果自动解密
await using var reader = await command.ExecuteReaderAsync();

2. Azure AD 集成

// 使用Azure AD令牌认证
var connection = new SqlConnection(connectionString)
{AccessToken = await GetAzureAdTokenAsync()
};// 或者使用连接字符串配置
var connStr = "Server=...; Authentication=Active Directory Default;";

(七)性能优化技巧

1. 连接池优化配置

// 在应用程序启动时配置全局连接池
SqlConnection.Configure(new SqlConnectionConfiguration{PoolSettings = new SqlConnectionPoolSettings{MaxPoolSize = 200,MinPoolSize = 10,LoadBalanceTimeout = 30}});

2. 命令准备与重用

// 准备常用命令(查询计划缓存)
var cmd = new SqlCommand("SELECT * FROM Products WHERE Id = @Id", connection);
cmd.Parameters.Add("@Id", SqlDbType.Int);
await cmd.PrepareAsync();  // 显式准备// 重用命令对象
for (int i = 0; i < ids.Length; i++)
{cmd.Parameters["@Id"].Value = ids[i];await using var reader = await cmd.ExecuteReaderAsync();// ...
}

(八)诊断与监控

1. 活动追踪配置

// 启用诊断监听
using var listener = new SqlClientListener(new SqlClientListenerOptions{CommandTimeout = true,ConnectionOpen = true,Statistics = true});listener.EventWritten += (sender, e) => 
{Console.WriteLine($"{e.EventId}: {e.Message}");
};

2. 获取性能统计

connection.StatisticsEnabled = true;
await connection.OpenAsync();// 执行操作...var stats = connection.RetrieveStatistics();
Console.WriteLine($"Bytes received: {stats["BytesReceived"]}");

(九)与 Entity Framework Core 集成

1. 混合使用原始SQL

var products = await context.Products.FromSqlRaw("SELECT * FROM Products WITH (NOLOCK) WHERE Price > {0}", minPrice).ToListAsync();// 使用SqlParameter防止注入
var param = new SqlParameter("@category", categoryId);
var products = await context.Products.FromSqlRaw("EXEC GetProductsByCategory @category", param).AsNoTracking().ToListAsync();

2. 批量操作集成

// 使用EF Core + ADO.NET批量插入
var bulkConfig = new BulkConfig
{BatchSize = 4000,SqlBulkCopyOptions = SqlBulkCopyOptions.TableLock
};await context.BulkInsertAsync(products, bulkConfig);

(十)迁移指南

从传统 ADO.NET 迁移步骤:

  1. 更新NuGet包:

    dotnet add package Microsoft.Data.SqlClient
    
  2. 替换命名空间:

    // 替换
    using System.Data.SqlClient;
    // 为
    using Microsoft.Data.SqlClient;
    
  3. 异步化改造:

    • 将同步方法替换为异步版本
    • 在调用链中添加 await
  4. 安全配置更新:

    • 添加 EncryptTrustServerCertificate 选项
    • 考虑启用 Always Encrypted
  5. 性能优化调整:

    • 配置连接池
    • 实现重试逻辑
    • 考虑使用批处理操作

小结

.NET 8 中的 ADO.NET 提供了:

  • 现代化的异步编程模型
  • 增强的安全特性(如 Always Encrypted)
  • 深度云集成(Azure AD、托管身份)
  • 显著性能优化
  • 更好的诊断和监控能力
  • 与 EF Core 的无缝集成

虽然基础编程模型保持熟悉,但开发者应充分利用新特性和最佳实践,以构建高性能、安全的现代数据访问层。

五、综合示例

下面是一个完整的.NET 8 WinForms应用程序示例,直接使用ADO.NET DataSet进行数据库操作。

1. 项目设置

1.1 创建项目

dotnet new winforms -n ProductManagementApp
cd ProductManagementApp
dotnet add package Microsoft.Data.SqlClient

2. 完整代码实现

2.1 数据库帮助类 (Data/DatabaseHelper.cs)

using Microsoft.Data.SqlClient;
using System.Data;namespace ProductManagementApp.Data
{public class DatabaseHelper{private readonly string _connectionString;public DatabaseHelper(string connectionString){_connectionString = connectionString;}public async Task<SqlConnection> CreateConnectionAsync(){var connection = new SqlConnection(_connectionString);await connection.OpenAsync();return connection;}public async Task<DataSet> GetProductsDataSetAsync(){var ds = new DataSet();await using var connection = await CreateConnectionAsync();// 获取产品数据var productsAdapter = new SqlDataAdapter("SELECT p.*, c.Name as CategoryName FROM Products p LEFT JOIN Categories c ON p.CategoryId = c.CategoryId", connection);productsAdapter.Fill(ds, "Products");// 获取分类数据var categoriesAdapter = new SqlDataAdapter("SELECT * FROM Categories", connection);categoriesAdapter.Fill(ds, "Categories");// 添加关系ds.Relations.Add("ProductCategory",ds.Tables["Categories"].Columns["CategoryId"],ds.Tables["Products"].Columns["CategoryId"]);return ds;}public async Task<int> AddProductAsync(DataRow productRow){await using var connection = await CreateConnectionAsync();var adapter = new SqlDataAdapter("SELECT * FROM Products WHERE 1=0", connection);var builder = new SqlCommandBuilder(adapter);var dt = new DataTable();adapter.Fill(dt);var newRow = dt.NewRow();newRow.ItemArray = productRow.ItemArray;dt.Rows.Add(newRow);return await adapter.UpdateAsync(dt);}public async Task<int> UpdateProductAsync(DataRow productRow){await using var connection = await CreateConnectionAsync();var adapter = new SqlDataAdapter("SELECT * FROM Products WHERE ProductId = @ProductId", connection);adapter.SelectCommand.Parameters.AddWithValue("@ProductId", productRow["ProductId"]);var builder = new SqlCommandBuilder(adapter);var dt = new DataTable();adapter.Fill(dt);if (dt.Rows.Count == 1){dt.Rows[0].ItemArray = productRow.ItemArray;return await adapter.UpdateAsync(dt);}return 0;}public async Task<int> DeleteProductAsync(int productId){await using var connection = await CreateConnectionAsync();var adapter = new SqlDataAdapter("SELECT * FROM Products WHERE ProductId = @ProductId", connection);adapter.SelectCommand.Parameters.AddWithValue("@ProductId", productId);var builder = new SqlCommandBuilder(adapter);var dt = new DataTable();adapter.Fill(dt);if (dt.Rows.Count == 1){dt.Rows[0].Delete();return await adapter.UpdateAsync(dt);}return 0;}public async Task<int> AddCategoryAsync(string name, string description){await using var connection = await CreateConnectionAsync();var adapter = new SqlDataAdapter("SELECT * FROM Categories WHERE 1=0", connection);var builder = new SqlCommandBuilder(adapter);var dt = new DataTable();adapter.Fill(dt);var newRow = dt.NewRow();newRow["Name"] = name;newRow["Description"] = description;dt.Rows.Add(newRow);return await adapter.UpdateAsync(dt);}public async Task<bool> ProcessOrderAsync(int productId, int quantity){await using var connection = await CreateConnectionAsync();await using var transaction = await connection.BeginTransactionAsync();try{// 1. 检查库存var checkCmd = new SqlCommand("SELECT StockQuantity FROM Products WHERE ProductId = @ProductId", connection, transaction);checkCmd.Parameters.AddWithValue("@ProductId", productId);var currentStock = (int)await checkCmd.ExecuteScalarAsync();if (currentStock < quantity){throw new Exception("库存不足");}// 2. 更新库存var updateCmd = new SqlCommand("UPDATE Products SET StockQuantity = StockQuantity - @Quantity WHERE ProductId = @ProductId", connection, transaction);updateCmd.Parameters.AddWithValue("@ProductId", productId);updateCmd.Parameters.AddWithValue("@Quantity", quantity);await updateCmd.ExecuteNonQueryAsync();// 3. 记录订单 (示例)// var orderCmd = new SqlCommand("INSERT INTO Orders...", connection, transaction);// await orderCmd.ExecuteNonQueryAsync();await transaction.CommitAsync();return true;}catch{await transaction.RollbackAsync();throw;}}}
}

2.2 主窗体 (MainForm.cs)

using ProductManagementApp.Data;
using System.Data;namespace ProductManagementApp
{public partial class MainForm : Form{private readonly DatabaseHelper _dbHelper;private DataSet _dataSet;private DataView _productsView;public MainForm(){InitializeComponent();// 配置连接字符串 (实际项目中应该放在配置文件中)var connectionString = "Server=(local);Database=ProductDB;Integrated Security=true;Encrypt=False;";_dbHelper = new DatabaseHelper(connectionString);}private async void MainForm_Load(object sender, EventArgs e){await LoadDataAsync();ConfigureDataGridView();ConfigureCategoryComboBox();}private async Task LoadDataAsync(){try{_dataSet = await _dbHelper.GetProductsDataSetAsync();_productsView = new DataView(_dataSet.Tables["Products"]);dataGridView1.DataSource = _productsView;}catch (SqlException ex){MessageBox.Show($"数据库错误: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);}catch (Exception ex){MessageBox.Show($"错误: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);}}private void ConfigureDataGridView(){dataGridView1.AutoGenerateColumns = false;dataGridView1.Columns.Clear();// 添加列dataGridView1.Columns.Add(new DataGridViewTextBoxColumn{DataPropertyName = "ProductId",HeaderText = "ID",Name = "colProductId",ReadOnly = true});dataGridView1.Columns.Add(new DataGridViewTextBoxColumn{DataPropertyName = "Name",HeaderText = "产品名称",Name = "colName"});dataGridView1.Columns.Add(new DataGridViewTextBoxColumn{DataPropertyName = "CategoryName",HeaderText = "分类",Name = "colCategory",ReadOnly = true});dataGridView1.Columns.Add(new DataGridViewTextBoxColumn{DataPropertyName = "Price",HeaderText = "价格",Name = "colPrice",DefaultCellStyle = new DataGridViewCellStyle { Format = "C2" }});dataGridView1.Columns.Add(new DataGridViewTextBoxColumn{DataPropertyName = "StockQuantity",HeaderText = "库存",Name = "colStock"});// 双击事件dataGridView1.CellDoubleClick += DataGridView1_CellDoubleClick;}private void ConfigureCategoryComboBox(){if (_dataSet?.Tables["Categories"] != null){cmbCategories.DataSource = _dataSet.Tables["Categories"];cmbCategories.DisplayMember = "Name";cmbCategories.ValueMember = "CategoryId";}}private void DataGridView1_CellDoubleClick(object sender, DataGridViewCellEventArgs e){if (e.RowIndex >= 0){var row = ((DataRowView)_productsView[e.RowIndex]).Row;ShowProductDetail(row);}}private void ShowProductDetail(DataRow productRow){var detailForm = new ProductDetailForm(_dbHelper, productRow);if (detailForm.ShowDialog() == DialogResult.OK){_productsView.Table.AcceptChanges();dataGridView1.Refresh();}}private async void btnAdd_Click(object sender, EventArgs e){if (_dataSet == null) return;var newRow = _dataSet.Tables["Products"].NewRow();newRow["Name"] = txtName.Text;newRow["Description"] = txtDescription.Text;newRow["Price"] = decimal.Parse(txtPrice.Text);newRow["StockQuantity"] = (int)nudStock.Value;newRow["CategoryId"] = cmbCategories.SelectedValue;newRow["CreatedDate"] = DateTime.Now;try{int result = await _dbHelper.AddProductAsync(newRow);if (result > 0){MessageBox.Show("产品添加成功!", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);await LoadDataAsync();ClearInputs();}}catch (Exception ex){MessageBox.Show($"添加产品失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);}}private void ClearInputs(){txtName.Clear();txtDescription.Clear();txtPrice.Clear();nudStock.Value = 0;if (cmbCategories.Items.Count > 0)cmbCategories.SelectedIndex = 0;}private async void btnRefresh_Click(object sender, EventArgs e){await LoadDataAsync();}private async void btnAddCategory_Click(object sender, EventArgs e){var categoryForm = new CategoryForm(_dbHelper);if (categoryForm.ShowDialog() == DialogResult.OK){await LoadDataAsync();ConfigureCategoryComboBox();}}private async void btnProcessOrder_Click(object sender, EventArgs e){if (dataGridView1.SelectedRows.Count > 0){var row = ((DataRowView)dataGridView1.SelectedRows[0].DataBoundItem).Row;int productId = (int)row["ProductId"];int currentStock = (int)row["StockQuantity"];using var orderForm = new OrderForm(productId, currentStock);if (orderForm.ShowDialog() == DialogResult.OK){try{bool success = await _dbHelper.ProcessOrderAsync(productId, orderForm.Quantity);if (success){MessageBox.Show("订单处理成功!", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);await LoadDataAsync();}}catch (Exception ex){MessageBox.Show($"订单处理失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);}}}else{MessageBox.Show("请先选择一个产品", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);}}}
}

2.3 产品详情窗体 (ProductDetailForm.cs)

using ProductManagementApp.Data;
using System.Data;namespace ProductManagementApp
{public partial class ProductDetailForm : Form{private readonly DatabaseHelper _dbHelper;private readonly DataRow _productRow;public ProductDetailForm(DatabaseHelper dbHelper, DataRow productRow){InitializeComponent();_dbHelper = dbHelper;_productRow = productRow;InitializeData();}private void InitializeData(){txtProductId.Text = _productRow["ProductId"].ToString();txtName.Text = _productRow["Name"].ToString();txtDescription.Text = _productRow["Description"].ToString();txtPrice.Text = Convert.ToDecimal(_productRow["Price"]).ToString("N2");nudStock.Value = Convert.ToInt32(_productRow["StockQuantity"]);dtpCreatedDate.Value = Convert.ToDateTime(_productRow["CreatedDate"]);}private async void btnSave_Click(object sender, EventArgs e){_productRow["Name"] = txtName.Text;_productRow["Description"] = txtDescription.Text;_productRow["Price"] = decimal.Parse(txtPrice.Text);_productRow["StockQuantity"] = (int)nudStock.Value;try{int result = await _dbHelper.UpdateProductAsync(_productRow);if (result > 0){MessageBox.Show("产品更新成功!", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);DialogResult = DialogResult.OK;Close();}else{MessageBox.Show("更新产品失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);}}catch (Exception ex){MessageBox.Show($"更新产品失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);}}private async void btnDelete_Click(object sender, EventArgs e){if (MessageBox.Show("确定要删除这个产品吗?", "确认", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes){try{int productId = (int)_productRow["ProductId"];int result = await _dbHelper.DeleteProductAsync(productId);if (result > 0){MessageBox.Show("产品删除成功!", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);DialogResult = DialogResult.OK;Close();}else{MessageBox.Show("删除产品失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);}}catch (Exception ex){MessageBox.Show($"删除产品失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);}}}}
}

2.4 分类管理窗体 (CategoryForm.cs)

using ProductManagementApp.Data;namespace ProductManagementApp
{public partial class CategoryForm : Form{private readonly DatabaseHelper _dbHelper;public CategoryForm(DatabaseHelper dbHelper){InitializeComponent();_dbHelper = dbHelper;}private async void btnSave_Click(object sender, EventArgs e){try{int result = await _dbHelper.AddCategoryAsync(txtName.Text, txtDescription.Text);if (result > 0){MessageBox.Show("分类添加成功!", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);DialogResult = DialogResult.OK;Close();}}catch (Exception ex){MessageBox.Show($"添加分类失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);}}}
}

2.5 订单处理窗体 (OrderForm.cs)

namespace ProductManagementApp
{public partial class OrderForm : Form{public int Quantity => (int)nudQuantity.Value;public OrderForm(int productId, int currentStock){InitializeComponent();lblProductId.Text = productId.ToString();lblStock.Text = currentStock.ToString();nudQuantity.Maximum = currentStock;}private void btnOK_Click(object sender, EventArgs e){if (nudQuantity.Value > 0){DialogResult = DialogResult.OK;Close();}else{MessageBox.Show("请输入有效的数量", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);}}}
}

3. 主要特点

  1. 纯ADO.NET实现:

    • 完全使用DataSet/DataTable进行数据操作
    • 使用SqlDataAdapter进行数据填充和更新
    • 利用SqlCommandBuilder自动生成CRUD命令
  2. 断开式数据访问:

    • 数据加载到内存后断开连接
    • 修改后批量提交更改
  3. 数据绑定:

    • DataGridView直接绑定到DataView
    • ComboBox绑定到DataTable
  4. 事务处理:

    • 订单处理演示完整的事务操作
    • 使用SqlTransaction确保数据一致性
  5. 异步编程:

    • 所有数据库操作都使用异步方法
    • 避免UI线程阻塞

4. 与传统ADO.NET的区别

  1. 现代异步API:

    • 使用ExecuteNonQueryAsync等异步方法
    • SqlDataAdapter新增异步方法
  2. 连接管理:

    • 使用await using确保资源释放
    • 自动连接池管理
  3. 安全增强:

    • 默认推荐加密连接
    • 参数化查询防止SQL注入

这个示例展示了如何在.NET 8 WinForms应用程序中完全不依赖Dapper等ORM,直接使用ADO.NET DataSet进行数据库操作,同时保持了现代编程实践如异步编程和依赖注入。

总结

本文全面介绍了在C# WinForms应用程序中使用数据库的技术和方法,重点聚焦于ADO.NET和Entity Framework Core两大主流技术栈。首先,文章详细阐述了ADO.NET的核心组件和使用方法,包括连接数据库、执行命令、数据读取、数据适配器与数据集的使用等,并提供了性能优化和安全实践的建议。接着,文章深入探讨了Entity Framework Core的安装、使用以及与ADO.NET的对比,展示了ORM框架在简化开发中的优势。此外,文章还介绍了如何将数据绑定到WinForms控件,如DataGridView,并提供了使用本地数据库SQLite的示例。在.NET 8环境下,文章详细解析了ADO.NET的现代化改进,包括新的连接管理、命令执行增强、数据读取技术、事务处理、安全增强特性以及性能优化技巧,并通过一个完整的WinForms应用程序示例,展示了如何使用ADO.NET进行数据库操作。最后,文章对比了ADO.NET和Entity Framework Core的适用场景,为开发者提供了技术选型的参考。

相关文章:

青少年编程与数学 02-020 C#程序设计基础 13课题、数据访问

青少年编程与数学 02-020 C#程序设计基础 13课题、数据访问 一、使用数据库1. 使用ADO.NET连接数据库连接SQL Server示例连接其他数据库 2. 使用Entity Framework (EF Core)安装EF Core示例代码 3. 数据绑定到WinForms控件DataGridView绑定简单控件绑定 4. 使用本地数据库(SQLi…...

无人机仿真环境(3维)附项目git链接

项目概述 随着无人机技术在物流、测绘、应急救援等领域的广泛应用&#xff0c;其自主导航、避障算法、路径规划及多机协同等核心技术的研究需求日益迫切。为降低实地测试成本、提高研发效率&#xff0c;本项目旨在构建一个高精度、可扩展的​​无人机三维虚拟仿真环境​​&…...

湖北理元理律师事务所:债务优化中的“生活锚点”设计

在债务重组领域&#xff0c;一个常被忽视的核心矛盾是&#xff1a;还款能力与生存需求的冲突。过度压缩生活支出还债&#xff0c;可能导致收入中断&#xff1b;放任债务膨胀&#xff0c;又加剧精神压力。湖北理元理律师事务所通过“三步平衡法”&#xff0c;尝试在法理框架内破…...

Python 训练营打卡 Day 30-模块和库的导入

模块和库的导入 1.1标准导入 import mathprint("方式1: 使用 import math") print(f"圆周率π的值: {math.pi}") print(f"2的平方根: {math.sqrt(2)}\n") 1.2从库中导入特定项 from math import pi, sqrtprint("方式2&#xff1a;使用 f…...

前端实现图片压缩:基于 HTML5 File API 与 Canvas 的完整方案

在 Web 开发中,处理用户上传的图片时,前端压缩可以有效减少服务器压力并提升上传效率。本文将详细讲解如何通过<input type="file">实现图片上传,结合 Canvas 实现图片压缩,并实时展示压缩前后的图片预览和文件大小对比。 一、核心功能架构 我们将实现以…...

【Docker管理工具】部署Docker管理面板DweebUI

【Docker管理工具】部署Docker管理面板DweebUI 一、DweebUI介绍1.1 DweebUI 简介1.2 主要特点1.3 使用场景 二、本次实践规划2.1 本地环境规划2.2 本次实践介绍 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四、下载DweebUI镜像五、…...

【后端高阶面经:架构篇】50、数据存储架构:如何改善系统的数据存储能力?

一、数据存储架构设计核心原则 (一)分层存储架构:让数据各得其所 根据数据访问频率和价值,将数据划分为热、温、冷三层,匹配不同存储介质,实现性能与成本的平衡。 热数据层:访问频率>100次/秒。采用Redis集群存储高频访问数据(如用户登录态、实时交易数据),配合…...

编程之巅:语言的较量

第一章&#xff1a;代码之城的召集令 在遥远的数字大陆上&#xff0c;有一座名为“代码之城”的神秘都市。这里居住着各种编程语言的化身&#xff0c;他们以拟人化的形态生活&#xff0c;每种语言都有独特的性格与技能。Python是个优雅的学者&#xff0c;C是个硬核战士&#x…...

STM32 通过 ESP8266 通信详解

✅作者简介&#xff1a;热爱科研的嵌入式开发者&#xff0c;修心和技术同步精进 ❤欢迎关注我的知乎&#xff1a;对error视而不见 代码获取、问题探讨及文章转载可私信。 ☁ 愿你的生命中有够多的云翳,来造就一个美丽的黄昏。 &#x1f34e;获取更多嵌入式资料可点击链接进群领…...

Qt/C++开发监控GB28181系统/sip协议/同时支持udp和tcp模式/底层协议解析

一、前言说明 在gb28181-2011协议中&#xff0c;只有udp要求&#xff0c;从2016版本开始要求支持tcp&#xff0c;估计也是在多年的实际运行过程中&#xff0c;发现有些网络环境差的场景下&#xff0c;一些udp交互指令丢失导致功能异常&#xff0c;所以后面修订的时候增加了tcp…...

晨控CK-FR03与汇川H5U系列PLC配置MODBUS TCP通讯连接操作手册

晨控CK-FR03与汇川H5U系列PLC配置MODBUS TCP通讯连接操作手册 CK-FR03-TCP是一款基于射频识别技术的高频RFID标签读卡器&#xff0c;读卡器工作频率为13.56MHZ&#xff0c;支持对I-CODE 2、I-CODE SLI等符合ISO15693国际标准协议格式标签的读取。 读卡器同时支持标准工业通讯协…...

山海鲸轻 3D 渲染技术深度解析:预渲染如何突破多终端性能瓶颈

在前期课程中&#xff0c;我们已系统讲解了山海鲸两大核心渲染模式——云渲染与端渲染的技术特性及配置方法。为满足复杂场景下的差异化需求&#xff0c;山海鲸创新推出轻3D渲染功能&#xff0c;本文将深度解析该技术的实现原理与操作实践。 一、轻3D功能研发背景 针对多终端协…...

t014-项目申报管理系统 【springBoot 含源码】

项目演示视频 摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;项目信息因为其管理内容繁杂&#xff0c;管理数量繁多导致手工进行…...

阻止H5页面中键盘收起的问题

在移动端H5开发中&#xff0c;当输入框失去焦点时&#xff0c;键盘会自动收起&#xff0c;但有时我们需要阻止这种行为。以下是几种解决方案&#xff1a; 常见原因 输入框失去焦点触发键盘收起页面滚动或触摸其他区域导致键盘收起某些浏览器(特别是iOS Safari)的默认行为 解…...

将 AI 解答转换为 Word 文档

相关说明 DeepSeek 风靡全球的2025年&#xff0c;估计好多人都已经试过了&#xff0c;对于理科老师而言&#xff0c;有一个使用痛点&#xff0c;就是如何将 AI 输出的 mathjax 格式的符号转化为我们经常使用的 mathtype 格式的&#xff0c;以下举例说明。 温馨提示&#xff1…...

AI 产品的 MVP 构建逻辑:Prompt 工程 ≠ 产品工程?

一、引言&#xff1a;技术细节与系统工程的本质分野 在 AI 产品开发的战场中&#xff0c;Prompt 工程与产品工程的边界模糊正在引发深刻的认知革命。当工程师们沉迷于优化 “请用三段式结构分析用户需求” 这类提示词时&#xff0c;产品经理却在思考如何通过用户行为数据验证 …...

Go语言开发的GMQT物联网MQTT消息服务器(mqtt Broker)支持海量MQTT连接和快速低延时消息传输-提供源码可二次开发定制需求

关于GMQT物联网MQTT消息平台 GoFly社区推出《GMQT物联网MQTT消息平台》&#xff0c;完全使用高性能的Go语言编写&#xff0c;内嵌数据库(不依赖三方库)&#xff0c; 全面支持MQTT的v3.0.0、v3.1.1以及完全兼容 MQTT v5 功能。利用Go语言高并发性、高效利用服务器资源、跨平台支…...

JavaScript es6 语法 map().filter() 链式调用,语法解析 和常见demo

摘要&#xff1a; map&#xff1a;遍历数组并对每个元素执行回调函数&#xff0c;返回一个新数组 filter&#xff1a;对 map 返回的数组进行过滤&#xff0c;返回满足条件的元素组成的新数组 1.数字数组处理 const numbers [1, 2, 3, 4, 5];// 先平方&#xff0c;再筛选偶数…...

leetcode2221. 数组的三角和-medium

1 题目&#xff1a;数组的三角和 官方标定难度&#xff1a;中 给你一个下标从 0 开始的整数数组 nums &#xff0c;其中 nums[i] 是 0 到 9 之间&#xff08;两者都包含&#xff09;的一个数字。 nums 的 三角和 是执行以下操作以后最后剩下元素的值&#xff1a; nums 初始…...

Express教程【001】:Express创建基本的Web服务器

文章目录 1、初识express1.1 什么是Express1.2 主要特点1.3 Express的基本使用1.3.1 安装1.3.2 创建基本的Web服务器 1、初识express 目标&#xff1a; 能够使用express.static()快速托管静态资源能够使用express路由精简项目结构能够使用常见的express中间件能够使用express创…...

asio之async_result

简介 async_result用来表示异步处理返回类型 async_result 是类模板 type&#xff1a;为类模板中声明的类型&#xff0c;对于不同的类型&#xff0c;可以使用类模板特例化&#xff0c;比如针对use_future...

代码随想录算法训练营 Day60 图论Ⅹ Bellmen_ford 系列算法

图论 题目 94. 城市间货物运输 I Bellmen_ford 队列优化算法 SPFA 大家可以发现 Bellman_ford 算法每次松弛 都是对所有边进行松弛。 但真正有效的松弛&#xff0c;是基于已经计算过的节点在做的松弛。 本图中&#xff0c;对所有边进行松弛&#xff0c;真正有效的松弛&#…...

独立机构软件第三方检测:流程、需求分析及电商软件检验要点?

独立机构承担着对软件进行第三方检测的任务&#xff0c;这一环节对于提升软件的质量和稳定性起到了极其关键的作用。检测过程拥有一套完善的流程&#xff0c;目的在于确保检测结果的精确性&#xff0c;并保障软件达到高标准。 需求分析 确保软件测试需求清晰十分关键。我们需…...

Java构建Tree并实现节点名称模糊查询

乐于学习分享… 大家加油努力 package com.tom.backtrack;import lombok.Data; import lombok.Getter;import java.util.ArrayList; import java.util.List; import java.util.Objects;/*** 树节点** author zx* date 2025-05-27 19:51*/ Data public class TreeNode {private …...

shadcn/ui

文章目录 前言✅ 核心特点&#x1f4e6; 支持组件&#xff08;常用&#xff09;&#x1f680; 安装使用&#xff08;框架支持&#xff09;初始化&#xff08;Next.js 项目为例&#xff09;添加一个组件 &#x1f9e0; 对比其他组件库&#x1f4d8; 官方资源✅ 总结✅ 功能特性&…...

华为FreeArc能和其他华为产品共用充电线吗?

最近刚买的FreeArc终于到手啦&#xff0c;看到网上有朋友说&#xff0c;这次的耳机是不附带充电线&#xff0c;开箱后发现果真如此&#xff0c;那FreeArc到底用什么规格的充电线&#xff0c;能不能和华为的Type-C数据线通用&#xff0c;我来给大家解答一下吧&#xff01; Free…...

[网页五子棋][匹配模式]创建房间类、房间管理器、验证匹配功能,匹配模式小结

文章目录 创建房间类创建房间类实现房间管理器 实现匹配器(3)验证匹配功能问题&#xff1a;匹配按钮不改变验证多开 小结 创建房间类 LOL&#xff0c;通过匹配的方式&#xff0c;自动给你加入到一个房间&#xff0c;也可手动创建游戏房间 这一局游戏&#xff0c;进行的“场所…...

实验设计与分析(第6版,Montgomery)第3章单因子实验:方差分析3.11思考题3.7 R语言解题

本文是实验设计与分析&#xff08;第6版&#xff0c;Montgomery著&#xff0c;傅珏生译) 第3章单因子实验&#xff1a;方差分析3.11思考题3.7 R语言解题。主要涉及单因子方差分析&#xff0c;正态性假设检验&#xff0c;残差与拟合值的关系图&#xff0c;平方根变换。 X<-c(…...

【知识点】第2章:Python程序实例解析

文章目录 知识点整理Python程序语法元素分析 练习题判断题填空题选择题 知识点整理 Python程序语法元素分析 Python程序包括格式框架、注释、变量、表达式、分支语句、循环语句、函数等语法元素。 程序的格式框架 Python语言采用严格的 “缩进” 来表明程序的格式框架。缩进…...

从解决一个分享图片生成的历史bug出发,详解LayoutInflater和View.post的工作原理

问题背景 最近在项目中遇到一个问题&#xff1a;在档口分享功能中&#xff0c;需要动态生成一个分享图片。代码是这样写的&#xff1a; // 项目中的代码 val shareView LayoutInflater.from(thisStallMainActivityV1).inflate(R.layout.share_header_stall_main_layout, nul…...