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

发布于 2024-08-06 13:55:39 字数 259 浏览 4 评论 0原文

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

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

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

On creation of a user, a row must be inserted into both the User and Email table. It can fail in either of them(unique constraints). How can I find out which is the reason for failure? My thoughts have been using a lock and querying the database prior to the inserts or parsing the SqlException that comes back(which I'd prefer not to do).

Edit: I should have mentioned this will be running on several machines simultaneously, and I would like it to support different databases.

Edit 2: My solution ended up being using a lock around checking for duplicates. Stored procedures was an option, but I didn't want to place business logic into the database. I commented for others that I was aware of the race conditions in the web farm, but the rarity of the circumstances didn't warrant further work.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

无边思念无边月 2024-08-13 13:55:39

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

Exception handling should be used to capture non-prime scenarios such as the database is down or a command overran the timeout. If you have constraints around User being unique and the Email being unique you should really test for them before you do your submission of data. Relying on check/index constraints as a way to handle these scenarios is going to create confusion in the longrun. Besides, a key best practice in error handling is to never let the end user know the particulars of why an error had occurred.

昔日梦未散 2024-08-13 13:55:39

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

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

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

Use a stored procedure, and check for known conditions that will make the transaction fail inside the transaction, e.g.:

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

This actually is using two mechanisms for returning an error (return code and result set); it usually only makes sense to use one.

一萌ing 2024-08-13 13:55:39

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

You should be catching that kind of case in your business logic somewhere rather than relying solely on the database to give you the error you're looking for.

话少情深 2024-08-13 13:55:39

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

这是我处理它的一般方法。

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

I would not rely on the table constraints for data validation. Validate the data with a query before the insert. Exceptions are expensive objects to create. Also, I prefer having constraints in place to prevent invalid data but not to validate. I think of a constraint as a safety-belt for the table. It should only be invoked if something wrong happens. The business logic should validate all data before inserting. Don't rely on stored procedures if you might be targeting databases that do not support them.

Here's the general way I would handle it.

        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();
        }
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文