- 每个学生一套数据库,长期保存(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>
121 lines
5.4 KiB
C#
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);
|
|
}
|