9

我正在调用一个存储过程,该过程将数据从 c# 插入到 sql server 数据库中。我对表有许多约束,例如唯一列等。目前我有以下代码:

try
{
   // inset data
}
catch (SqlException ex)
{
    if (ex.Message.ToLower().Contains("duplicate key"))
    {

        if (ex.Message.ToLower().Contains("url"))
        {
            return 1;
        }

        if (ex.Message.ToLower().Contains("email"))
        {
            return 2;
        }
    }

    return 3;
}

在 C# 或存储过程中插入数据之前检查列是否唯一等是更好的做法,还是让异常发生并像上面一样处理?我不是上述内容的粉丝,但正在寻找该领域的最佳实践。

4

5 回答 5

8

我将数据库约束视为最后的手段。(即,无论如何,它们应该作为维护数据完整性的备份方式出现在您的架构中。)但我想说,在您尝试将数据保存在数据库中之前,数据应该确实有效。如果没有其他原因,那么因为提供有关无效输入的反馈是一个 UI 问题,并且数据有效性错误确实不应该每次都在整个层堆栈上下冒泡。

此外,您想要对数据的形状做出多种断言,这些断言无法使用约束轻松表达。(例如,订单的状态转换。“订单只能SHIPPED来自PAID”或更复杂的场景。)也就是说,您需要使用涉及基于过程语言的检查,这些检查会复制更多的业务逻辑,并且然后让它们也报告某种错误代码,并在您的应用程序中包含更多复杂性,只是为了在架构定义中进行所有数据验证。

验证本质上很难放置在应用程序中,因为它既涉及 UI与模型架构耦合,但我倾向于在 UI 附近进行。

于 2013-03-22T18:45:39.407 回答
3

我在这里看到两个问题,这是我的看法...

数据库约束好吗?对于大型系统,它们是不可或缺的。大多数大型系统都有不止一个前端,而且并不总是使用可以共享中间层或 UI 数据检查逻辑的兼容语言。它们也可能仅在 Transact-SQL 或 PL/SQL 中具有批处理。可以在前端重复检查,但在多用户应用程序中,真正检查唯一性的唯一方法是插入记录并查看数据库的内容。与外键约束相同 - 在您尝试插入/更新/删除之前您不会真正知道。

应该允许抛出异常,还是应该替换返回值?这是问题的代码:

    try
    {
       // inset data
    }
    catch (SqlException ex)
    {
        if (ex.Message.ToLower().Contains("duplicate key"))
        {
            if (ex.Message.ToLower().Contains("url"))
            {
                return 1; // Sure, that's one good way to do it
            }
            if (ex.Message.ToLower().Contains("email"))
            {
                return 2; // Sure, that's one good way to do it
            }
        }
        return 3; // EVIL! Or at least quasi-evil :)
    }

如果您可以保证调用程序实际上会根据返回值进行操作,我认为return 1return 2最好留给您自己判断。我更喜欢为这样的情况(例如DuplicateEmailException)重新抛出自定义异常,但这只是我 - 返回值也可以解决问题。毕竟,消费者类可以像忽略返回值一样容易地忽略异常。

我反对return 3. 这意味着发生了意外的异常(数据库关闭、连接错误等)。在这里,您有一个未指定的错误,您拥有的唯一诊断信息是:“3”。想象一下在 SO 上发布一个问题,说我试图插入一行,但系统说“3”。请指教。它将在几秒钟内关闭:)

如果您不知道如何处理数据类中的异常,那么数据类的使用者就无法处理它。在这一点上,你已经非常紧张了,所以我说记录错误,然后尽可能优雅地退出,并显示“意外错误”消息。

我知道我对意外异常有些抱怨,但我处理了太多支持事件,程序员只是对数据库异常进行后续处理,当出现意外情况时,应用程序要么静默失败,要么下游失败,留下零诊断信息。很调皮。

于 2013-03-22T19:22:02.557 回答
2

我更喜欢在将数据扔到 SQL Server 并让约束冒泡出现错误之前检查潜在违规的存储过程。其原因与性能有关:

有些人会主张数据库层的约束是不必要的,因为你的程序可以做任何事情。我不会仅仅依靠您的 C# 程序来检测重复项的原因是人们会找到影响数据的方法,而无需通过您的 C# 程序。您可以稍后介绍其他程序。您可能让人们编写自己的脚本或直接与数据库交互。您是否真的要让桌子不受保护,因为它们不遵守您的业务规则?而且我认为 C# 程序也不应该只是将数据扔到桌面上并希望获得最好的结果。

如果您的业务规则发生变化,您真的要重新编译您的应用程序(或所有多个应用程序)吗?我想这取决于您的数据库受到的保护程度以及您的业务规则更改的可能性/频率。

于 2013-03-22T19:11:24.830 回答
0

I did something like this:

public class SqlExceptionHelper
{
    public SqlExceptionHelper(SqlException sqlException)
    {
        // Do Nothing.
    }

    public static string GetSqlDescription(SqlException sqlException)
    {
        switch (sqlException.Number)
        {
             case 21:
                 return "Fatal Error Occurred: Error Code 21.";
             case 53:
                 return "Error in Establishing a Database Connection: 53.";
             default
                 return ("Unexpected Error: " + sqlException.Message.ToString());
         }
     }
}

Which allows it to be reusable, and it will allow you to get the Error Codes from SQL.

Then just implement:

public class SiteHandler : ISiteHandler
{
     public string InsertDataToDatabase(Handler siteInfo)
     {
          try
          {
              // Open Database Connection, Run Commands, Some additional Checks.
          }
          catch(SqlException exception)
          {
             SqlExceptionHelper errorCompare = new SqlExceptionHelper(exception);
             return errorCompare.ToString();
          }
     }
}

Then it is providing some specific errors for common occurrences. But as mentioned above; you really should ensure that you've tested your data before you just input it into your database. That way no mismatched constraints surface or exists.

Hope it points you in a good direction.

于 2013-03-22T18:51:51.163 回答
0

Depends on what you're trying to do. Some things to think about:

  • Where do you want to handle your error? I would recommend as close to the data as possible.
  • Who do you want to know about the error? Does your user need to know that 'you've already used that ID'...?
  • etc.

Also -- constraints can be good -- I don't 100% agree with millimoose's answer on that point -- I mean, I do in the should be this way / better performance ideal -- but practically speaking, if you don't have control over your developers / qc, and especially when it comes to enforcing rules that could blow your database up (or otherwise, break dependent objects like reports, etc. if a duplicate key were to turn-up somewhere, you need some barrier against (for example) the duplicate key entry.

于 2013-03-22T18:51:53.427 回答