在创建用户时,必须在User和Email表中插入一行。它们中的任何一个都可能失败(唯一约束)。我怎样才能找出失败的原因?我的想法是在插入或解析返回的SqlException之前使用锁并查询数据库(我不想这样做)。
编辑:我应该提到这将同时在多台机器上运行,我希望它支持不同的数据库。
编辑2:我的解决方案最终使用锁来检查重复项。存储过程是一种选择,但我不想将业务逻辑放入数据库。我评论了其他人,我知道网络农场中的竞争条件,但这种情况的罕见性并不能保证进一步的工作。
答案 0 :(得分:2)
应使用异常处理来捕获非主要方案,例如数据库已关闭或命令超出超时。如果您对用户唯一且电子邮件是唯一的约束,您应该在提交数据之前对它们进行测试。依赖检查/索引约束作为处理这些场景的方法将在longrun中产生混淆。此外,错误处理的关键最佳实践是永远不要让最终用户知道错误发生原因的细节。
答案 1 :(得分:1)
使用存储过程,并检查将导致事务在事务中失败的已知条件,例如:
BEGIN TRANSACTION
IF EXISTS (SELECT UserID FROM User WHERE UserID = @UserID)
BEGIN
ROLLBACK
SELECT 'User already exists in the User table.'
RETURN 1
END
IF EXISTS (SELECT UserID FROM Email WHERE UserID = @UserID)
BEGIN
ROLLBACK
SELECT 'User already exists in the Email table.'
RETURN 2
END
INSERT INTO User ...
INSERT INTO Email ...
COMMIT
RETURN 0
这实际上是使用两种机制来返回错误(返回代码和结果集);通常只有使用它才有意义。
答案 2 :(得分:0)
你应该在某个地方的业务逻辑中捕捉到这种情况,而不是仅仅依靠数据库来为你提供你正在寻找的错误。
答案 3 :(得分:0)
我不会依赖表约束来进行数据验证。在插入之前使用查询验证数据。例外是要创建的昂贵对象。此外,我更喜欢有适当的约束来防止无效数据但不能验证。我认为约束是桌子的安全带。只有在出现错误时才应调用它。业务逻辑应在插入之前验证所有数据。如果您的目标数据库可能不支持存储过程,请不要依赖存储过程。
这是我处理它的一般方法。
private void Form1_Load(object sender, EventArgs e)
{
OleDbConnection conn = null;
OleDbTransaction t = null;
try
{
conn = new OleDbConnection("a database");
conn.Open();
//query both tables to prevent insert fail,
//obviously UserID should be parameter.
var cmd = new OleDbCommand("select count(*) from User where UserID = 1", conn);
var count = (double)cmd.ExecuteScalar();
cmd.CommandText = "select count(*) from Email where UserID = 1";
count += (double)cmd.ExecuteScalar();
if (count != 0)
{
MessageBox.Show("Record exists");
return;
}
t = conn.BeginTransaction();
//insert logic goes here
t.Commit();
}
catch (Exception x)
{
//we still need catch block, someone else may have updated the
//data after you checked but before you insert or db open may
//fail
MessageBox.Show(x.Message);
if (t != null)
t.Rollback();
}
finally
{
if (conn != null)
conn.Close();
}
}