ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

VS2019下VB.NET用ADO.NET读写SQL Server数据库实战源码详解

VS2019下VB.NET用ADO.NET读写SQL Server数据库实战源码详解 简介基于VS2019开发的VB.NET例程源码面向需要在.NET环境中快速操作SQL Server 2014数据库的开发人员尤其适合正在学习数据库编程或准备工程落地的初学者。利用自定义FUNCTION函数封装读取与写入逻辑运行后可直接查询数据库并显示编辑文本内容点击保存即可写回数据库形成从查询到存储的完整闭环省去繁琐的手工SQL拼写适合工程直接引用与项目二次开发。资源共45个文件压缩包仅635KB主要包括VB源码模块、工程文件sln/vbproj、程序集依赖dll、配置文件config以及文本说明等结构清晰加载后即可对照学习或按需改造。目前已有680人学习下载可作为数据库编程入门、毕业设计或日常开发的参考范例。通过该例程可掌握SQL连接、命令执行、结果展示及数据持久化的实用写法同时理解工程中模块划分与配置管理的基本思路尤其对于需要快速对接SQL Server的新项目能明显缩短基础功能开发周期。1. 基于VS2019的VB.NET读写SQL数据库这份例程源码到底能拿来干什么先说结论这不是一个封装好的ORM框架也不是什么高深的数据访问中间件而是一份能直接打开、直接跑、直接改的VS2019工程源码目标数据库是SQL Server 2014开发语言是VB.NET。它解决的是工程师在工控、桌面工具、小型管理系统里最常遇到的刚需——把界面上的文本框内容写进SQL库以及把SQL库里的数据查出来显示到界面上整个过程没有EF、没有LINQ to SQL就是纯粹的ADU.NET SqlConnection、SqlCommand那套原生写法。适合谁用两种人。一种是半路接手VB.NET维护项目的工程师手头有一个老系统要改正好需要一份干净的读写例程做参考另一种是刚把开发环境从VB6迁到VS2019的人想快速搞清楚SqlConnection、SqlCommand、SqlDataAdapter在三层结构里怎么摆。这份源码的价值不在算法而在“完整”——它带了一个可以运行的WinForm工程表结构、连接串、增删改查的FUNCTION函数都齐了你改改表名和字段就能挂到自己项目里。编译环境是VS2019.NET Framework版本看工程文件里的TargetFramework一般默认是4.x跑在Windows 10/11上没什么问题。2. 先搞懂这套源码的骨架从工程结构到SQL Server 2014的连接串拿到压缩包解压后第一件事别急着开.sln先把文件清单过一遍。这个工程的名字叫SQL数据库.vbproj解决方案文件是Project1.sln源码文件里有数据转换.vb、串口参数.vb、报表.vb还有一个My Project目录存放程序集信息。注意里面有_UpgradeReport.htm和UpgradeLog.htm这说明工程可能经历过从旧版本VS升级的过程打开工程时如果弹出迁移向导直接确认就行一般不影响运行。2.1 工程里的关键文件各自管什么文件作用你要不要改SQL数据库.vbprojVB.NET工程文件声明了编译目标、引用程序集换机器编译时可能要用记事本改TargetFramework版本串口参数.vb串口配置界面相关逻辑含参数读写如果你的场景不需要串口可以整个移除数据转换.vb数据类型转换、字符串处理的辅助函数建议保留里面可能有现成的类型转换写法报表.vb报表展示或打印相关逻辑需要报表功能就留否则可忽略app.config存储数据库连接串通常写在connectionStrings节点必须改改成你自己的服务器名和库名SQL数据库.vbproj.user用户级工程配置记录调试启动项无需提交到版本库本地自动生成打开app.config你会看到类似下面的连接串定义。这是整份源码里第一个要修改的地方。connectionStrings add nameSQLConn connectionStringData Source.;Initial CatalogSL_BB_20220430;User IDsa;Password123456;Integrated SecurityFalse; providerNameSystem.Data.SqlClient / /connectionStrings这里Data Source.代表本机SQL Server实例如果是命名实例就写成服务器名\实例名Initial Catalog是数据库名称从解压目录里的SQL数据库2014文件夹以及SL_BB_20220430这个库名来看这套源码对应的就是SQL Server 2014数据库文件应该也包含在压缩包里.mdf和.ldf。Integrated SecurityFalse表示用账号密码登录对应User ID和Password两项。如果你的SQL Server用的是Windows身份验证改成Integrated SecurityTrue同时去掉User ID和Password即可。2.2 SQL Server 2014的库怎么挂上去压缩包里没有直接给出.bak备份文件但从SL_BB_20220430这个命名推测当时是用一个具体业务日期的库来演示的。常见的做法是用.mdf文件附加到SQL Server 2014实例。CREATE DATABASE SL_BB_20220430 ON PRIMARY (NAME NSL_BB_20220430, FILENAME ND:\SQLData\SL_BB_20220430.mdf) LOG ON (NAME NSL_BB_20220430_log, FILENAME ND:\SQLData\SL_BB_20220430_log.ldf) GO如果手上只有.mdf直接用SSMS界面操作更省事右键“数据库”→“附加”→“添加”选中.mdf路径确认.ldf文件在同目录下能被识别点确定就挂上了。挂上之后把app.config里的Initial Catalog改成这个库的名字然后跑一下工程如果界面能正常显示出表中已有数据说明连接串和数据库都没问题。2.3 代码里是怎么读和怎么写的这套源码把数据库操作封装成了FUNCTION函数集中在SQL数据库.vb里。典型的读取函数写法如下Public Function QueryData(ByVal sql As String) As DataTable Dim dt As New DataTable() Using conn As New SqlConnection(ConfigurationManager.ConnectionStrings(SQLConn).ConnectionString) Using cmd As New SqlCommand(sql, conn) conn.Open() Using da As New SqlDataAdapter(cmd) da.Fill(dt) End Using End Using End Using Return dt End Function这段代码值得逐行看明白SqlConnection负责建立连接SqlCommand承载SQL语句SqlDataAdapter.Fill把查询结果一次性灌进内存中的DataTable。用Using块的原因是不管代码是否抛异常连接都会在块结束时关闭这对桌面程序反复读写SQL库特别重要——忘了关连接跑一晚上就会出现“连接池已满”的玄学问题。写入操作一般走ExecuteNonQueryPublic Function ExecuteSql(ByVal sql As String) As Integer Using conn As New SqlConnection(ConfigurationManager.ConnectionStrings(SQLConn).ConnectionString) Using cmd As New SqlCommand(sql, conn) conn.Open() Return cmd.ExecuteNonQuery() End Using End Using End FunctionExecuteNonQuery返回受影响的行数。比如执行INSERT插入一行返回1执行UPDATE更新3行返回3。代码里若发现保存后返回0先别怀疑函数写错——大概率是SQL语句里WHERE条件没匹配到任何记录或者插入了主键冲突的数据被数据库拦了。3. 把读写例程改造成自己的业务逻辑从界面到数据库的闭环源码里的串口参数.vb和报表.vb其实是两个很好的参照物。它们演示了同一个模式界面上摆几个TextBox和Button点击“查询”按钮后把文本框中的条件拼进SQL查询结果绑定到DataGridView点击“保存”按钮后把各个文本框的值作为参数拼进INSERT或UPDATE语句。理解了这个套路把它迁移到自己业务表上就是体力活。3.1 查询按钮的标准写法防止SQL注入的参数化查询从源码里能看到读取操作是直接把SQL语句传进QueryData函数。这里有一个工程上必须注意的坑如果SQL语句是直接字符串拼接文本框内容比如SELECT * FROM t WHERE name TextBox1.Text 那这个程序只能在内部工具场景用。一旦涉及多用户或对外部署必须改成参数化查询。Private Sub btnQuery_Click(sender As Object, e As EventArgs) Handles btnQuery.Click Dim sql As String SELECT * FROM SL_BB_20220430 WHERE 设备编号 devId Using conn As New SqlConnection(ConfigurationManager.ConnectionStrings(SQLConn).ConnectionString) Using cmd As New SqlCommand(sql, conn) cmd.Parameters.AddWithValue(devId, txtDevId.Text.Trim()) Dim da As New SqlDataAdapter(cmd) Dim dt As New DataTable() da.Fill(dt) DataGridView1.DataSource dt End Using End Using End SubParameters.AddWithValue就是参数化查询的写法devId是SQL语句里的占位符运行时由框架把参数值和SQL语句分开传送给SQL Server从根上堵住注入。注意尽量不要用AddWithValue传nvarchar类型参数有些场景会导致索引失效但在这套源码的简单场景下问题不大。txtDevId.Text.Trim()是为了去掉用户误输入的首尾空格否则查询条件多一个空格就查不出数据。3.2 保存按钮的标准写法先查重再插入看串口参数.vb里的保存逻辑大致能猜到它的流程是把窗体上的串口号、波特率、数据位等参数拼成INSERT语句执行ExecuteSql后返回受影响行数大于0就提示保存成功。这个思路没问题但真实业务里直接INSERT很容易踩主键重复的坑。更好的写法是先按业务主键查一次存在就UPDATE不存在才INSERT。Private Sub btnSave_Click(sender As Object, e As EventArgs) Handles btnSave.Click Dim checkSql As String SELECT COUNT(1) FROM SL_BB_20220430 WHERE 设备编号 devId Dim cnt As Integer Convert.ToInt32(QueryDataWithScalar(checkSql, txtDevId.Text.Trim())) Dim sql As String If cnt 0 Then sql UPDATE SL_BB_20220430 SET 设备名称 name, 备注 remark WHERE 设备编号 devId Else sql INSERT INTO SL_BB_20220430 (设备编号, 设备名称, 备注) VALUES (devId, name, remark) End If Dim affected As Integer ExecuteSqlWithParams(sql, txtDevId.Text.Trim(), txtName.Text.Trim(), txtRemark.Text.Trim()) If affected 0 Then MessageBox.Show(保存成功) btnQuery_Click(Nothing, Nothing) Else MessageBox.Show(保存失败请检查输入数据) End If End Sub这段代码里出现了QueryDataWithScalar和ExecuteSqlWithParams它们是前面QueryData、ExecuteSql的重载版本区别在于一个返回单个值、一个接收参数集合。源码里可能没有现成的这两个函数但改造起来很简单在原来的函数签名后面加上ParamArray params As SqlParameter()然后循环cmd.Parameters.Add进去就行。Convert.ToInt32用来把COUNT(1)的结果从Object转成整数别忘了DBNull的情况——这里COUNT永远不会返回DBNull所以可以放心转。4. 常见问题与避坑连不上库、中文乱码、64位驱动这些坎这份源码在VS2019里跑通的概率很大但换到你的机器上大概率会撞上几个典型问题。我把它们按“现象 → 原因 → 解决”列出来都是实际操作中反复出现过的。问题1运行报“尝试读取或写入受保护的内存”现象点击查询按钮后程序直接崩溃或弹出一个AccessViolationException错误信息提示“尝试读取或写入受保护的内存。这通常表示其他内存已损坏”。原因最常见的是SQL语句里表名或字段名写错导致SqlDataAdapter.Fill时返回了非预期的模式另一种原因是数据库连接串里的Integrated Security设置与SQL Server登录模式不匹配比如SQL Server只开了Windows身份验证代码却用sa登录。解决先去SSMS里用这段SQL确认登录方式是否可用。把连接串里的Integrated Security改成True或者新建一个SQL Server账号并授予db_datareader和db_datawriter权限。如果报错发生在调用非托管代码的场合比如操作Excel COM组件那就得检查工程里是否有Microsoft.Office.Interop相关引用把编译目标改成x64或x86分别测试。问题2cmd.Parameters.AddWithValue(列名, 空字符串) 导致保存失败现象界面上某个文本框没填内容点击保存后报“列不允许为NULL”之类的错误。原因TextBox.Text为空时返回的是空字符串不是DBNull.ValueSQL Server在遇到空字符串写入非空列时会把它当作合法空串但如果列设置的是NOT NULL且没有默认值约束在某些严格模式下会报错。解决在拼接参数之前做空值判断If String.IsNullOrWhiteSpace(txtName.Text) Then cmd.Parameters.AddWithValue(name, DBNull.Value) Else cmd.Parameters.AddWithValue(name, txtName.Text.Trim()) End IfDBNull.Value是.NET世界里表示SQL NULL的唯一正确姿势用或Nothing都会埋雷。问题3中文数据显示成乱码或问号现象数据库里存的中文正常界面显示正常但从界面上写入数据库的中文再查出来变成了???。原因通常是因为SqlConnection的连接串里没指定编码或者SQL Server端字段类型是varchar而不是nvarchar。varchar存中文依赖数据库代码页很容易在跨语言环境下变成问号。解决字段类型能改的话优先改成nvarchar(n)连接串里追加charsetutf8是MySQL的习惯SQL Server不吃这一套SQL Server端就是建表时选对类型。代码层面不需要额外设置编码.NET的SqlClient默认就是UTF-16传输。如果字段已经是一堆问号的脏数据只能删掉重建。问题4工程打开后引用全部标黄编译不过现象用VS2019打开源码后解决方案资源管理器里的引用项全部带黄色感叹号编译时报“未能找到类型或命名空间名SqlConnection”。原因目标机器上没装对应的.NET Framework开发包或者工程文件里声明的TargetFramework版本比当前机器的高。解决右键工程→属性→“应用程序”→“目标框架”改成你机器上已安装的版本。.NET Framework 4.6.2在VS2019里基本是通配项。如果改完还报引用缺失检查packages.config——这套源码没用NuGet包所以引用缺失大概率是System.Data程序集没勾选在“引用”→“添加引用”→“框架”里补上System.Data和System.Configuration即可。问题5Debug目录下没有生成exe或生成后双击闪退现象F5调试能跑但直接运行bin\Debug下的exe窗口一闪就消失。原因调试模式下依赖*.vshost.exe和.pdb文件直接拷exe到别的机器缺了配套文件或者app.config没被拷贝到输出目录。解决确认app.config在“复制到输出目录”属性里被设为“如果较新则复制”发布时把整个Debug或Release目录一起拷走别只拷一个exe。真正要分发时用VS的“发布”功能生成安装包连接串加密又是另一套话题了——这套源码里没做加密工程内部使用够用对外发布建议至少把User ID和Password从app.config挪到Windows凭据管理器里。问题6SQL Server服务没启动时报“建立与服务器的连接时出错”现象一运行就弹“在与 SQL Server 建立连接时出现网络相关或特定于实例的错误”。原因SQL Server服务没启动或者实例名写错防火墙拦了1433端口。解决WinR输入services.msc找到SQL Server (MSSQLSERVER)右键启动连接串Data Source处的实例名要和SSMS连接时的服务器名完全一致检查Windows防火墙是否放行1433端口New-NetFirewallRule -DisplayName SQL Server -Direction Inbound -Protocol TCP -LocalPort 1433 -Action Allow这个PowerShell命令用管理员身份执行把SQL Server的TCP端口放行。局域网内其他机器要连这台库的话这一步是必须的。5. 把这份源码改造成你自己的工具一段可复用的健壮读写类前面四章已经讲完了这份源码的骨架、读写套路和常见坑。这一章直接给你一套可以整体替换源码里SQL数据库.vb的健壮版封装兼容原来工程里的所有调用方式同时把容易翻车的点全部堵死。这套代码我在好几个工控项目里改完用过稳定性和可维护性比原版高一个档次。Imports System.Data.SqlClient Imports System.Configuration Public Class SqlHelper Private ReadOnly _connStr As String Public Sub New() _connStr ConfigurationManager.ConnectionStrings(SQLConn).ConnectionString End Sub Public Function QueryData(ByVal sql As String, ParamArray params As SqlParameter()) As DataTable Dim dt As New DataTable() Using conn As New SqlConnection(_connStr) Using cmd As New SqlCommand(sql, conn) If params IsNot Nothing AndAlso params.Length 0 Then cmd.Parameters.AddRange(params) End If conn.Open() Using da As New SqlDataAdapter(cmd) da.Fill(dt) End Using End Using End Using Return dt End Function Public Function ExecuteSql(ByVal sql As String, ParamArray params As SqlParameter()) As Integer Using conn As New SqlConnection(_connStr) Using cmd As New SqlCommand(sql, conn) If params IsNot Nothing AndAlso params.Length 0 Then cmd.Parameters.AddRange(params) End If conn.Open() Return cmd.ExecuteNonQuery() End Using End Using End Function Public Function QueryScalar(ByVal sql As String, ParamArray params As SqlParameter()) As Object Using conn As New SqlConnection(_connStr) Using cmd As New SqlCommand(sql, conn) If params IsNot Nothing AndAlso params.Length 0 Then cmd.Parameters.AddRange(params) End If conn.Open() Dim result As Object cmd.ExecuteScalar() If result Is DBNull.Value Then Return Nothing Return result End Using End Using End Function End Class这套SqlHelper有三个核心改进一是把连接串读取收敛到构造函数后续所有方法不用再各自读app.config二是ParamArray参数数组让调用方可以灵活传0到N个参数代码看起来更干净三是加了QueryScalar方法专门应对SELECT COUNT(1)这类取单个值的场景比用DataTable再取Rows(0)(0)轻量得多。对应的调用方式也变了。原来你是QueryData(SELECT ... WHERE id 1)这样直接拼字符串现在推荐这样写Dim helper As New SqlHelper() Dim sql As String SELECT * FROM SL_BB_20220430 WHERE 设备编号 devId AND 日期 startDate Dim dt As DataTable helper.QueryData(sql, New SqlParameter(devId, txtId.Text.Trim()), New SqlParameter(startDate, DateTimePicker1.Value.Date)) DataGridView1.DataSource dt注意New SqlParameter(startDate, DateTimePicker1.Value.Date)这一行日期类型必须用强类型的DateTime不要先转成字符串再传参否则SQL Server在隐式转换上偶尔会闹脾气导致索引失效或查不出当天数据。字符串参数全部Trim()数值参数先Convert.ToInt32再传这是我在多次翻车之后总结出来的铁律。这套类替换掉源码里原来的QueryData、ExecuteSql函数之后原来界面按钮的调用代码基本不用大改因为函数签名兼容。唯一需要检查的是原来有没有用ExecuteSql的返回值来做业务判断——原来返回的是受影响行数现在依然返回的是受影响行数语义完全一致。数据库连接串依然由app.config驱动这意味着后续你要换数据库服务器、换账号、换库名只需要改app.config一行代码都不用动。验证这套类是否正常工作最直接的办法是在原工程的btnQuery_Click里临时加一个断点单步走一遍QueryData确认传进SqlCommand的SQL语句没有字符串拼接的痕迹。再检查cmd.Parameters集合里的参数数量是不是和SQL语句里的占位符一致——这个数量对不上运行时会直接报“过程或函数’XXX’期望参数’YYY’未提供”排查时先数占位符再数参数个数能省半小时。另外提醒一点这套源码跑在.NET Framework上如果你后续要把它迁到.NET Core或.NET 5ConfigurationManager.ConnectionStrings这行会变成编译错误需要引入System.Configuration.ConfigurationManager这个NuGet包。这是VB.NET老工程迁移到现代.NET平台最常见的坑提前知道比到时候搜半天要顺手。我从那以后每接到一个VB.NET读写SQL库的维护任务第一件事就是先看对方原来的数据库访问代码有没有重构成参数化查询——如果是清一色的字符串拼接我通常会直接翻出这套SqlHelper类的思路重构掉再顺手把app.config里裸露的数据库密码改成从环境变量读取。这个过程不做的话后续加一个查询条件就要改一行SQL拼一个字符串迟早要出事。这套源码本身是一个很好的起点数据库连接、界面绑定、函数封装的骨架都在你只要按自己的表结构往里填就行。希望帮到你。本文还有配套的精品资源点击获取
返回列表