使用外键REFERENCE约束删除行时用户友好的错误消息

时间:2012-01-18 12:36:54

标签: c# .net sql validation ado.net

处理具有FK REFERENCE约束的db行的最佳做法是什么?我的目标是向最终用户提供更加用户友好的错误消息。请注意,我不想t want to delete department with employees and that I don想要在表上进行级联删除。

例如,如果我们有两个表:

-- Department table
CREATE TABLE [dbo].[Department](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Name] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
 CONSTRAINT [PK_Department] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

-- Employee table
CREATE TABLE [dbo].[Employee](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Name] [nchar](10) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [DepartmentId] [int] NULL,
 CONSTRAINT [PK_Employee] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

GO
ALTER TABLE [dbo].[Employee]  WITH CHECK ADD  CONSTRAINT [FK_Employee_Department] FOREIGN KEY([DepartmentId])
REFERENCES [dbo].[Department] ([Id])
GO
ALTER TABLE [dbo].[Employee] CHECK CONSTRAINT [FK_Employee_Department]

如果想删除department表中的行,那么在employee表中引用该行。应该怎么做?

  1. 在执行DELETE语句之前,检查employee表中是否引用了行,并优雅地将错误返回给GUI(如果需要,可以使用eployee列表)

  2. 执行DELETE语句并捕获异常,如:

    catch (SqlException ex) 
    { 
        switch (ex.Number) 
           case 547: HandleErrorGracefully()
    } 
    
  3. 其他方式?

  4. 如果有人拥有应用程序示例的代码/链接,那会很好......

4 个答案:

答案 0 :(得分:5)

选项3:同时执行1和2

有人可能会在检查(通过)和删除失败之间插入另一个进程。

在这种情况下,您可以向用户说“抱歉,出现问题”(但记录下来),看看他们是否想再试一次。然后检查将拦截它。

答案 1 :(得分:2)

更好!

显示其中的部门和员工人数,只有员工人数为0才启用删除选项。

依靠引用完整性来停止编码错误,而不是用户错误...

答案 2 :(得分:0)

@ gbn的答案是一个很好的方法。但您也可以将ON DELETE CASCADE放在外键关系上,并询问用户“您确定要删除此部门及其所有员工吗?”。

答案 3 :(得分:0)

我几天/几个月也有同样的问题。经过大量的谷歌搜索后,我开始使用SqlHelper类在DAL中翻译错误消息。

我使用以下方式解决了这个问题。

创建了一些派生自Exception类的类,如diagram

所示
DalExceptionClass:Exception

ForeignKeyException: DalException

在SqlHelper

public void ExecuteNonQuery(string procedure)
{
    try
    {
      con.open()
       cmd.executenonquery();
      con.close();
      //Sql Exception is raised
    }
    catch(SqlException ex)
    {
        throw TranslateException(ex);
    }
}

使用链接中的代码,我将Sql Exceptions转换为DalException而不是Exception

protected DalException TranslateException(SqlException ex) { 
    DalException dalException = null; 

    // Return the first Custom exception thrown by a RAISERROR 
    foreach (SqlError error in ex.Errors) { 
        if (error.Number >= 50000) { 
            dalException = new DalException(error.Message, ex); 
        } 
    } 

    if (dalException == null) { 
        // uses SQLServer 2000 ErrorCodes 
        switch (ex.Number) { 
            case 2601: 
                // Unique Index/Constriant Violation 
                dalException = new DalUniqueConstraintException("{0} failed, {1} must be unique", ex); 
                break; 
            default: 
                // throw a general DAL Exception 
                dalException = new DalException(ex.Message, ex); 
                break; 
        } 
    } 

    // return the error 
    return dalException; 
} 

请注意案例2601:

我在这里发送了自定义消息。

现在在我的DAL中,我将只处理DalException,它将返回异常消息

  

“{0}失败,{1}必须是唯一的”使用string.Format(msg,   “插入”,“客户”);

这将向BAL发送一个例外,因为“插入失败,客户端必须是唯一的”,这可能有点像是一条消息。

修改

这将在DAL处理

    public Client Insert()
    {
    try
       {
        _repository.Insert(client);
       } 
    catch(DalException ex)
       {
//wrap message since, error will always be in client entity as client's method is being called. Also, insertion is being failed.
         throw new BusinessException(string.Format(ex.message), "Insertion", "Client");
       }
    }

参考

How to manipulate SqlException message into user friendly message

http://www.reflectionit.nl/DAL.aspx

What should be the strategy to handle the sql exceptions?