前言
这两年国内的数据库产品如异军突起,保持了非常良好的发展势头,如达梦,人大金仓,神通,南大通用GBase,以及基于ServerLess形态的TDSQL等,今天以达梦为例,聊一下基于EFCore组件接入达梦数据库的案例。
环境配置
达梦的官方站点给出了比较详细的安装配置说明,这里不在赘述,大家可以看以下官方的文档,传送门👉:https://eco.dameng.com/document/dm/zh-cn/start/
官方提供了基于Windows,Linux,以及容器(docker)的安装说明。我这边尝试了Windows和docker两种形式,安装好后的截图如下👇
- Windows
- Docker
然后通过官方提供的客户端工具,可以看到我们安装好的数据库
上手项目
1、创建
创建一个任何形态的应用类项目,如web,控制台等(不推荐使用winform),这里以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的话,把数据库平滑迁移到达梦这边以后,业务代码几乎不用做太多的修改,当然具体情况还是要结合实际情况来看。
说起来,达梦官方还给开发者们安排了一些主流数据库的迁移方案,可以参考执行👉:https://eco.dameng.com/document/dm/zh-cn/start/migrate-mysql-dm.html
好了,基本就这样了,给国产数据库软件点个赞👍
ps:本文同步发表于InfoQ社区:https://xie.infoq.cn/article/e5e407d7ca7dd69e8769a7987