近期意外发现了国产达梦数据库对.NET Core环境下EFCore的支持,把测试过程分享如下。
1.测试环境
- .Net Core 2.1
- EntityFramework Core 2.2.4
- DM v8(达梦)
- Window 10 x64
2.踩坑记录
近些年数据库等基础设施国产化的呼声越来越高,达梦也算国产数据库中知名度较高的了,不过开发生态环境还不够完善,使用过程中也是各种坑。
2.1达梦数据库的.NET驱动
达梦数据库提供了dotNet、jdbc、odbc等多种驱动方式,在安装目录的drivers目录下都可以找到。市面上找了很久都没有发现.NET Core环境下连接达梦数据库的方案。2019年国内有大牛发布了开源项目FreeSql能够支持达梦数据库,但底层采用的依然是odbc驱动。
在浏览达梦数据库安装目录时意外发现了EFCore.Dm的类库。
并且在drivers/dotNet目录下的readme文件中也发现这样一段文字。
达梦.Net驱动分为DmProvider、EFDmProvider、EFCore.Dm、DmDialect和DmConnect。
DmProvider可以在.NET框架和NETCore框架下使用,NETCore框架下需要用户安装System.Text.Encoding.CodePages包或者直接以NUGET包的形式安装DmProvider,可以自动依赖的System.Text.Encoding.CodePages包
其中EFDmProvider是支持Entity Framework框架的驱动,它与数据库交互的部分由DmProvider完成,所以如果程序中需要使用EFDmProvider,需要同时引用DmProvider
DmConnect是达梦提供给VS的DDEX驱动,它也引用了DmProvider。
EFCore.Dm已支持EFCore2.1版本
DmDialect方言包有for Nhibernate3、for Nhibernate4、for Nhibernate5分别对应NET3.5、NET4、NET4.6.1;用户可根据开发环境选择对应的方言包版本;
Nhibernate中App.config配置要求:
1、驱动名称
NHibernate.Driver.DmDriver, DmDialect, Version=1.0.0.0, Culture=neutral, PublicKeyToken=072d25982b139bf8
2、方言包名称
NHibernate.Dialect.DmDialect, DmDialect, Version=1.0.0.0, Culture=neutral, PublicKeyToken=072d25982b139bf8
文件结构说明:
DmProvider文件夹中是完整的DmProvider驱动文件。使用DmProvider的DmBulkCopy对象,需要引用dmfldr_dll.dll以及此dll依赖的其他库。
EFDmProvider文件夹中是老版本的EFDmProvider,已不再更新版本。
EFDmProvider6.1.3-net40文件夹中是基于EntityFramework6.1.3及.Net4.0的EFDmProvider 2.0版本。
EFDmProvider6.1.3-net45文件夹中是基于EntityFramework6.1.3及.Net4.5的EFDmProvider 2.0版本。
DmConnect文件夹中是DmConnect驱动及所需文件。
DmDialect文件夹是不同版本NHibernate的方言包
gacutil.exe是全局程序集缓存工具,使用它可以将.Net驱动加载到程序集中。
既然你说你已支持,那就试试。
2.2安装Microsoft.EntityFrameworkCore.Dm
将Microsoft.EntityFrameworkCore.Dm添加到本地的程序包源,使用NuGet管理器安装,失败!
Internal.AspNetCore.Sdk是什么?打开nupkg文件,猜测就是AspNetCore.SDK的非正式环境,这估计也就解释了为什么这个程序包会藏在这里而在NuGet官方市场上找不到,看来是未完成的测试版了。此处是第一坑。
2.3直接引用类库
无法使用NuGet管理器安装,就根据Microsoft.EntityFrameworkCore.Dm的依赖关系直接引用相关类库。直接在项目中引用“drivers\dotNet\DmProvider\netstandard2.0”目录下的DmProvider.dll和“drivers\dotNet\EFCore.Dm\netstandard2.0”目录下的Microsoft.EntityFrameworkCore.Dm.dll。编译成功。修改数据库连接字符串,启动项目,数据库连接成功。
使用EFCore的CodeFirst模式创建实体类并建立与数据库表的映射关系,执行"update-databse"命令,总是提示“不存在表XXXX”。经过查阅达梦数据库文档才发现,问题可能出在用于登录数据库的用户身上。
在达梦数据库中有模式、登录和用户三个不同的概念,在官方技术论坛上有这样一段描述:
一、登录、用户、模式的定义
登录是相对于数据库服务器而言的,它仅仅代表着连接到数据库实例的权利,但并不代表创建登录就能操作数据库服务器里面的任何数据库(在达梦数据库中,一个数据库实例可以包含多个数据库)。
用户是相对于数据库实例中特定数据库来说的,数据库中的用户拥有对该数据库中指定范围内的对象的操作权利。
模式用来代表特定数据库中的一个对象集,在概念上可将其看作是包含表、视图、索引和权限定义的对象集合。一个模式只作用于一个数据库,不同的数据库可以有同名模式。
二、登录、用户、模式的关系
一个登录可以对应多个不同数据库中的用户,单个数据库中最多只能有一个用户与某一登录对应,没有登录与之对应的用户是没有意义的。一个用户可以创建多个模式,一个模式中的对象(表、视图等)可以被多个用户使用。一个用户可以访问他所属数据库中的任意模式中的对象,只要授予他相应的权限。
为了更好的理解三者之间的关系,下面打个比喻。有一间房(对应一个达梦数据库运行实例),房里有多个保险柜(对应实例中的多个数据库),每个保险柜又有多个抽屉(对应所在数据库中的模式),抽屉里存放一些贵重的物品(对应数据库中的某个模式下的表、视图等数据实体)。
我们要进入房间需要房间的钥匙(对应登录名),进入房间后我们要打开保险柜需要保险柜的钥匙(对应用户名),并且进入房间并不代表我们可以打开保险柜,我们只有是某一保险柜的使用用户才能打开保险柜,以一个登录身份进入房间后,我们可能是多个保险柜的使用用户,这样我们有可能可以打开多个保险柜(一个登录可以对应多个不同数据库中的用户)。对保险柜而言,不同的用户身份对应保险柜中不同的抽屉,打开保险柜后,依据用户身份的不同,决定用户所能使用的抽屉,保险柜的一个用户可以使用多个抽屉,一个抽屉也可能被多个用户使用(一个用户可以创建多个模式,一个模式中的对象可以被多个用户使用)。
简单消化了上述文字,检查数据库发现用到的用户被锁定了,修改了用户选项,错误消失。此处是第二坑。
2.4 .NET Core 2.x环境
上面的错误消失了,又一个新的错误出现了。
“Method ‘get_Info’ in type ‘Microsoft.EntityFrameworkCore.Dm.Infrastructure.Internal.DmOptionsExtension’ from assembly ‘Microsoft.EntityFrameworkCore.Dm, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60’ does not have an implementation”
大意就是某个方法没实现,思来想去又从头检查了一遍,才发现是自己坑了自己。原来是我创建项目是默认选择了.NET Core 3.1的开发环境,人家说的是支持.NET Core 2.1,错误原因就在这里。将项目改成了.NET Core 2.1,重新编译项目,执行EFCore命令,数据表成功被创建!继续测试了其他功能,查询没有问题,插入、更新、删除都还存在问题,原因正在查找中。
2-25补充
继续测试,更换多个版本的EntityFramework Core(仅限于2.x版本),插入、更新、删除均报错,详细信息如下:
System.MissingMethodException: Method not found: 'Dm.DmTransaction Dm.DmConnection.BeginTransaction(System.Data.IsolationLevel)'.
at Microsoft.EntityFrameworkCore.Dm.Storage.Internal.DmRelationalConnection.BeginTransactionWithNoPreconditions(IsolationLevel isolationLevel, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Dm.Storage.Internal.DmRelationalConnection.BeginTransactionAsync(IsolationLevel isolationLevel, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.BeginTransactionAsync(CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(DbContext _, ValueTuple`2 parameters, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Dm.Storage.Internal.DmExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IReadOnlyList`1 entriesToSave, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
基本可以证明达梦8的.NET驱动EFCore.Dm存在BUG,尚未完成,不能使用。
还望达梦官方能早日更新,完善生态,造福广大开发者!
2-28再次补充
发给达梦官方的邮件今天有回复了,说是支持EFCore3.1驱动正在开发中,就让我们翘首以待吧!
参考资料: