使用SCOPE_IDENTITY时SQL出现重复记录的C#桌面应用问题
问题:调用存储过程获取SCOPE_IDENTITY时出现重复插入记录
我在开发C#桌面应用时,尝试通过SQL的SCOPE_IDENTITY()获取新增记录的ID,但数据库里出现了重复记录。以下是我的相关代码:
SQL存储过程
ALTER PROCEDURE [dbo].[ztSinifEgitimiEkle] @islev NVARCHAR(MAX), @ztSinifEgitimiDurumAtamaID INT, @ztSinifEgitimiDurumKisiID NVARCHAR(30), @ztSinifEgitimiDurumKisiTipiID TINYINT , @ztSinifEgitimiDurumKatilim BIT, @ztSinifEgitimiEgitimAdi VARCHAR(50), @ztSinifEgitimiAtamaEgitimID int, @ztSinifEgitimiAtamaEgitmenID CHAR(30), @ztSinifEgitimiAtamaKisiTipiID TINYINT, @ztSinifEgitimiAtamaTarihSaat DATETIME, @ztSinifEgitimiAtamaSüre varchar(50), @ztSinifEgitimiAtamaKategoriID INT, @ztSinifEgitimiAtamaEgitimTipi Smallint AS /* İşlev 0 ise : ztSinifEgitimiDurum2 tablosuna Insert Eder İşlev 1 ise : ztSinifEgitimi2 tablosuna Insert Eder İşlev 2 ise : ztSinifEgitimiAtama2 tablosuna Insert Eder */ IF @islev='0' BEGIN INSERT INTO [dbo].[ztSinifEgitimiDurum2] ( AtamaID, KisiID , KisiTipiID , Katilim ) VALUES ( @ztSinifEgitimiDurumAtamaID , @ztSinifEgitimiDurumKisiID , @ztSinifEgitimiDurumKisiTipiID , @ztSinifEgitimiDurumKatilim ) END IF @islev='1' BEGIN INSERT INTO [dbo].[ztSinifEgitimi2] ( [EgitimAdi] ) VALUES (@ztSinifEgitimiEgitimAdi ) SELECT SCOPE_IDENTITY() -- Son Kaydın IDsi END IF @islev='2' BEGIN INSERT INTO [dbo].[ztSinifEgitimiAtama2] ([EgitimID] ,[EgitmenID] ,[KisiTipiID] ,[TarihSaat] ,[Süre] ,[KategoriID] ,[EgitimTipi]) VALUES ( @ztSinifEgitimiAtamaEgitimID , @ztSinifEgitimiAtamaEgitmenID , @ztSinifEgitimiAtamaKisiTipiID , @ztSinifEgitimiAtamaTarihSaat , @ztSinifEgitimiAtamaSüre , @ztSinifEgitimiAtamaKategoriID , @ztSinifEgitimiAtamaEgitimTipi ) END
C#代码
private void button4_Click(object sender, EventArgs e) { try { if (egitimadi.Text.Length!=0) { if (baglan.State == ConnectionState.Closed) baglan.Open(); SqlCommand sqlCmd = new SqlCommand("ztSinifEgitimiEkle", baglan); sqlCmd.CommandType = CommandType.StoredProcedure; sqlCmd.Parameters.AddWithValue("@islev", 1); sqlCmd.Parameters.AddWithValue("@ztSinifEgitimiDurumAtamaID", 0); sqlCmd.Parameters.AddWithValue("@ztSinifEgitimiDurumKisiID", ""); sqlCmd.Parameters.AddWithValue("@ztSinifEgitimiDurumKisiTipiID", 8); sqlCmd.Parameters.AddWithValue("@ztSinifEgitimiDurumKatilim", 1); sqlCmd.Parameters.AddWithValue("@ztSinifEgitimiEgitimAdi", egitimadi.Text); sqlCmd.Parameters.AddWithValue("@ztSinifEgitimiAtamaEgitimID", 1); sqlCmd.Parameters.AddWithValue("@ztSinifEgitimiAtamaEgitmenID", ""); sqlCmd.Parameters.AddWithValue("@ztSinifEgitimiAtamaKisiTipiID", 8); sqlCmd.Parameters.AddWithValue("@ztSinifEgitimiAtamaTarihSaat", "2017-01-01"); sqlCmd.Parameters.AddWithValue("@ztSinifEgitimiAtamaSüre", ""); sqlCmd.Parameters.AddWithValue("@ztSinifEgitimiAtamaKategoriID", 0); sqlCmd.Parameters.AddWithValue("@ztSinifEgitimiAtamaEgitimTipi", 0); String LastID = sqlCmd.ExecuteScalar().ToString(); sqlCmd.ExecuteNonQuery(); SqlCommand sqlCmd2 = new SqlCommand("ztSinifEgitimiEkle", baglan); sqlCmd2.CommandType = CommandType.StoredProcedure; sqlCmd2.Parameters.AddWithValue("@islev", 2); sqlCmd2.Parameters.AddWithValue("@ztSinifEgitimiDurumAtamaID", 0); sqlCmd2.Parameters.AddWithValue("@ztSinifEgitimiDurumKisiID", ""); sqlCmd2.Parameters.AddWithValue("@ztSinifEgitimiDurumKisiTipiID", 8); sqlCmd2.Parameters.AddWithValue("@ztSinifEgitimiDurumKatilim", 1); sqlCmd2.Parameters.AddWithValue("@ztSinifEgitimiEgitimAdi", ""); sqlCmd2.Parameters.AddWithValue("@ztSinifEgitimiAtamaEgitimID", LastID); sqlCmd2.Parameters.AddWithValue("@ztSinifEgitimiAtamaEgitmenID", ""); sqlCmd2.Parameters.AddWithValue("@ztSinifEgitimiAtamaKisiTipiID", 8); sqlCmd2.Parameters.AddWithValue("@ztSinifEgitimiAtamaTarihSaat", "2017-01-01"); sqlCmd2.Parameters.AddWithValue("@ztSinifEgitimiAtamaSüre", ""); sqlCmd2.Parameters.AddWithValue("@ztSinifEgitimiAtamaKategoriID", 0); sqlCmd2.Parameters.AddWithValue("@ztSinifEgitimiAtamaEgitimTipi", 0); sqlCmd2.ExecuteNonQuery(); button4.Enabled = false; yenile(); } else { MessageBox.Show("Lütfen Tüm Bilgileri Doldurunuz."); } } catch (Exception ex) { MessageBox.Show(ex.Message, "Hata Mesajı"); } finally { baglan.Close(); } }
问题根源
看你的C#代码,在执行sqlCmd.ExecuteScalar()获取新增记录ID之后,又额外调用了sqlCmd.ExecuteNonQuery()——这相当于两次执行了同一个存储过程的islev=1分支!第一次ExecuteScalar已经完成了插入操作并返回了ID,第二次ExecuteNonQuery再次触发插入,直接导致数据库里多出了一条重复记录。
另外还有个小问题:SCOPE_IDENTITY()返回的是decimal类型,你直接转成string再作为参数传给下一个存储过程,可能会引发类型不匹配的潜在问题。
修复方案
- 移除多余的
ExecuteNonQuery()调用:只保留ExecuteScalar()来执行存储过程并获取返回的ID。 - 正确转换ID类型:把返回的结果转换成
int类型,再传入下一个存储过程。 - 使用
using语句管理资源:确保SqlCommand和连接资源能被正确释放,避免连接泄漏。
修改后的C#代码
private void button4_Click(object sender, EventArgs e) { try { if (!string.IsNullOrEmpty(egitimadi.Text)) { using (var baglan = new SqlConnection("你的连接字符串")) // 建议在这里初始化连接,用using自动释放 { baglan.Open(); // 第一次调用:插入ztSinifEgitimi2并获取ID using (SqlCommand sqlCmd = new SqlCommand("ztSinifEgitimiEkle", baglan)) { sqlCmd.CommandType = CommandType.StoredProcedure; sqlCmd.Parameters.AddWithValue("@islev", 1); sqlCmd.Parameters.AddWithValue("@ztSinifEgitimiDurumAtamaID", 0); sqlCmd.Parameters.AddWithValue("@ztSinifEgitimiDurumKisiID", ""); sqlCmd.Parameters.AddWithValue("@ztSinifEgitimiDurumKisiTipiID", 8); sqlCmd.Parameters.AddWithValue("@ztSinifEgitimiDurumKatilim", 1); sqlCmd.Parameters.AddWithValue("@ztSinifEgitimiEgitimAdi", egitimadi.Text); sqlCmd.Parameters.AddWithValue("@ztSinifEgitimiAtamaEgitimID", 1); sqlCmd.Parameters.AddWithValue("@ztSinifEgitimiAtamaEgitmenID", ""); sqlCmd.Parameters.AddWithValue("@ztSinifEgitimiAtamaKisiTipiID", 8); sqlCmd.Parameters.AddWithValue("@ztSinifEgitimiAtamaTarihSaat", new DateTime(2017,1,1)); // 用DateTime类型而非字符串 sqlCmd.Parameters.AddWithValue("@ztSinifEgitimiAtamaSüre", ""); sqlCmd.Parameters.AddWithValue("@ztSinifEgitimiAtamaKategoriID", 0); sqlCmd.Parameters.AddWithValue("@ztSinifEgitimiAtamaEgitimTipi", 0); // 正确转换SCOPE_IDENTITY的返回值 int lastID = Convert.ToInt32(sqlCmd.ExecuteScalar()); // 第二次调用:插入ztSinifEgitimiAtama2 using (SqlCommand sqlCmd2 = new SqlCommand("ztSinifEgitimiEkle", baglan)) { sqlCmd2.CommandType = CommandType.StoredProcedure; sqlCmd2.Parameters.AddWithValue("@islev", 2); sqlCmd2.Parameters.AddWithValue("@ztSinifEgitimiDurumAtamaID", 0); sqlCmd2.Parameters.AddWithValue("@ztSinifEgitimiDurumKisiID", ""); sqlCmd2.Parameters.AddWithValue("@ztSinifEgitimiDurumKisiTipiID", 8); sqlCmd2.Parameters.AddWithValue("@ztSinifEgitimiDurumKatilim", 1); sqlCmd2.Parameters.AddWithValue("@ztSinifEgitimiEgitimAdi", ""); sqlCmd2.Parameters.AddWithValue("@ztSinifEgitimiAtamaEgitimID", lastID); // 传入int类型的ID sqlCmd2.Parameters.AddWithValue("@ztSinifEgitimiAtamaEgitmenID", ""); sqlCmd2.Parameters.AddWithValue("@ztSinifEgitimiAtamaKisiTipiID", 8); sqlCmd2.Parameters.AddWithValue("@ztSinifEgitimiAtamaTarihSaat", new DateTime(2017,1,1)); sqlCmd2.Parameters.AddWithValue("@ztSinifEgitimiAtamaSüre", ""); sqlCmd2.Parameters.AddWithValue("@ztSinifEgitimiAtamaKategoriID", 0); sqlCmd2.Parameters.AddWithValue("@ztSinifEgitimiAtamaEgitimTipi", 0); sqlCmd2.ExecuteNonQuery(); } button4.Enabled = false; yenile(); } } } else { MessageBox.Show("Lütfen Tüm Bilgileri Doldurunuz."); } } catch (Exception ex) { MessageBox.Show(ex.Message, "Hata Mesajı"); } }
额外建议
- 存储过程里的
@islev参数是NVARCHAR(MAX),但你传入的是整数,建议把@islev改成INT类型,这样更严谨,避免类型转换问题。 - 尽量不要用
AddWithValue,因为它会自动推断参数类型,可能引发性能或类型匹配问题,建议用SqlParameter指定明确的类型和长度,比如:sqlCmd.Parameters.Add("@ztSinifEgitimiEgitimAdi", SqlDbType.VarChar, 50).Value = egitimadi.Text;
内容的提问来源于stack exchange,提问作者Yunus
相关产品推荐
相关产品推荐

