RAISERROR从未使用具有适当严重性的EF 6

时间:2017-03-31 21:56:52

标签: c# sql sql-server entity-framework stored-procedures

我觉得我要废弃整个项目并重新开始,因为这给我带来了挫折感。所以我非常感谢任何人的见解......

我有SQL Server存储过程,需要进行某些逻辑检查才能继续。如果这些检查失败,则调用RAISERROR()并返回SP。以下是其中一项检查的示例:

IF DATALENGTH(@LastName) < 2
BEGIN
    SELECT @err_msg = CONVERT(NVARCHAR(200), @LastName);
    RAISERROR('The "Single / Last Name" param provided is too short: %s',16,1, @err_msg);
    RETURN;
END

使用EF 6 model-first将此SP导入到C#项目中。 SP将导入模型的“函数导入”部分。导入上述SP的示例(简化参数):

public virtual ObjectResult<Nullable<int>> CreatePendingContact(string lastName)
    {
        var lastNameParameter = lastName != null ?
            new ObjectParameter("LastName", lastName) :
            new ObjectParameter("LastName", typeof(string));

        return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<Nullable<int>>("CreatePendingContact", lastNameParameter);
    }

可以在C#代码中调用此函数,并调用try / catch块(其中_model是EF生成的dbContext子类):

try
    {
        var result = _model.CreatePendingContact(
            LastName
            );
    }
catch (SqlException ex)
    {
        Debug.WriteLine(ex.Message);
        throw;
    }

现在我应该可以传递LastName =&#34;&#34; (或其他任何&lt; 2 chars)并使用上面的RAISERROR文本返回一个SqlException - 但我能做的任何事情都没有触发它的SqlException!

到目前为止,这是我尝试过的事情:

  • 使用THROW关键字而不是简单的RETURN(没有捕获)
  • 运行SQL Server Profiler,抓取C#客户端发送的查询,并在SSMS中手动执行该查询(导致错误在输出中正确显示)
  • 修改生成的EF以在ExecuteFunction&lt;&gt;()行添加try / catch块(没有捕获)

现在,大多数在线主题都会将严重性级别设置为16来解决问题,因此我不知道应该设置或寻找的其他内容...

2 个答案:

答案 0 :(得分:0)

这是我实施的解决方案,但感觉不干净。此外,我不明白为什么我必须对生成的代码进行这些编辑才能捕获SqlException - 如果有人有改进,那肯定会受到欢迎:

第1部分:确保将模型的“函数导入”下的导入函数设置为返回“无”的集合作为选项。 (我还没有得到返回对象的SP)

第2部分:将EF生成的函数代码从上面的返回行修改为:

try
{
    return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction("CreatePendingContact", lastNameParameter);
}
catch (Exception ex)
{
    if (ex.InnerException is SqlException) { throw ex.InnerException; }
    else { throw; }
}

此更改仅转发SqlExceptions,同时仍在本地抛出所有其他异常。这是一个超级简单的案例,但到目前为止似乎有效......

答案 1 :(得分:0)

当我使用您的示例代码进行测试时

IF DATALENGTH(@LastName) < 2
BEGIN
    SELECT @err_msg = CONVERT(NVARCHAR(200), @LastName);
    RAISERROR('The "Single / Last Name" param provided is too short: %s',16,1, @err_msg);
    RETURN;
END

我的过程没有失败。当我像下面那样使用LTRIM和RTRIM时它确实失败了

IF DATALENGTH(LTRIM(RTRIM(@LastName))) < 2
BEGIN
    SELECT @err_msg = CONVERT(NVARCHAR(200), @LastName);

RAISERROR(提供的“单个/姓氏”参数太短:%s',16,1,@ err_msg);     结束

通过为错误和清理执行提供返回值,有一种更可靠的方法来编写过程。然后在.Net中获取返回值并对其进行评估。下面是修改后的存储过程来演示我的意思。

CREATE Procedure dbo.TestError @LastName Char(10)
AS
BEGIN
DECLARE @err_msg nvarchar(200)
IF DATALENGTH(LTRIM(RTRIM(@LastName))) < 2
BEGIN
    SELECT @err_msg = CONVERT(NVARCHAR(200), @LastName);
    GOTO ErrorExit   
END

CleanExit:
BEGIN 
  RETURN 0
END

ErrorExit:
BEGIN
   RAISERROR('The "Single / Last Name" param provided is too short: %s',16,1, @err_msg);
    RETURN 1;
END
END