简介:EF Core 3.1对接达梦8数据库的实战示例项目,面向需要在国产化数据库环境下使用微软ORM框架的.NET开发者。压缩包内以ConsoleApp1控制台应用程序为入口,包含完整的DbContext配置、实体类定义以及增删改查操作代码,项目结构直观,便于快速了解达梦8数据库与EF Core的整合方式。包内共83个文件,以dll与exe运行文件、cs源代码、json配置及cache缓存文件为主,整体仅1.79MB,轻量易下载;其中7个cs文件清晰呈现核心代码逻辑,4个exe可直接运行观察效果。已有179人学习下载,适合正在将.NET应用迁移到达梦数据库,或需要配置EF Core达梦提供程序(如DAMENG.EntityFrameworkCore)的开发者参考。解压后即可对照源码学习连接字符串写法、OnConfiguring配置方法及SaveChanges持久化流程,节省自行摸索的时间。
1. EFCore 3.1 连达梦 8 的示例包:国产化选型期最缺的那份最小工程
接到信创改造任务的老 .NET 工程师大概都经历过这个阶段:系统跑在 .NET Core 3.1 上,EF Core 3.1 用了两年,突然通知要把底层数据库从 SQL Server 换成达梦 8。官方文档几百页,社区案例稀缺,GitHub 上翻半天也找不到一份能直接跑起来的样例。这个压缩包就是干这个用的——一个名为 ConsoleApp1 的最小控制台工程,把 EF Core 3.1 通过达梦适配器连上达梦 8 数据库的完整链路做成了可复现的例子。它不解决业务问题,解决的是选型评估期最痛的那个问题:连接串怎么写、包该装哪个、增删改查能不能跑通。适合准备做达梦适配评估、或者刚接到迁移任务想先花半小时验证可行性的工程师。
2. 先看懂这条连接链路:达梦 8 的 EF Core 提供者与项目结构
2.1 EF Core 3.1 为什么需要单独的达梦适配包
EF Core 本身是一个 ORM 框架,它不直接认识达梦 8,也不认识 Oracle、MySQL。EF Core 与具体数据库之间的桥梁叫“数据库提供者”(Database Provider),它在内部实现了IEntityTypeMappingSource、IQueryTranslationPreprocessor等抽象接口,把 LINQ 表达式翻译成对应数据库的 SQL 方言。SQL Server 能用UseSqlServer,MySQL 能用UseMySql,Oracle 能用UseOracle,达梦要连,就得有对应的UseDAMENG之类的扩展方法。
这个扩展方法不是微软官方提供的。达梦官方提供的适配包里封装了自己的 ADO.NET 驱动,底层的连接和命令走的是达梦原生驱动,EF Core 只负责把表达式树变成文本 SQL。EF Core 3.1 是 .NET Core 3.1 时代的版本,它的 API 面和 EF Core 5.0、6.0 差别不小,所以适配包必须对应 3.1 的版本,不能用更高版本适配包强行落到 3.1 项目里。这一点在后续踩坑章节会单独展开。
2.2 解压后先看这三个文件
这个压缩包解压后是一个完整控制台解决方案,包含ConsoleApp1.sln解决方案文件和ConsoleApp1项目目录。obj和bin是构建产物,project.assets.json是 NuGet 还原生成的依赖清单,这些都不用手动碰。真正要读的是三个文件。
第一个是ConsoleApp1.csproj。这个文件里定义了目标框架和 NuGet 引用。示例用的是 .NET Core 3.1,TargetFramework对应netcoreapp3.1。PackageReference里应该能看到达梦适配包和Microsoft.EntityFrameworkCore的版本号。核对这两个版本是否匹配,是跑通例子的第一道关。
第二个是Program.cs。这是控制台入口,通常的套路是:创建DbContext实例 → 执行插入或查询 → 打印结果。这个文件决定例子的演示路径——是只测连接还是做了完整增删改查。
第三个是Class1.cs。名字起得很随意,但在这个例子场景里它一般放实体类和DbContext派生类。看到它,你就能确认实体属性与达梦表字段的映射方式。
| 文件类型 | 该不该改 | 备注 |
|---|---|---|
| ConsoleApp1.csproj | 通常不用改 | 核对包版本即可 |
| Program.cs | 可能要改连接串 | 主逻辑入口 |
| Class1.cs | 可能要改实体/上下文 | 表名、字段映射 |
| obj/bin 目录 | 不用管 | 构建还原产物 |
| ConsoleApp1.sln | 不用改 | 直接双击或用 dotnet 命令加载 |
2.3 为什么示例偏要用控制台项目
见过不少人质疑:生产环境是 Web 项目,拿控制台样例有什么用?恰恰是这个“最小形态”最有价值。控制台项目没有 IIS、没有 Kestrel、没有依赖注入容器那一整套,运行链路最短,问题最容易暴露。连接失败时,报错堆栈里只有 ADO.NET 驱动和 EF Core,排除了框架层的干扰。
我一般会先跑通控制台例子,确认连接串、驱动、SQL 翻译三个环节没问题,再往 Web 项目里迁。这个顺序能省下大量排查时间——否则 Web 项目里一旦出问题,你没法判断是数据库连不上还是依赖注入配置错了。
3. 把 ConsoleApp1 跑起来:还原依赖、改连接串、跑增删改查
3.1 还原 NuGet 依赖
拿到压缩包的第一步是还原 NuGet 包。解压后进入ConsoleApp1目录,执行:
dotnet restore如果本机装了 Visual Studio,也可以在 IDE 里直接打开ConsoleApp1.sln,让 VS 自动还原。dotnet restore会读取ConsoleApp1.csproj里的PackageReference,把Microsoft.EntityFrameworkCore和达梦适配包下载到本地 NuGet 缓存,并生成project.assets.json。
对于这个例子本身,dotnet restore的目的是确认依赖能拉下来。如果公司内网有 NuGet 私有源,需要先配置好源地址;如果提示“找不到包”,多半是适配包的包名或版本不对,这个在避坑章节会详说。还原成功后,直接dotnet run就能看例子能不能跑,但通常连接串还没改成你的达梦环境,所以先改连接串。
3.2 连接串怎么改
达梦 8 的连接串写法跟 SQL Server 差异很大。SQL Server 常用的Server=xxx;Database=xxx;User Id=xxx;Password=xxx在这里不完全适用。达梦适配器通常认下面的写法:
optionsBuilder.UseDAMENG( "Server=127.0.0.1;Port=5236;UserId=SYSDBA;Password=SYSDBA;Schema=TEST" );这个连接串里最关键的是Port。达梦 8 默认端口是 5236,不是 SQL Server 的 1433,也不是 MySQL 的 3306。UserId默认可以用达梦安装时自带的SYSDBA账号,Password是安装时设置的密码,如果没改过,很多环境是SYSDBA,但这要跟安装达梦的人确认,不要瞎猜。
Schema参数对应达梦里的“模式”。达梦的一个特点是一个模式对应一个用户,默认情况下访问某个用户下的表,Schema 要写那个用户名。如果省略这个参数,可能会在查询时报“表或视图不存在”,这个坑后面专门讲。
3.3 定义实体与 DbContext
示例里Class1.cs大概长这样——定义一张测试表的实体,再写一个DbContext派生类:
public class TestUser { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } } public class TestDbContext : DbContext { public DbSet<TestUser> TestUsers { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseDAMENG( "Server=127.0.0.1;Port=5236;UserId=SYSDBA;Password=SYSDBA;Schema=TEST" ); } }实体类的属性名建议跟达梦表的字段名保持一致,或者用[Column("字段名")]标注。如果达梦表里字段是大写,C# 属性用 PascalCase 没问题,因为达梦和 EF Core 会在 SQL 翻译时自动处理大小写,但如果你有[Table("表名")]特性没写对,就会撞上“表或视图不存在”的报错。
OnConfiguring是 EF Core 的经典配置入口。它适合示例这种不用依赖注入的场景,直接写死连接串。真实项目里建议改成从配置文件读,但示例坚持用这种方式能少一层配置干扰。
3.4 增删改查演示
Program.cs里的主逻辑通常会演示插入和查询两件事:
using (var context = new TestDbContext()) { var user = new TestUser { Name = "张三", Age = 30 }; context.TestUsers.Add(user); context.SaveChanges(); var list = context.TestUsers .Where(u => u.Age > 18) .ToList(); foreach (var item in list) { Console.WriteLine($"{item.Id} - {item.Name} - {item.Age}"); } }这段代码覆盖了 EF Core 做增删改查的最小闭环。Add方法把实体标记为Added状态,SaveChanges在后台生成 INSERT 语句并交给达梦执行。查询走的 LINQ 表达式Where(u => u.Age > 18)会被翻译成带 WHERE 子句的 SQL。
运行这段代码时,控制台会输出查询结果,这说明整条链路已经通了:连接串配置正确、适配包版本匹配、SQL 方言翻译没有致命错误。如果在这里就翻车,问题基本集中在连接串和包版本上,往下走会撞到更隐蔽的坑。
4. 参数边界与行为差异:连接串字段、并发与 SQL 翻译
4.1 连接串字段逐项拆解
达梦适配器的连接串字段看着跟 SQL Server 有点像,但含义和可选项完全不同。用一张表把常见字段说清楚:
| 参数 | 示例值 | 作用 | 要不要必填 |
|---|---|---|---|
| Server | 127.0.0.1 | 达梦服务器 IP | 必填 |
| Port | 5236 | 达梦监听端口 | 必填 |
| UserId | SYSDBA | 登录用户名 | 必填 |
| Password | 对应密码 | 登录密码 | 必填 |
| Schema | TEST | 默认模式,对应达梦的一个用户 | 建议填 |
| Pooling | true/false | 是否启用连接池 | 可选 |
| Connection Timeout | 30 | 连接超时秒数 | 可选 |
| Charset | UTF-8 | 字符集,解决中文乱码 | 按需 |
Schema参数值得多说一句。达梦跟 SQL Server 最大的不同是:SQL Server 里数据库和用户是独立的,库里有多个 schema;达梦里默认一个用户对应一个 schema,SYSDBA登录后默认访问SYSDBA模式,如果你想访问TEST用户建的表,要么在 SQL 里写TEST.表名前缀,要么在连接串里指定Schema=TEST。
4.2 和 SQL Server 三个最不同的体验
先说说大小写。SQL Server 默认不区分大小写,表名写成Users或users都能查到。达梦默认创建的对象名是大写存储,EF Core 翻译 SQL 时如果不加引号,达梦会把标识符统一转成大写再匹配。所以 C# 实体名用TestUser没问题,翻译出来是TESTUSER,能和库里的表对应上。但如果你的表是建表时用小写加双引号创建的,那 EF Core 生成的未加引号 SQL 就匹配不上了。
再说分页。EF Core 对Skip/Take的翻译,SQL Server 用的是OFFSET ... FETCH,达梦 8 不同版本对分页 SQL 的支持方式不同。较新的版本支持OFFSET子句,老版本或有限制版本可能要走ROWNUM方式。适配包版本较旧时,Skip/Take可能被翻译成先取全表再内存分页——数据量小没感觉,数据量大就等着内存爆炸吧。
第三个是布尔处理。SQL Server 有bit类型,C# 的bool属性映射到 bit,查询时直接WHERE IsActive = 1。达梦没有 bit 类型,通常用 NUMBER(1) 或 TINYINT 代替,适配器在赋值和比较时可能需要特殊处理。如果实体里定义了bool属性,达梦侧对应字段就得保证是整数 0/1,否则写入会报类型转换错误。
4.3 并发与事务:SaveChanges 的批量性能
数据库并发这块,达梦支持标准的BEGIN TRAN事务和普通行锁,这块不像连接串那样有那么多差异。真正要警惕的是 EF Core 的逐条写入模式:SaveChanges默认一条一条地 INSERT,循环 10000 条就是 10000 次往返。
在达梦 8 上调大批量数据导入时,这个性能差距会特别明显。常见做法是缩小事务粒度,分批调用SaveChanges,比如每 500 条提交一次。如果导入量级更大,就别用 EF Core 了,直接拼INSERT INTO ... VALUES (...), (...)批量语句,或者用达梦自带的数据装载工具,EF Core 只负责业务查询那条链路。
事务和并发锁这块,EF Core 的Database.BeginTransaction在达梦适配器上是可用的,正常提交回滚都能工作。但如果你的应用是长事务里套大量写入,小心“数据库死锁”这个经典问题——多个事务交叉更新同一批行,达梦会按顺序锁资源,长时间不释放就可能触发死锁检测。遇到这种场景,把事务范围缩小,让锁持有时间变短,比调锁超时参数更有效。
5. 避坑:EFCore 3.1 连达梦 8 的五个翻车现场
5.1 现象:照抄 SQL Server 连接串,报“不支持关键字”
有人直接把Server=xxx;Database=xxx;User Id=xxx;Password=xxx换成UseDAMENG,运行时报 “Keyword not supported” 或“连接字符串中的 Data 参数不受支持”。原因很简单:达梦适配器的连接串解析器不认Database、User Id这些 SQL Server 风格的关键字。
解决:改用达梦适配器自己的关键字。注意UserId中间没有空格,不是User Id;Database在达梦里对应的是Schema。改完再跑,这个报错就消失了。
5.2 现象:装了包但找不到 UseDAMENG 扩展方法
代码里明明写好了optionsBuilder.UseDAMENG(...),编译却说上下文没有这个方法。原因通常是装了错误的包——有人装的是达梦的 ADO.NET 驱动包(比如只有DmProvider),那个包只提供底层连接,不提供 EF Core 的UseDAMENG扩展方法;也有人装了高版本适配包但项目是 .NET Core 3.1,版本错位导致方法签名不匹配。
解决:确认ConsoleApp1.csproj里的PackageReference是达梦官方 EF Core 适配包,并且版本对应 EF Core 3.1。还原后到 NuGet 缓存里查一下包的实际版本,如果目标框架不是 netcoreapp3.1,把它卸了重装正确版本。
5.3 现象:客户端能看到表,EF Core 查询报“表或视图不存在”
用达梦管理工具连接同一个库,能看到TEST_USER表,查数据也没问题;但程序跑context.TestUsers.ToList()就报无效的表名。原因就是连接串里没配Schema,EF Core 默认在当前用户模式里找表,而你要查的表在另一个 schema 下。
解决:连接串里补上Schema=TEST,或者实体上打[Table("TEST_USER")]配合[Schema("TEST")]。两个方案选一个,优先改连接串,因为只动一处就能全局生效。
5.4 现象:Skip/Take 分页翻车,数据被全量查出
列表页用Skip(20).Take(10)做分页,肉眼观察还没什么问题,后台日志显示 EF Core 把整张表都查出来了,然后在内存里做分页。数据量在十几万行时,这一条查询能把应用内存打满。原因是适配包版本较旧,没有把Skip/Take翻译成达梦的分页方言,而是退化成全表扫描加内存操作。
解决:升级达梦 EF Core 适配包到一个较新版本。升级后观察生成的 SQL 是否出现了LIMIT或ROWNUM相关子句。如果适配包迟迟不更新,退路是写原生 SQL 查询,FromSqlRaw可以绕过 LINQ 翻译直接执行达梦方言,分页和性能问题一次解决。
5.5 现象:写入中文正常,查询出来是乱码
插入数据库的中文在管理工具里看是乱的,或者查出来显示成问号。这是因为达梦实例的初始化字符集与客户端编码不一致。达梦 8 安装时可以选字符集,常见的有 UTF-8 和 GBK,客户端连接串里如果不显式指定,可能走了默认字符集。
解决:连接串加Charset=UTF-8(如果达梦实例是 GBK 就写 GBK),保持两端一致。如果已经写入了大量乱码数据,先改连接串再重新插入测试行——之前写坏的旧数据只能靠更新语句或重导修复。检查字符集要两头看:达梦实例初始化参数,和连接串里的 Charset 配置。
6. 确认它真的连通了:两个能直接抄的验证技巧
6.1 打开 EF Core 日志看翻译出来的 SQL
连接串对了、包对了,但增删改查出来的结果对不对,不能只靠眼睛看。我习惯在DbContext配置里直接打开日志,让 EF Core 把每次生成的 SQL 打到控制台:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder .UseDAMENG("Server=127.0.0.1;Port=5236;UserId=SYSDBA;Password=SYSDBA;Schema=TEST") .EnableSensitiveDataLogging() .LogTo(Console.WriteLine); }LogTo(Console.WriteLine)是 EF Core 3.1 里注册日志输出的方式,它把执行的 SQL、参数值、执行耗时都打到控制台。EnableSensitiveDataLogging会把参数值明文打出来,方便核对写入的参数是不是预期的值。看日志重点看三处:SQL 里表名有没有带前缀、WHERE 条件是否合理、有没有出现全表扫描的分页 SQL。
6.2 用一条原生 SQL 做连通性冒烟
连接问题最容易和业务代码搅在一起,所以我每次换数据库环境,都会先跑一条原生 SQL:
using (var context = new TestDbContext()) { var result = context.Database .ExecuteSqlRaw("SELECT 1"); Console.WriteLine("Connectivity OK"); }ExecuteSqlRaw绕过 EF Core 的 LINQ 翻译,直接把 SQL 文本交给达梦执行。它能通,说明底层 ADO.NET 驱动、连接串、网络三件事没问题;它不通,问题还在连接链路里,别急着查业务代码。注意ExecuteSqlRaw返回的是受影响行数,对 SELECT 语句来说通常返回 -1,这正常,别当成异常。
从那以后,我每次接达梦或任何国产数据库的迁移任务,都强制自己先走一遍这个流程:还原示例项目、改连接串、开日志、跑原生 SQL 冒烟,然后再把业务代码搬过来。这套流程帮我避开了无数个“连接看着通了但 SQL 翻译有毛病”的隐蔽问题。希望这份例子包和里面的踩坑记录,能帮你把达梦适配的第一公里走顺。
本文还有配套的精品资源,点击获取