C#程序调用Sql Server存储过程异常处理:调用存储过程后不返回、不抛异常的解决方案

目录

一、代码解析:

二、解决方案

1、增加日志记录

2、异步操作

注意事项

3、增加超时机制

4、使用线程池

5、使用信号量或事件

6、监控数据库连接状态


在C#程序操作Sql Server数据库的实际应用中,若异常就会抛出异常,我们还能找到异常的原因,进一步去解决;但偶发的不返回、也不抛出异常的情况真是令人头疼,下面我将从C#程序代码层面去分析解决思路,先上代码

        /// <summary>
        /// 执行存储过程,返回影响的行数
        /// </summary>
        /// <param name="procedureName">存储过程名</param>
        /// <param name="parameters">自定义参数数组,根据调用数据库的种类,给参数中相应的数组赋值</param>
        /// <returns></returns>
        public int ExecuteProcedure(string procedureName, object[] aParas)
        {
            SqlCommand cmd = new SqlCommand();
            try
            {
                PrepareCommand(con, cmd, null, CommandType.StoredProcedure, procedureName, aParas);
                int rows = cmd.ExecuteNonQuery();
                cmd.Parameters.Clear();
                return rows;
            }
            catch (Exception err)
            {
                throw err;
            }
        } 

一、代码解析:

我们解析下这个函数,这个也是一个比较简单常用的方法:

这个函数的作用是执行一个存储过程,并返回该存储过程执行后影响的行数。下面是对函数各个部分的详细解释:

函数定义

/// <summary>
/// 执行存储过程,返回影响的行数
/// </summary>
/// <param name="procedureName">存储过程名</param>
/// <param name="parameters">自定义参数数组,根据调用数据库的种类,给参数中相应的数组赋值</param>
/// <returns></returns>
public int ExecuteProcedure(string procedureName, object[] aParas)
  • summary: 该函数用于执行存储过程并返回影响的行数。
  • paramprocedureName 是存储过程的名称。
  • paramparameters 是一个自定义参数数组,根据调用数据库的种类,给参数中相应的数组赋值。
  • returns: 返回存储过程执行后影响的行数。

函数实现

public int ExecuteProcedure(string procedureName, object[] aParas)
{
    //创建SqlCommand对象
    SqlCommand cmd = new SqlCommand();
    try
    {
        //调用PrepareCommand方法来设置SqlCommand对象的参数,包括连接对象con、命令对象cmd、命令类型(这里是存储过程)、存储过程名称和参数数组。
        PrepareCommand(con, cmd, null, CommandType.StoredProcedure, procedureName, aParas);
        //使用ExecuteNonQuery方法执行存储过程,并返回影响的行数。
        int rows = cmd.ExecuteNonQuery();
        //清除SqlCommand对象的参数,以便下次使用。
        cmd.Parameters.Clear();
        return rows;
    }
    catch (Exception err)
    {
        //捕获并抛出任何可能发生的异常。
        throw err;
    }
}

        这个函数的主要作用是执行一个存储过程,并返回该存储过程执行后影响的行数。它通过设置SqlCommand对象的参数,执行存储过程,并处理可能发生的异常。

二、解决方案

        问题:在实际应用中遇到这种情况,即在调用 ExecuteProcedure 方法后,没有到达 return rows; 语句,也没有进入 catch (Exception err) 块,这意味着代码在某个地方被阻塞或挂起,但没有抛出异常。这可能是由于以下几种情况之一:

  1. 死锁或资源争用

    • 数据库连接可能被其他操作占用,导致当前操作无法继续执行。
    • 数据库中的某个存储过程可能存在死锁,导致执行被阻塞。
  2. 无限循环或长时间运行的操作

    • 存储过程中可能存在无限循环或长时间运行的操作,导致执行时间过长。
  3. 网络问题

    • 数据库服务器或网络连接可能出现问题,导致请求无法完成。
  4. 线程问题

    • 当前线程可能被其他线程或操作阻塞,导致无法继续执行。
  5. 内存或资源耗尽

    • 系统内存或资源可能耗尽,导致无法继续执行。

解决方法

  1. 增加日志记录

    • 在关键步骤增加更多的日志记录,以便更好地了解代码执行到哪个步骤。
  2. 检查数据库状态

    • 检查数据库服务器的状态,查看是否有死锁或其他问题。
    • 使用数据库管理工具监控数据库性能和资源使用情况。
  3. 优化存储过程

    • 检查存储过程的逻辑,确保没有无限循环或长时间运行的操作。
    • 优化存储过程的性能,减少资源消耗。
  4. 检查网络连接

    • 确保数据库服务器和应用程序之间的网络连接稳定。
  5. 监控系统资源

    • 监控系统内存、CPU 和其他资源的使用情况,确保没有资源耗尽的情况。
  6. 使用异步操作

    • 考虑将数据库操作改为异步操作,以避免阻塞主线程。

        因为问题是偶发的,半年一年出现一次,也找不出什么原因,但是客户会较真,拿这个说事;以上方法,从代码层面只有1、6适用 ,当然我们也可以通过控制超时 时间来处理,实用解决方案如下:

1、增加日志记录

 关键步骤增加日志记录,以便更好地了解代码执行情况:

public int ExecuteProcedure(string procedureName, object[] aParas)
{
    SqlCommand cmd = new SqlCommand();
    try
    {
        // 增加日志记录
        Log("Preparing command for procedure: " + procedureName);
        PrepareCommand(con, cmd, null, CommandType.StoredProcedure, procedureName, aParas);
        
        // 增加日志记录
        Log("Executing command for procedure: " + procedureName);
        int rows = cmd.ExecuteNonQuery();
        
        // 增加日志记录
        Log("Command executed successfully, rows affected: " + rows);
        cmd.Parameters.Clear();
        return rows;
    }
    catch (Exception err)
    {
        // 增加日志记录
        Log("Exception occurred: " + err.Message);
        throw err;
    }
}

private void Log(string message)
{
    // 实现日志记录逻辑,例如写入文件或数据库
    Console.WriteLine(message);
}

        通过增加日志记录,可以更好地了解代码执行到哪个步骤,从而更容易定位问题,这种特殊情况也只能定位到走哪一行就没往下走了。

2、异步操

将数据库操作改为异步操作可以提高应用程序的响应性和性能,特别是在处理大量数据或长时间运行的操作时。

使用 ExecuteNonQueryAsync 方法

SqlCommand 类提供了 ExecuteNonQueryAsync 方法,用于异步执行 SQL 命令。以下是修改后的 ExecuteProcedure 方法:

using System.Data;
using System.Data.SqlClient;
using System.Threading.Tasks;

public async Task<int> ExecuteProcedureAsync(string procedureName, object[] aParas)
{
    SqlCommand cmd = new SqlCommand();
    try
    {
        // 增加日志记录
        Log("Preparing command for procedure: " + procedureName);
        PrepareCommand(con, cmd, null, CommandType.StoredProcedure, procedureName, aParas);
        
        // 异步执行命令
        Log("Executing command for procedure: " + procedureName);
        int rows = await cmd.ExecuteNonQueryAsync();
        
        // 增加日志记录
        Log("Command executed successfully, rows affected: " + rows);
        cmd.Parameters.Clear();
        return rows;
    }
    catch (Exception err)
    {
        // 增加日志记录
        Log("Exception occurred: " + err.Message);
        throw err;
    }
}

private void Log(string message)
{
    // 实现日志记录逻辑,例如写入文件或数据库
    Console.WriteLine(message);
}

调用异步方法

在调用异步方法时,需要使用 await 关键字,并且调用方法也必须是异步的。例如:

public async Task SomeMethodAsync()
{
    string procedureName = "YourStoredProcedureName";
    object[] parameters = new object[] { /* 参数值 */ };
    
    try
    {
        int rowsAffected = await ExecuteProcedureAsync(procedureName, parameters);
        Console.WriteLine("Rows affected: " + rowsAffected);
    }
    catch (Exception ex)
    {
        Console.WriteLine("Exception: " + ex.Message);
    }
}

注意事项

  1. 异步方法的返回类型:异步方法的返回类型通常是 Task 或 Task<T>,其中 T 是方法的返回类型。
  2. 异步上下文:确保在异步上下文中调用异步方法,例如在 async 方法中使用 await
  3. 异常处理:异步方法中的异常处理与同步方法类似,但需要使用 await 关键字捕获异常。

        通过将数据库操作改为异步操作,可以提高应用程序的性能和响应性,特别是在处理大量数据或长时间运行的操作时。

 ?????但是并不是所有程序都适合异步操作,那么将如何处理呢?

如果程序逻辑不能使用异步方式,但仍然遇到在调用 ExecuteProcedure 方法后没有到达 return rows; 语句的问题,可以考虑以下几种方法来解决这个问题:

3、增加超时机制

为数据库操作增加超时机制,确保操作不会无限期地阻塞。可以使用 SqlCommandCommandTimeout 属性来设置超时时间。

public int ExecuteProcedure(string procedureName, object[] aParas)
{
    SqlCommand cmd = new SqlCommand();
    try
    {
        // 设置命令超时时间为30秒
        cmd.CommandTimeout = 30;

        PrepareCommand(con, cmd, null, CommandType.StoredProcedure, procedureName, aParas);
        int rows = cmd.ExecuteNonQuery();
        cmd.Parameters.Clear();
        return rows;
    }
    catch (Exception err)
    {
        throw err;
    }
}

        这个超时机制在特殊情况下没有用,亲测没用,就算不设置CommandTimeout属性的值,也有默认的吧,特殊情况等多久都没返回没异常。

4、使用线程池

将数据库操作放在单独的线程中执行,并在主线程中等待结果。可以使用 ThreadPoolTask 来实现这一点。

using System.Threading;

public int ExecuteProcedure(string procedureName, object[] aParas)
{
    int rows = 0;
    bool isCompleted = false;
    Exception exception = null;

    ThreadPool.QueueUserWorkItem(_ =>
    {
        SqlCommand cmd = new SqlCommand();
        try
        {
            PrepareCommand(con, cmd, null, CommandType.StoredProcedure, procedureName, aParas);
            rows = cmd.ExecuteNonQuery();
            cmd.Parameters.Clear();
        }
        catch (Exception err)
        {
            exception = err;
        }
        finally
        {
            isCompleted = true;
        }
    });

    // 等待操作完成或超时
    DateTime startTime = DateTime.Now;
    while (!isCompleted && (DateTime.Now - startTime).TotalSeconds < 30)
    {
        Thread.Sleep(100);
    }

    if (exception != null)
    {
        throw exception;
    }

    if (!isCompleted)
    {
        throw new TimeoutException("Database operation timed out.");
    }

    return rows;
}

5、使用信号量或事件

使用信号量或事件来同步主线程和数据库操作线程。

using System.Threading;

public int ExecuteProcedure(string procedureName, object[] aParas)
{
    int rows = 0;
    Exception exception = null;
    ManualResetEvent completionEvent = new ManualResetEvent(false);

    ThreadPool.QueueUserWorkItem(_ =>
    {
        SqlCommand cmd = new SqlCommand();
        try
        {
            PrepareCommand(con, cmd, null, CommandType.StoredProcedure, procedureName, aParas);
            rows = cmd.ExecuteNonQuery();
            cmd.Parameters.Clear();
        }
        catch (Exception err)
        {
            exception = err;
        }
        finally
        {
            completionEvent.Set();
        }
    });

    // 等待操作完成或超时
    if (!completionEvent.WaitOne(TimeSpan.FromSeconds(30)))
    {
        throw new TimeoutException("Database operation timed out.");
    }

    if (exception != null)
    {
        throw exception;
    }

    return rows;
}

6、监控数据库连接状态

在执行数据库操作之前,检查数据库连接的状态,确保连接是可用的。

public int ExecuteProcedure(string procedureName, object[] aParas)
{
    SqlCommand cmd = new SqlCommand();
    try
    {
        // 检查数据库连接状态
        if (con.State != ConnectionState.Open)
        {
            con.Open();
        }

        PrepareCommand(con, cmd, null, CommandType.StoredProcedure, procedureName, aParas);
        int rows = cmd.ExecuteNonQuery();
        cmd.Parameters.Clear();
        return rows;
    }
    catch (Exception err)
    {
        throw err;
    }
}

        

通过以上方法,可以在不改变程序逻辑的情况下,增加超时机制、使用线程池、信号量或事件来解决数据库操作阻塞的问题。这些方法可以帮助确保数据库操作在合理的时间内完成,并在出现异常时及时处理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/777405.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

在Apache HTTP服务器上配置 TLS加密

安装mod_ssl软件包 [rootlocalhost conf.d]# dnf install mod_ssl -y此时查看监听端口多了一个443端口 自己构造证书 [rootlocalhost conf.d]# cd /etc/pki/tls/certs/ [rootlocalhost certs]# openssl genrsa > jiami.key [rootlocalhost certs]# openssl req -utf8 -n…

docker buildx 交叉编译设置

dockerd配置文件 /etc/docker/daemon.json设置&#xff1a; rootubuntu:/etc/docker# cat daemon.json {"insecure-registries":["localhost:5000","127.0.0.1:5000","172.16.67.111:5000"],"features": {"buildkit&…

磁力泵与屏蔽泵

1.磁力泵的工作原理 磁力传动是利用磁体能吸引铁磁物质以及磁体或磁场之间有磁力作用的特性&#xff0c;而非铁磁物质不影响或很少影响磁力的大小&#xff0c;因此可以无接触地透过非磁导体&#xff08;隔离套&#xff09;进行动力传输。磁力传动可分为同步或异步设计。 大多数…

基于深度学习的人脸多任务识别(附代码)

项目说明 本项目为人脸多任务识别(单输入&#xff0c;多输出)&#xff0c;可以同时输出人脸关键点、性别和年龄。 采用了两个算法进行应用的实现&#xff0c;人脸目标检测和人脸多任务识别。 其中人脸目标检测采用YOLOV5进行实现&#xff0c;主要对人脸部分进行截取&#xf…

查看电脑ip地址快捷键是什么?是哪个

在网络世界中&#xff0c;IP地址是每个网络设备的唯一标识&#xff0c;无论是我们的电脑、手机还是其他联网设备&#xff0c;都需要一个独特的IP地址来进行通讯。在日常生活和工作中&#xff0c;我们有时需要查看电脑的IP地址&#xff0c;以便进行网络设置、故障排查或远程连接…

WordPress主题开发进群付费主题v1.1.2 多种引流方式

全新前端UI界面&#xff0c;多种前端交互特效让页面不再单调&#xff0c;进群页面群成员数&#xff0c;群成员头像名称&#xff0c;每次刷新页面随机更新不重复&#xff0c;最下面评论和点赞也是如此随机刷新不重复 进群页面简介&#xff0c;群聊名称&#xff0c;群内展示&…

unix高级编程系列之文件I/O

背景 作为linux 开发者&#xff0c;我们不可避免会接触到文件编程。比如通过文件记录程序配置参数&#xff0c;通过字符设备与外设进行通信。因此作为合格的linux开发者&#xff0c;一定要熟练掌握文件编程。在文件编程中&#xff0c;我们一般会有两类接口函数&#xff1a;标准…

Vue异步操作发送AJAX请求

5. Vue异步操作 1 axios介绍 在Vue中发送异步请求&#xff0c;本质上还是AJAX。我们可以使用axios这个插件来简化操作&#xff01; 使用步骤 1.引入axios核心js文件。 2.调用axios对象的方法来发起异步请求。 3.调用axios对象的方法来处理响应的数据。 axios常用方法 代码…

泽州县和美环保科技有限公司——绿色环保的践行者

在环保产业蓬勃发展的今天&#xff0c;泽州县和美环保科技有限公司以其卓越的技术和强大的实力&#xff0c;成为山西省危废综合处置领域的翘楚。作为雅居乐环保集团的全资子公司&#xff0c;和美环保科技有限公司紧跟集团发展战略&#xff0c;致力于为社会提供全方位的环境服务…

阿里云安装rabbitMQ

1、首先看linux 版本 uname -a如果时centos 7 可以参考其他文档。我这里是centos 8 这个很重要 。网上全是按centos7 按照。导致我前面一直安装不上 各种问题。 2、查看rabbitmq 对应 erl 的版本下载 https://www.rabbitmq.com/docs/which-erlang 选择rabbitmq 3.11.19 选择…

https 自签证书相关生成csr文件、p12文件、crt文件、jks文件、key文件、pem文件

文章目录 前言https 自签证书相关生成csr文件、p12文件、crt文件、jks文件、key文件、pem文件1, 检查openssl的版本2. 生成私钥和证书签署请求 (CSR)3. 生成自签名证书4. 将证书和私钥转换为 PKCS12 格式的密钥库5. 创建信任库 (Truststore)6. 将 PKCS12 文件转换为 JKS 文件7.…

java对接海康摄像头

现在制造业很多都是用的海康的摄像头&#xff0c;作为程序员有时候需要对接海康摄像头&#xff0c;实现门禁访问控制&#xff0c;监控预览&#xff0c;录像文件下载等功能。 一、开发环境准备 在海康官网下载SDK开发文档及库文件&#xff1a; https://open.hikvision.com/dow…

关于如何做好淘汰 IT 资产数据安全销毁工作的思考 文件销毁 硬盘销毁 数据销毁 物料销毁 文件粉碎

在当今数字化时代&#xff0c;企业的 IT 资产不断更新换代&#xff0c;淘汰的 IT 资产中往往存储着大量的敏感数据。如何确保这些数据在资产淘汰过程中被安全销毁&#xff0c;成为了企业面临的重要挑战。以下是对如何做好淘汰 IT 资产数据安全销毁工作的一些思考。 一、明确数…

WACV2023论文速览域迁移Domain相关

Paper1 CellTranspose: Few-Shot Domain Adaptation for Cellular Instance Segmentation 摘要原文: Automated cellular instance segmentation is a process utilized for accelerating biological research for the past two decades, and recent advancements have produc…

控制下属绝不是拍桌子、甩脸子、摆架子,一流领导都用这3招!

控制下属绝不是拍桌子、甩脸子、摆架子&#xff0c;一流领导都用这3招&#xff01; 第一招&#xff1a;规矩严 国有国法&#xff0c;家有家规&#xff0c;公司也不例外。 下属能不能和领导齐心&#xff0c;愿不愿意共同发力&#xff0c;全看管理者如何操控。 毫无疑问的是&a…

Leetcode - 周赛403

目录 一&#xff0c;3200. 三角形的最大高度 二&#xff0c;3195. 包含所有 1 的最小矩形面积 I 三&#xff0c;3196. 最大化子数组的总成本 四&#xff0c;3197. 包含所有 1 的最小矩形面积 II 一&#xff0c;3200. 三角形的最大高度 本题是一道模拟题&#xff0c;可以先排…

【不容错过】可灵AI重磅更新:画质升级,运镜控制,首尾帧自定义,还有30万创作激励奖金!

还记得最近在各大平台肆虐的老照片变成视频吗&#xff0c;就是用快手的可灵AI做的&#xff0c;今天可灵又迎来了一次重大更新。 「电脑端上线了」 之前一直用其他工具生的图片还需要保存到手机上&#xff0c;再用可灵来生成视频&#xff0c;很多人都能感受到手机操作不太方便&…

七人互助拼团模式:共创共赢的电商新篇章

在当今电商行业的繁荣浪潮中&#xff0c;七人互助拼团模式犹如一股清流&#xff0c;凭借其独特的激励机制与深厚的互助合作文化&#xff0c;赢得了消费者与商家的广泛赞誉。这一模式不仅重新定义了团购体验&#xff0c;更在无形中强化了社群间的联系与协作&#xff0c;共同绘制…

提升用户体验之requestAnimationFrame实现前端动画

1)requestAnimationFrame是什么? 1.MDN官方解释 2.解析这段话&#xff1a; 1、那么浏览器重绘是指什么呢&#xff1f; ——大多数电脑的显示器刷新频率是60Hz&#xff0c;1000ms/6016.66666667ms的时间刷新一次 2、重绘之前调用指定的回调函数更新动画&#xff1f; ——requ…

多特征线性回归

目录 一、多特征符号意义说明&#xff1a;二、多特征模型表示&#xff1a;三、Numpy向量表示、内积计算&#xff1a;1.向量表示&#xff1a;2.内积计算&#xff1a; 四、多元线性回归梯度下降算法&#xff1a; 一、多特征符号意义说明&#xff1a; x下标j&#xff1a;表示第j个…