简介:这份资源是《Visual Studio 2015(C#) Windows数据库项目开发》的配套资料包,面向具备C#基础、希望系统掌握Windows桌面端数据库应用开发的开发者与在校学生。内容围绕Visual Studio 2015与.NET Framework展开,覆盖ADO.NET数据访问、CRUD操作、DataSet与DataAdapter离线数据管理,以及多表查询、存储过程、触发器、视图、索引和数据库安全等进阶主题,并配有环境配置说明、实训项目、课件与教材案例,形成从入门到实战的完整学习路径。压缩包共3423个文件,约95.9MB,以cs源码、resx与resources资源、dll类库、config配置、exe程序、rdlc报表及ppt课件等为主,另含sln解决方案与csproj工程文件,便于直接编译运行与二次修改。目前已有1081人学习下载,适合对照实训案例动手实践,理解数据库连接、事务处理与性能优化的具体实现思路。
1. 用 Visual Studio 2015 和 C# 把 Windows 数据库项目跑起来:从建库到第一个可交付界面
很多团队至今仍有一批基于 .NET Framework 4.5/4.6 的 Windows 桌面数据库项目在维护,开发机装的是 Visual Studio 2015,数据库是 SQL Server 2012 或 2014,需求方要的是能装、能连、能增删改查的本地客户端。这个标题说的就是这条链路:用 VS2015 的 C# 工程模板,配一个 Windows 数据库项目,把数据访问层、界面层和部署包串起来。它解决的不是“学 C# 语法”,而是“怎么在一个老版本工具链里把数据库项目做成能交付的东西”。适合两类人:一类是被派去接手遗留系统的开发者,一类是要在受限环境里快速搭出内部管理工具的工程师。下面按建库、连库、写数据层、做界面、打包排错的顺序讲,每一步都给可复现的命令和参数。
2. 先定数据访问方案:DataSet、EF 还是原生 ADO.NET
2.1 三种方案在 VS2015 里的真实差别
在 VS2015 里新建 C# Windows 项目时,数据访问有三条常见路线。第一条是 DataSet + TableAdapter,靠设计器拖拽生成强类型数据集,优点是可视化、改字段快,缺点是并发和复杂查询一多就难维护。第二条是 Entity Framework 6,用 EDMX 或 Code First 映射实体,适合表关系清晰、查询以对象为主的场景,但 VS2015 默认模板对 EF6 的支持需要手动装 NuGet 包。第三条是原生 ADO.NET,自己写 SqlConnection、SqlCommand、SqlDataAdapter,控制力最强,适合表结构复杂、SQL 要精细调优的项目。
我一般会这样选:如果项目表少于 20 张、查询以单表增删改查为主,用 DataSet 设计器最快;如果表之间有明确外键、业务逻辑要写 LINQ,用 EF6;如果涉及大量存储过程、批量导入或性能敏感查询,直接上 ADO.NET。不要一上来就追新,VS2015 的生态里 EF Core 并不合适,硬上会碰到工具链不兼容。
2.2 用 NuGet 装齐依赖包
VS2015 的 NuGet 包管理器在“工具 → NuGet 包管理器 → 程序包管理器控制台”里。假设选 EF6,先装 EntityFramework,再装对应数据库提供程序。命令如下:
# 在 VS2015 的程序包管理器控制台执行 Install-Package EntityFramework -Version 6.4.4 Install-Package EntityFramework.zh-Hans -Version 6.4.4逻辑说明:第一条装 EF6 核心库,第二条装中文资源包,避免运行时弹英文异常。参数说明:-Version必须显式指定,因为不指定会拉最新版,而最新版可能要求更高的 .NET Framework 版本,VS2015 默认项目是 4.5.2,容易翻车。装完后检查 packages.config,确认 targetFramework 是 net452 或 net46。
如果选原生 ADO.NET,不需要额外包,直接用 System.Data.SqlClient。但要注意 VS2015 默认引用的是 .NET Framework 自带的 SqlClient,版本较老,连 SQL Server 2014 没问题,连更高版本要确认 TLS 兼容性。
2.3 连接字符串放哪、怎么加密
连接字符串不要硬编码在 .cs 文件里。常见做法是放 App.config 的 connectionStrings 节点,然后用 ConfigurationManager 读取。示例:
<!-- App.config --> <configuration> <connectionStrings> <add name="DbConn" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=DemoDb;Integrated Security=True;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" /> </connectionStrings> </configuration>逻辑说明:Data Source指向实例名,Initial Catalog是数据库名,Integrated Security=True表示用 Windows 身份验证,省去账号密码。MultipleActiveResultSets=True允许同一连接上多个 DataReader 并行,EF 和嵌套查询会用到。参数说明:如果数据库在远程,把 Data Source 换成 IP,端口;如果必须用 SQL 账号,改成User ID=xxx;Password=xxx,但要在部署前用 aspnet_regiis 或自定义加密方式保护配置文件。
提示:VS2015 里调试时 App.config 会被复制到 bin\Debug,改完配置要重新生成,否则读到的还是旧值。
3. 建库建表与数据层代码:从 SQL 脚本到可调用的仓储类
3.1 用 SQL 脚本建库,别依赖设计器
在 VS2015 里可以右键“数据连接”直接建表,但团队协作时更可靠的是维护一份 .sql 脚本。示例脚本:
-- CreateDatabase.sql IF DB_ID('DemoDb') IS NULL BEGIN CREATE DATABASE DemoDb; END GO USE DemoDb; GO IF OBJECT_ID('dbo.Customer') IS NULL BEGIN CREATE TABLE dbo.Customer ( Id INT IDENTITY(1,1) PRIMARY KEY, Name NVARCHAR(50) NOT NULL, Phone NVARCHAR(20) NULL, CreatedAt DATETIME NOT NULL DEFAULT(GETDATE()) ); END GO逻辑说明:IF DB_ID(...) IS NULL保证脚本可重复执行,不会因为库已存在报错。IDENTITY(1,1)自增主键,DEFAULT(GETDATE())自动填创建时间。参数说明:NVARCHAR 比 VARCHAR 更适合中文,长度按业务留余量;如果表数据量大,主键建议用 BIGINT。
3.2 写一个原生 ADO.NET 仓储类
下面是一个可复用的 CustomerRepository,包含查询和插入:
// CustomerRepository.cs using System; using System.Collections.Generic; using System.Configuration; using System.Data; using System.Data.SqlClient; public class CustomerRepository { private readonly string _conn = ConfigurationManager.ConnectionStrings["DbConn"].ConnectionString; public List<Customer> GetAll() { var list = new List<Customer>(); using (var conn = new SqlConnection(_conn)) using (var cmd = new SqlCommand("SELECT Id,Name,Phone,CreatedAt FROM dbo.Customer", conn)) { conn.Open(); using (var reader = cmd.ExecuteReader()) { while (reader.Read()) { list.Add(new Customer { Id = reader.GetInt32(0), Name = reader.GetString(1), Phone = reader.IsDBNull(2) ? null : reader.GetString(2), CreatedAt = reader.GetDateTime(3) }); } } } return list; } public int Insert(Customer c) { using (var conn = new SqlConnection(_conn)) using (var cmd = new SqlCommand( "INSERT INTO dbo.Customer(Name,Phone) VALUES(@Name,@Phone); SELECT SCOPE_IDENTITY();", conn)) { cmd.Parameters.Add("@Name", SqlDbType.NVarChar, 50).Value = c.Name; cmd.Parameters.Add("@Phone", SqlDbType.NVarChar, 20).Value = (object)c.Phone ?? DBNull.Value; conn.Open(); return Convert.ToInt32(cmd.ExecuteScalar()); } } }逻辑说明:using保证连接和命令及时释放,避免连接池耗尽。SCOPE_IDENTITY()返回刚插入行的自增 Id,比@@IDENTITY安全,不会受触发器影响。参数说明:Add的第三个参数是长度,必须和表定义一致,否则可能截断;可空字段用?? DBNull.Value处理,不能直接传 null。
3.3 用 EF6 的 DbContext 做同样的事
如果选 EF6,代码更短:
// DemoContext.cs using System.Data.Entity; public class Customer { public int Id { get; set; } public string Name { get; set; } public string Phone { get; set; } public System.DateTime CreatedAt { get; set; } } public class DemoContext : DbContext { public DemoContext() : base("name=DbConn") { } public DbSet<Customer> Customers { get; set; } }逻辑说明:base("name=DbConn")告诉 EF 去 App.config 找名为 DbConn 的连接串。DbSet<Customer>对应表,EF 默认按复数规则映射到 Customers,如果表名是 Customer,要用[Table("Customer")]标注。参数说明:EF6 默认开启延迟加载和自动迁移,生产环境建议关掉自动迁移,用Database.SetInitializer<DemoContext>(null)。
4. 界面层与数据绑定:WinForm 里把数据真正显示出来
4.1 DataGridView 绑定的两种方式
WinForm 里最直接的是 DataGridView。方式一:直接绑 List,适合只读展示。
// Form1.cs private void Form1_Load(object sender, EventArgs e) { var repo = new CustomerRepository(); dataGridView1.AutoGenerateColumns = true; dataGridView1.DataSource = repo.GetAll(); }逻辑说明:AutoGenerateColumns = true会根据对象属性自动生成列,省去手动配置。参数说明:如果列顺序不对,先设 AutoGenerateColumns 为 false,再手动加 DataGridViewTextBoxColumn 并设 DataPropertyName。
方式二:用 BindingSource 做双向绑定,适合可编辑表格。先拖一个 BindingSource 组件,设 DataSource 为 Customer 类型,再把 DataGridView 的 DataSource 设为该 BindingSource。这样新增行、修改单元格会同步到内存对象,保存时遍历 BindingSource.List 即可。
4.2 保存按钮怎么写才不丢数据
保存逻辑要处理新增和修改两种情况。常见做法是给 Customer 加一个状态标记,或者用 Id 是否为 0 判断。示例:
private void btnSave_Click(object sender, EventArgs e) { var repo = new CustomerRepository(); foreach (Customer c in bindingSource1.List) { if (c.Id == 0) c.Id = repo.Insert(c); else repo.Update(c); } MessageBox.Show("保存完成"); }逻辑说明:遍历 BindingSource 里的对象,Id 为 0 说明是新增,插入后回填 Id;否则走更新。参数说明:Update 方法内部用UPDATE dbo.Customer SET Name=@Name,Phone=@Phone WHERE Id=@Id,注意 WHERE 条件必须带主键,否则会全表更新,这是血泪经验。
4.3 用 TableAdapter 快速生成数据层
如果不想手写仓储,VS2015 的 DataSet 设计器可以拖表生成 TableAdapter。步骤:右键项目 → 添加 → 新建项 → 数据集 → 从服务器资源管理器拖表到设计面 → 自动生成 Fill、GetData、Insert、Update、Delete 方法。优点是快,缺点是生成的 SQL 固定,复杂查询要手动改。适合原型阶段,正式项目还是建议把 SQL 抽出来。
5. 避坑与排查:VS2015 数据库项目最常见的 5 个翻车点
5.1 报“未找到连接字符串”或读到 null
现象:运行时报ConfigurationErrorsException,或者ConfigurationManager.ConnectionStrings["DbConn"]返回 null。原因通常是 App.config 没被复制到输出目录,或者节点名拼写不一致。解决:检查 bin\Debug 下有没有 项目名.exe.config,没有就右键 App.config → 属性 → 复制到输出目录设为“始终复制”。再确认 name 属性和代码里完全一致,大小写敏感。
5.2 连 SQL Server 报“证书链不受信任”
现象:连接远程 SQL Server 时抛A connection was successfully established... but then an error occurred during the login process,内层是证书信任问题。原因:老版本 .NET Framework 默认不信任自签名证书。解决:在连接字符串加TrustServerCertificate=True,或者把服务器证书导入受信任根。注意这只影响加密握手,不改变身份验证方式。
5.3 DataGridView 绑定 List 后修改不生效
现象:表格里改了单元格,保存时还是旧值。原因:List 不是可观察集合,DataGridView 修改的是内部副本。解决:改用 BindingList 或 BindingSource,它们会通知变更。如果坚持用 List,保存前要手动调用dataGridView1.EndEdit()。
5.4 EF6 首次查询特别慢
现象:第一次查询要等好几秒,之后正常。原因:EF 首次运行要生成模型映射和视图,属于正常开销。解决:在程序启动时调用var ctx = new DemoContext(); ctx.Customers.FirstOrDefault();预热,或者用预生成视图工具。不要以为是数据库慢,盲目加索引。
5.5 部署到客户机报“缺少 SqlClient 或版本不匹配”
现象:开发机正常,客户机报Could not load file or assembly System.Data.SqlClient。原因:客户机 .NET Framework 版本低于开发机,或者项目引用了 NuGet 版的 SqlClient 但没随包发布。解决:确认目标机装了 .NET Framework 4.5.2 以上;如果用了 NuGet 包,把对应 dll 一起复制到输出目录,或者改用框架自带的 System.Data.SqlClient。
6. 打包部署与一个让维护省力的技巧
6.1 用 InstallShield Limited Edition 做安装包
VS2015 自带 InstallShield Limited Edition,在“新建项目 → 其他项目类型 → 安装和部署”里。基本步骤:添加项目输出(主输出、内容文件、源文件),添加 .NET Framework 4.5.2 作为先决条件,设置安装目录,生成 setup.exe。关键参数:在“应用程序文件”里把 App.config 对应的 exe.config 包含进去,否则装完连不上库。数据库脚本可以做成自定义操作,在安装后调用 sqlcmd 执行,但要注意客户机有没有 sqlcmd,没有就改用 C# 代码执行脚本。
6.2 一个让后续维护省力的技巧:把 SQL 集中到资源文件
项目做久了,SQL 散落在各个 .cs 里,改一个字段要全局搜。我一般会把所有 SQL 语句放到一个 .resx 资源文件或单独的 SqlScripts 类里,用常量引用。这样改表结构时只动一处,也方便做 SQL 审查。示例:
// SqlScripts.cs public static class SqlScripts { public const string SelectCustomers = "SELECT Id,Name,Phone,CreatedAt FROM dbo.Customer"; public const string InsertCustomer = "INSERT INTO dbo.Customer(Name,Phone) VALUES(@Name,@Phone); SELECT SCOPE_IDENTITY();"; }逻辑说明:常量在编译期确定,不会带来运行时开销。参数说明:如果 SQL 很长,用 @"" 逐字字符串,注意里面的双引号要转义。这个习惯在多人协作时特别有用,新人接手能一眼看到所有数据库操作。
6.3 验证部署是否成功的三个检查点
装完后别急着交付,按这三步验:第一,打开程序,看主界面能否加载,说明依赖 dll 和配置没问题;第二,执行一次查询,看 DataGridView 有没有数据,说明连接字符串和权限没问题;第三,新增一条记录再重启程序,看数据还在不在,说明写入和持久化没问题。三步都过,基本可以交付。如果第二步失败,优先看 Windows 事件查看器里的 .NET Runtime 错误,比猜快得多。
我自己的习惯是每次改完数据层,先在控制台写个小测试跑一遍增删改查,再回到 WinForm 调界面。这样能把数据库问题和界面问题分开,省掉很多来回折腾。希望帮到你。
本文还有配套的精品资源,点击获取