Files
IOT_SIM/runtime/Sim.Data/SqlClient/SqlDataAdapter.cs
T
xuyueandClaude Opus 5.5 670abe5921
CI/CD / test (push) Canceled after 0s
CI/CD / deploy (push) Canceled after 0s
项目 6 数据存储:仿冒 SqlClient(T-SQL 转写成 SQLite)、数据库页面、DataGridView、DateTimePicker
- 每个学生一套数据库,长期保存(data/databases/<用户编号>/库名.db);学生程序运行时挂进沙箱可读写
- Sim.Data:SqlConnection / SqlCommand / SqlDataReader / SqlDataAdapter / SqlCommandBuilder / SqlParameter;
  连接字符串只按库名打开学生自己的库;ScriptDom 解析 T-SQL 后按白名单转写成 SQLite 语句,
  STRICT 表 + 元数据表还原 SQL Server 的列类型,报错编号和中文信息与 SQL Server 一致;
  INSERT 不写列名时跳过自增列,日期统一格式(书上 DateTime.ToString() 拼接的日期能正确比较)
- 常驻 SQL 服务(Sim.Runner sql-server):数据库页面的查询、对象资源管理器,评分前按实验 database.sql 建库
- 数据库页面(代替 SSMS):对象资源管理器、查询窗口、编辑前 200 行、表设计器、恢复实验初始数据
- DataGridView(绑定 DataView/DataTable/DataSet,单元格修改写回 DataTable)、DateTimePicker;设计器工具箱加入
- 评分:每个检查项一份新建的实验库;新增点 / 修改表格单元格、选日期、表格里有 / 没有某行的检查
- 示例:项目 6 任务 2、3;时间格式改成和中文 Windows 一致(9:05:03);异常堆栈不暴露服务器路径
- 测试:数据库端到端、接口(每人只看到自己的库、恢复实验数据)、评分出错情况、T-SQL 生成

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 09:18:56 +08:00

121 lines
5.4 KiB
C#

using System.Data.Common;
namespace System.Data.SqlClient;
public delegate void SqlRowUpdatingEventHandler(object sender, SqlRowUpdatingEventArgs e);
public delegate void SqlRowUpdatedEventHandler(object sender, SqlRowUpdatedEventArgs e);
public sealed class SqlRowUpdatingEventArgs : RowUpdatingEventArgs
{
public SqlRowUpdatingEventArgs(DataRow row, IDbCommand? command, StatementType statementType, DataTableMapping tableMapping)
: base(row, command, statementType, tableMapping) { }
public new SqlCommand? Command
{
get => (SqlCommand?)base.Command;
set => base.Command = value;
}
}
public sealed class SqlRowUpdatedEventArgs : RowUpdatedEventArgs
{
public SqlRowUpdatedEventArgs(DataRow row, IDbCommand? command, StatementType statementType, DataTableMapping tableMapping)
: base(row, command, statementType, tableMapping) { }
public new SqlCommand? Command => (SqlCommand?)base.Command;
}
/// <summary>在数据库和 DataSet 之间搬运数据:Fill 读出来,Update 把 DataTable 里改过的行写回去</summary>
public sealed class SqlDataAdapter : DbDataAdapter, ICloneable
{
public SqlDataAdapter() { }
public SqlDataAdapter(SqlCommand selectCommand) => SelectCommand = selectCommand;
public SqlDataAdapter(string selectCommandText, SqlConnection selectConnection) => SelectCommand = new SqlCommand(selectCommandText, selectConnection);
public SqlDataAdapter(string selectCommandText, string selectConnectionString) =>
SelectCommand = new SqlCommand(selectCommandText, new SqlConnection(selectConnectionString));
public new SqlCommand? SelectCommand
{
get => (SqlCommand?)base.SelectCommand;
set => base.SelectCommand = value;
}
public new SqlCommand? InsertCommand
{
get => (SqlCommand?)base.InsertCommand;
set => base.InsertCommand = value;
}
public new SqlCommand? UpdateCommand
{
get => (SqlCommand?)base.UpdateCommand;
set => base.UpdateCommand = value;
}
public new SqlCommand? DeleteCommand
{
get => (SqlCommand?)base.DeleteCommand;
set => base.DeleteCommand = value;
}
public event SqlRowUpdatingEventHandler? RowUpdating;
public event SqlRowUpdatedEventHandler? RowUpdated;
protected override RowUpdatingEventArgs CreateRowUpdatingEvent(DataRow dataRow, IDbCommand? command, StatementType statementType, DataTableMapping tableMapping) =>
new SqlRowUpdatingEventArgs(dataRow, command, statementType, tableMapping);
protected override RowUpdatedEventArgs CreateRowUpdatedEvent(DataRow dataRow, IDbCommand? command, StatementType statementType, DataTableMapping tableMapping) =>
new SqlRowUpdatedEventArgs(dataRow, command, statementType, tableMapping);
protected override void OnRowUpdating(RowUpdatingEventArgs value) => RowUpdating?.Invoke(this, (SqlRowUpdatingEventArgs)value);
protected override void OnRowUpdated(RowUpdatedEventArgs value) => RowUpdated?.Invoke(this, (SqlRowUpdatedEventArgs)value);
object ICloneable.Clone() => new SqlDataAdapter { SelectCommand = SelectCommand, InsertCommand = InsertCommand, UpdateCommand = UpdateCommand, DeleteCommand = DeleteCommand };
}
/// <summary>按 SqlDataAdapter 的查询语句自动生成 INSERT / UPDATE / DELETE(要求查询包含主键列)</summary>
public sealed class SqlCommandBuilder : DbCommandBuilder
{
public SqlCommandBuilder()
{
QuotePrefix = "[";
QuoteSuffix = "]";
}
public SqlCommandBuilder(SqlDataAdapter adapter) : this() => DataAdapter = adapter;
public new SqlDataAdapter? DataAdapter
{
get => (SqlDataAdapter?)base.DataAdapter;
set => base.DataAdapter = value;
}
public new SqlCommand GetInsertCommand() => (SqlCommand)base.GetInsertCommand();
public new SqlCommand GetInsertCommand(bool useColumnsForParameterNames) => (SqlCommand)base.GetInsertCommand(useColumnsForParameterNames);
public new SqlCommand GetUpdateCommand() => (SqlCommand)base.GetUpdateCommand();
public new SqlCommand GetUpdateCommand(bool useColumnsForParameterNames) => (SqlCommand)base.GetUpdateCommand(useColumnsForParameterNames);
public new SqlCommand GetDeleteCommand() => (SqlCommand)base.GetDeleteCommand();
public new SqlCommand GetDeleteCommand(bool useColumnsForParameterNames) => (SqlCommand)base.GetDeleteCommand(useColumnsForParameterNames);
protected override void ApplyParameterInfo(DbParameter parameter, DataRow row, StatementType statementType, bool whereClause) { }
protected override string GetParameterName(int parameterOrdinal) => "@p" + parameterOrdinal.ToString(Globalization.CultureInfo.InvariantCulture);
protected override string GetParameterName(string parameterName) => "@" + parameterName;
protected override string GetParameterPlaceholder(int parameterOrdinal) => GetParameterName(parameterOrdinal);
public override string QuoteIdentifier(string unquotedIdentifier) => "[" + unquotedIdentifier.Replace("]", "]]") + "]";
protected override void SetRowUpdatingHandler(DbDataAdapter adapter)
{
if (adapter is not SqlDataAdapter sql) throw new InvalidOperationException("SqlCommandBuilder 只能用于 SqlDataAdapter。");
if (adapter == base.DataAdapter) sql.RowUpdating += Handler;
else sql.RowUpdating -= Handler;
}
private void Handler(object sender, SqlRowUpdatingEventArgs e) => RowUpdatingHandler(e);
}