C# 检查级联存储错误的正确方法是什么?

C# 检查级联存储错误的正确方法是什么?,c#,sql,llblgenpro,C#,Sql,Llblgenpro,创建用户时,必须在用户和电子邮件表中插入一行。它可能在其中任何一种情况下失败(唯一约束)。我怎样才能找出失败的原因?我的想法是在插入之前使用锁并查询数据库,或者解析返回的SqlException(我不希望这样做) 编辑:我应该提到这将在多台机器上同时运行,我希望它支持不同的数据库 编辑2:我的解决方案最终使用了一个锁来检查重复项。存储过程是一种选择,但我不想将业务逻辑放入数据库中。我为其他人评论说,我知道网络农场的比赛条件,但是这种罕见的情况并不需要进一步的工作。你应该在你的业务逻辑中的某个地方

创建用户时,必须在用户和电子邮件表中插入一行。它可能在其中任何一种情况下失败(唯一约束)。我怎样才能找出失败的原因?我的想法是在插入之前使用锁并查询数据库,或者解析返回的SqlException(我不希望这样做)

编辑:我应该提到这将在多台机器上同时运行,我希望它支持不同的数据库


编辑2:我的解决方案最终使用了一个锁来检查重复项。存储过程是一种选择,但我不想将业务逻辑放入数据库中。我为其他人评论说,我知道网络农场的比赛条件,但是这种罕见的情况并不需要进一步的工作。

你应该在你的业务逻辑中的某个地方捕捉到这种情况,而不是仅仅依靠数据库来给出你正在寻找的错误。

你应该在你的业务逻辑中的某个地方捕捉到这种情况,而不是仅仅依靠数据库数据库提供您正在查找的错误。

应使用异常处理来捕获非主要场景,例如数据库关闭或命令超过超时时间。如果您对用户的唯一性和电子邮件的唯一性有限制,那么您应该在提交数据之前对其进行测试。依靠检查/索引约束作为处理这些场景的一种方式,将在长期内造成混乱。此外,错误处理的一个关键最佳实践是永远不要让最终用户知道错误发生的具体原因

应使用异常处理来捕获非主要场景,例如数据库关闭或命令超过超时时间。如果您对用户的唯一性和电子邮件的唯一性有限制,那么您应该在提交数据之前对其进行测试。依靠检查/索引约束作为处理这些场景的一种方式,将在长期内造成混乱。此外,错误处理的一个关键最佳实践是永远不要让最终用户知道错误发生的具体原因

使用存储过程,并检查会使事务在事务内部失败的已知条件,例如:

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

这实际上是使用两种机制返回错误(返回代码和结果集);通常只使用一个存储过程是有意义的。

使用存储过程,并检查事务内部是否存在导致事务失败的已知条件,例如:

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

这实际上是使用两种机制返回错误(返回代码和结果集);通常只有使用一个才有意义。

我不会依赖表约束进行数据验证。在插入之前使用查询验证数据。异常是创建的昂贵对象。另外,我更喜欢有适当的约束来防止无效数据,而不是验证。我认为约束是桌子的安全带。只有在发生错误时才应该调用它。业务逻辑应该在插入之前验证所有数据。如果目标数据库可能不支持存储过程,请不要依赖存储过程

这是我处理这件事的一般方法

        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();
        }
    }

我不会依赖表约束进行数据验证。在插入之前使用查询验证数据。异常是创建的昂贵对象。另外,我更喜欢有适当的约束来防止无效数据,而不是验证。我认为约束是桌子的安全带。只有在发生错误时才应该调用它。业务逻辑应该在插入之前验证所有数据。如果目标数据库可能不支持存储过程,请不要依赖存储过程

这是我处理这件事的一般方法

        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();
        }
    }

如果代码在多台机器上运行,一台机器进行检查,在它可以保存另一台机器保存该电子邮件之前,会发生什么情况?您需要找到一种方法通过回滚提交来进行补偿。它将被回滚,但在这种情况下,我不知道回滚的原因。如果代码在多台机器上运行,一台机器进行检查,然后在保存另一台机器之前保存该电子邮件,会发生什么情况?您需要找到一种方法,通过回滚提交来进行补偿。它将被回滚,但是在这种情况下,我不知道回滚的原因。案例可能是失败的原因,需要传递给服务类进行日志记录(不是我的案例,只是一个示例)。案例可能是失败的原因,需要传递给服务类进行日志记录(不是我的案例,只是一个示例)