前言
这两年国内的数据库产品如异军突起,保持了非常良好的发展势头,如达梦,人大金仓,神通,南大通用 GBase,以及基于 ServerLess 形态的 TDSQL 等,今天以达梦为例,聊一下基于 EFCore 组件接入达梦数据库的案例。
环境配置
达梦的官方站点给出了比较详细的安装配置说明,这里不在赘述,大家可以看以下官方的文档,传送门👉:https://eco.dameng.com/document/dm/zh-cn/start/官方提供了基于 Windows,Linux,以及容器(docker)的安装说明。我这边尝试了 Windows 和 docker 两种形式,安装好后的截图如下👇
然后通过官方提供的客户端工具,可以看到我们安装好的数据库
上手项目
1、创建
创建一个任何形态的应用类项目,如 web,控制台等(不推荐 Windows 窗体应用),这里以 webapi 为例
2、引入所需组件
引入 dmdbms.Microsoft.EntityFrameworkCore.Dm 及其相关组件,注意目前这个库只支持到了.net 6.x,高于这个大版本的话,在执行迁移命令时会报错。注意在引用该组件之前,同时引用 Microsoft.EntityFrameworkCore.Relational v6.0.16 版本以上,v7.x 版本以下的版本。我这里还引入了 Microsoft.EntityFrameworkCore.Tools,用于执行数据迁移,非 CodeFirst 模式可以不用这个。
强烈推荐新建项目优先考虑 CodeFirst 模式,我们要面向领域,面向系统建模,不要面向数据库建模,这也是为什么要使用 EFCore 作为 orm,而不是用 Freesql,SurgeSql 等,它们也支持 CodeFirst,但能力对比 EF 来说还是弱一些,且相关的生态太差。此外,说到 ORM,EFCore 作为.net 领域最著名的 ORM 组件,其虽然是由微软主导开发,但其归属和.net 一样并不完全属于微软,而是属于.NET Foundation,也就是.net 基金会,其开源协议也是对商业最为友好的 MIT 协议,所以几乎不用担心该 ORM 被卡脖子,另外像 FreeSql 也是该基金会下的项目,其旗下还有很多有名的项目,更多的大家可以到其官网查看(https://dotnetfoundation.org/)
对了,也正是由于.net 的归属是该基金会,所以像国产的龙芯架构,天然也支持了.net,还发布了龙芯架构专属.net sdk 的版本,而且对大版本的更新一直在跟进,所以国产化系统使用.net,相比其他国际化编程环境,还是比较可靠的。相关内容:https://www.oschina.net/news/193488/dotnet-add-loongarch64-architecture-port
<ItemGroup>
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.22" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.22">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="dmdbms.Microsoft.EntityFrameworkCore.Dm" Version="6.0.16.16649" />
</ItemGroup>
复制代码
3、创建模型 &配置模型关系和上下文
这里我们创建一个考试模型和一个试卷模型,并对其建立关联关系。
这里的模型是我从现有系统里摘过来了两个模型,不太具备典型性,仅供参考
public class Examination
{
public string Id { get; set; }=YitIdHelper.NextId().ToString();
public string Title { get; set; }
public string Description { get; set; } = "无";
public int AssociationId { get; set; } = 3300;
public string AssociationTitle { get; set; } = "测试大赛";
public string FilterInfo { get; set; } = "";
public string Remark { get; set; } = "";
public DateTime StartTime { get; set; } = DateTime.Now;
public DateTime EndTime { get; set; }=DateTime.Now.AddDays(1);
public double BaseScore { get; set; } = 15d;
public double BaseDuration { get; set; } = 15d;
public int ExamType { get; set; } = 0;
public DateTime CreatedAt { get; set; } = DateTime.Now;
public DateTime UpdatedAt { get; set;} = DateTime.Now;
public string CreatedBy { get; set; } = "system";
public string UpdatedBy { get; set; } = "";
public int IsDeleted { get; set; } = 0;
public int Status { get; set; } = 0;
public int OrderIndex { get; set; } = 0;
public string GroupCode { get; set; } = "";
public int IsStrict { get; set; } = 0;
public List<Paper> Papers { get; set; } = new List<Paper> { };
public ExaminationMode Mode { get; set; } = ExaminationMode.None;
}
public enum ExaminationMode
{
//不设定政策,暂时和宽松政策保持一致,后续会有标准
None=0,
//严格政策,未按时交卷给0分,作弊次数不能超过1次
Strict = 1,
//宽松政策,只要交卷了就有分,超时也可以交卷,但是不可以继续答题,机器不做作弊检查
Loose =2
}
public class Paper
{
public string Id { get; set; } = YitIdHelper.NextId().ToString();
public string Title { get; set; }
public string Description { get; set; }
public int Status { get; set; } = 1;
public int IsDeleted { get; set; } = 0;
public double Score { get; set; } = 10d;
public int Duration { get; set; } = 10;
public DateTime CreatedAt { get; set; } = DateTime.Now;
public DateTime UpdatedAt { get; set; } = DateTime.Now;
public Examination Examination { get; set; }
//public string ExaminationId { get; set; } = "";
}
复制代码
通过 EF 提供的 FluentAPI 配置字段属性的约束规则,以及相应的模型关系(也可以引入 DataAnnotations 命名空间,在模型中配置,但这里推荐使用 FluentAPI,因为 DataAnnotations 可以做的 FluentAPI 都可以做,反过来却不行)
public class ExaminationEntityConfig:IEntityTypeConfiguration<Examination>
{
public void Configure(EntityTypeBuilder<Examination> builder)
{
builder.ToTable(nameof(Examination));
builder.HasKey(x => x.Id);
builder.Property(e=>e.Title).HasMaxLength(100).IsUnicode(false).IsRequired();
builder.Property(e => e.Description).HasMaxLength(1000).IsUnicode(false);
builder.Property(e=>e.FilterInfo).HasMaxLength(2000).IsUnicode(false);
builder.Property(e => e.Mode).HasConversion<int>();
}
}
复制代码
public class PaperEntityConfig : IEntityTypeConfiguration<Paper>
{
public void Configure(EntityTypeBuilder<Paper> builder)
{
builder.ToTable(nameof(Paper));
builder.HasKey(p => p.Id);
builder.Property(p => p.Title).HasMaxLength(100).IsUnicode(false).IsRequired();
builder.HasOne<Examination>(e => e.Examination).WithMany(p => p.Papers).IsRequired().HasForeignKey(p => p.ExaminationId);
//显式声明外键
//builder.Property(p => p.ExaminationId).IsRequired().HasForeignKey();
}
}
复制代码
然后创建应用的数据操作上下文
public class ApplicationDbContext : DbContext
{
public DbSet<Examination> Examinations { get; set; }
public DbSet<Paper> Paper { get; set; }
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{ }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
}
}
复制代码
4、注入服务
在新创建的项目中,通过熟悉的方式注入数据服务上下文,我这里采用的是.net6 的顶级语句模板,写法如下
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
builder.Services.AddDbContext<ApplicationDbContext>(optionsBuilder =>
{
string connStr = builder.Configuration.GetConnectionString("DefaultConnection");
optionsBuilder.UseDm(connStr)
.LogTo(Console.WriteLine, LogLevel.Information);
});
复制代码
在配置文件中写好达梦数据库的连接串
"ConnectionStrings": {
//DM8
"DefaultConnection": "Data Source=localhost:5236;User ID=EXAMTEST;Password=abcdefg123"
}
复制代码
5、迁移
通过 ef tool 提供的迁移命令,在 VS 里的程序包管理器控制台窗口执行迁移命令(Add-Migration {xxx} 和 update-database)即可
注意,在执行 update-database 将模型结构同步到数据库时,要确保根据达梦文档提供的建库步骤分别创建了合适的表空间,对应的用户,并且授予该用户相关的权限,否则迁移时可能会报错哟,像这样👇
相关的操作文档在这里👉:https://eco.dameng.com/document/dm/zh-cn/start/dm-create-tablespace.html
6、测试
至此准备工作就绪,可以测试下成果了准备一个接口,分别测试写入,查询和关联查询的效果怎么样
public class HomeController : Controller
{
private ApplicationDbContext _ctx;
private ILogger<HomeController> _logger;
public HomeController(ApplicationDbContext context, ILogger<HomeController> logger)
{
_ctx = context;
_logger = logger;
}
//关联查询
public IActionResult Query()
{
var query = from p in _ctx.Set<Paper>().Take(10)
join exam in _ctx.Set<Examination>()
on p.ExaminationId equals exam.Id into pe
from exam in pe.DefaultIfEmpty()
select new
{
paperId = p.Id,
paperTitle = p.Title,
exam.Title,
exam.Description,
exam.StartTime,
exam.EndTime,
exam.AssociationId,
exam.AssociationTitle,
exam.BaseScore,
exam.BaseDuration,
exam.ExamType,
exam.CreatedAt,
exam.UpdatedAt,
};
return Json(new { code = 0, msg = "ok", data = new { total = query.Count(),items = query } });
}
//简单查询
public async Task<IActionResult> SimpleQuery()
{
var exam = await _ctx.Set<Examination>().FirstOrDefaultAsync();
//打印到控制台,方便观察调试,实际可以不要
var options = new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs)
};
Console.WriteLine(JsonSerializer.Serialize(exam, options));
return Json(new { code = 0, msg = "ok", data = exam });
}
//写入
public async Task<IActionResult> Insert()
{
List<Paper> papers = new List<Paper>();
string examId = YitIdHelper.NextId().ToString();
var exam = new Examination()
{
Id = examId,
Title = $"测试_{Guid.NewGuid()}_{DateTime.Now.Ticks + 1}",
OrderIndex = new Random().Next(0, 3)
};
for (int j = 0; j < 10; j++)
{
papers.Add(new Paper()
{
Title = $"测试试卷_{exam.Id}_{j}",
Description = $"归属考试_{examId}",
ExaminationId = examId,
});
}
await _ctx.AddRangeAsync(papers);
await _ctx.AddRangeAsync(exam);
await _ctx.SaveChangesAsync();
return Json(new { code = 0, msg = "ok" });
}
}
复制代码
分别请求对应的接口地址,效果如下
在看下数据库里的情况 EXAM 表
PAPER 表
结束语
就测试来说,达梦数据库的接入还是比较流畅,对 EF 的支持度也很好,如果之前使用了 orm 的话,把数据库平滑迁移到达梦这边以后,业务代码几乎不用做太多的修改。好了,基本就这样了,给国产数据库软件点个赞👍
评论