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.mzph.cn/diannao/41812.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Leetcode 完美数

1.题目要求: 对于一个 正整数&#xff0c;如果它和除了它自身以外的所有 正因子 之和相等&#xff0c;我们称它为 「完美数」。给定一个 整数 n&#xff0c; 如果是完美数&#xff0c;返回 true&#xff1b;否则返回 false。示例 1&#xff1a;输入&#xff1a;num 28 输出&a…

2024年6月份找工作和面试总结

转眼间6月份已经过完了&#xff0c;2024年已经过了一半&#xff0c;希望大家都找到了合适的工作。 本人前段时间写了5月份找工作的情况&#xff0c;请查看2024年5月份面试总结-CSDN博客 但是后续写的总结被和谐了&#xff0c;不知道这篇文章能不能发出来。 1、6月份面试机会依…

网络爬虫基础

网络爬虫基础 网络爬虫&#xff0c;也被称为网络蜘蛛或爬虫&#xff0c;是一种用于自动浏览互联网并从网页中提取信息的软件程序。它们能够访问网站&#xff0c;解析页面内容&#xff0c;并收集所需数据。Python语言因其简洁的语法和强大的库支持&#xff0c;成为实现网络爬虫…

verilog读写文件注意事项

想要的16进制数是文本格式提供的文件&#xff0c;想将16进制数提取到变量内&#xff0c; 可以使用 f s c a n f ( f d 1 , " 也可以使用 fscanf(fd1,"%h",rd_byte);实现 也可以使用 fscanf(fd1,"也可以使用readmemh(“./FILE/1.txt”,mem);//fe放在mem[0…

运用Redis作为设备注册中心,解决20w+设备高并发读写,高性能读写异步把数据同步到mysql持久化。

使用 Redis 作为设备注册中心&#xff0c;并通过高并发读写将数据异步同步到 MySQL 数据库&#xff0c;可以采用以下策略&#xff1a; 1. **设备注册与发现**&#xff1a; - 使用 Redis 的字符串或哈希表存储设备信息&#xff0c;其中键可以是设备的唯一标识符。 2. **高并…

基于Android Studio零食工坊

目录 项目介绍 图片展示 运行环境 获取方式 项目介绍 用户 可以浏览商品 &#xff0c; 查询商品 &#xff0c; 加入购物车 &#xff0c; 结算商品 &#xff0c; 查看浏览记录 &#xff0c; 修改密码 &#xff0c; 修改个人信息 &#xff0c; 查询订单 管理员 能够实现商品的…

别再写一堆 if 判断了?分享 1 段优质 JS 代码片段!

本内容首发于工粽号&#xff1a;程序员大澈&#xff0c;每日分享一段优质代码片段&#xff0c;欢迎关注和投稿&#xff01; 大家好&#xff0c;我是大澈&#xff01; 本文约 700 字&#xff0c;整篇阅读约需 1 分钟。 今天分享一段优质 JS 代码片段&#xff0c;用条件数组来简…

huggingface笔记:gpt2

0 使用的tips GPT-2是一个具有绝对位置嵌入的模型&#xff0c;因此通常建议在输入的右侧而不是左侧填充GPT-2是通过因果语言建模&#xff08;CLM&#xff09;目标进行训练的&#xff0c;因此在预测序列中的下一个标记方面非常强大 利用这一特性&#xff0c;GPT-2可以生成语法连…

C#面 :ASP.Net Core中有哪些异常处理的方案?

在 ASP.NET Core中&#xff0c;有多种异常处理方案可供选择。以下是其中几种常见的异常处理方案&#xff1a; 中间件异常处理&#xff1a; ASP.NET Core提供了一个中间件来处理全局异常。通过在Startup类的Configure方法中添加UseExceptionHandler中间件&#xff0c;可以捕获…

Autosar MCAL-S32k324 Crypto配置-RandomNumber生成及使用

文章目录 前言CryptoPrimitivesCryptoPrimitiveAlgorithmFamilyCryptoPrimitiveAlgorithmModeCryptoPrimitiveAlgorithmSecondaryFamilyCryptoPrimitiveServiceCryptoDriverObject代码使用Random Generate执行流程配置job函数使用示例总结前言 之前介绍过AES-CMAC算法的配置,…

Windows 与 Windows Server 2022环境下如何开启远程桌面

文章目录 前言Windows 环境下如何开启远程桌面控制功能Windows Server 环境下如何开启远程桌面 前言 我这边是客户需要搭建一套备份系统&#xff0c;整体的系统流程是这样的&#xff1a;客户的笔记本或者其他PC工具可以自由访问到我司搭建的服务器平台并进行文件传输&#xff…

【C++】开源:地图投影和坐标转换proj库配置使用

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍地图投影和坐标转换proj库配置使用。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&a…

vue中的watch和$watch的用法实际场景和区别

watch&#xff08;组件选项&#xff09; watch 是Vue组件的一个选项&#xff0c;它允许你定义一些监听器&#xff0c;这些监听器会在组件的响应式数据变化时调用。它主要用于组件内部的数据监听。 实际场景&#xff1a; 当组件内部的数据变化时&#xff0c;需要执行一些操作&…

5款极其强大的大模型文生图工具!

文生图技术是一种基于深度学习的技术&#xff0c;可以根据自然语言描述生成相应的高品质图像。 下面介绍几个目前市场上比较优秀的工具或网站&#xff0c;并制作一张男性的白袍巫师图来比较。 针对大模型和AIGC技术趋势、AIGC 算法项目落地经验分享、新手如何入门算法岗、该如…

el-scrollbar组件使用踩坑记录

一、el-scrollbar和浏览器原生滚动条一起出现 问题描述 el-scrollbar组件主要用于替换浏览器原生导航条。如下图所示&#xff0c;使用el-scrollbar组件后&#xff0c;发现未能成功替换掉浏览器原生导航条&#xff0c;二者同时出现。 引发原因 el-scrollbar的height属性如果…

前端根据目录生成模块化路由routes

根据约定大于配置的逻辑&#xff0c;如果目录结构约定俗成&#xff0c;前端是可以根据目录结构动态生成路由所需要的 route 结构的&#xff0c;这个过程是要在编译时 进行&#xff0c;生成需要的代码&#xff0c;保证运行时的代码正确即可 主流的打包工具都有对应的方法读取文…

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

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

Vue3项目给ElementPlus设置中文的两个方案

介绍 在Vue3项目将ElementPlus切换为中文 1、在App.vue的文件中修改 <template><el-config-provider :locale"zhCn"><router-view></router-view></el-config-provider> </template><script lang"ts" setup>im…

elasticsearch源码分析-04集群状态发布

集群状态发布 cluster模块封装了在集群层面执行的任务&#xff0c;如集群健康、集群级元信息管理、分片分配给节点、节点管理等。集群任务执行之后可能会产生新的集群状态&#xff0c;如果产生新的集群状态主节点会将集群状态广播给其他节点。 集群状态封装在clusterState中&…

Linux下网络编程-简易poll服务器和客户端

Linux下网络编程-简易poll服务器和客户端 简易poll服务器: //编译命令&#xff1a;g -g xxx.cpp -o xxx #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h…