net core天马行空系列-可用于依赖注入的,数据库表和c#实体类互相转换的接口实现
1.前言
hi,大家好,我是三合。作为一名程序猿,日常开发中,我们在接到需求以后,一般都会先构思一个模型,然后根据模型写实体类,写完实体类后在数据库里建表,接着进行增删改查, 也有第二种情况,就是有些人喜欢先在数据库里建表,然后再添加实体类。前者是code First,后者是db First,如果数据库表和c#实体类可以互相转换的话,那么无疑将大大加快我们的开发速度,很幸运的是,当前依靠一些第三方建模软件或者是efcode就可以实现,我们以ef core举例,
1.1 ef core根据实体类生成数据库表
//先定义一个我们自己的dbcontext
public class SqlServerDb : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var connectionString = MyConfiguration.GetConfiguration("sqlServerDbConnectionString");
if (string.IsNullOrWhiteSpace(connectionString))
{
throw new ArgumentNullException("sqlServer connectionString must not be null");
}
optionsBuilder.UseSqlServer(connectionString);
}
public DbSet<Customer> Customer { get; set; }
}
//然后在代码里这样调用
using (var database = new SqlServerDb())
{
database.Database.EnsureCreated();
}
那么efcore就会自动替我们生成一个数据库,库里面有一张表叫做Customer。整个过程无须我们手动干预。接着如果实体类有变更的话,也可以通过add-migration指令进行迁移。
1.2 ef core根据数据库表生成实体类
通过nuget安装了Microsoft.EntityFrameworkCore,Microsoft.EntityFrameworkCore.SqlServer,Microsoft.EntityFrameworkCore.Tools,Microsoft.VisualStudio.Web.CodeGeneration.Design这几个包之后,我们就可以在"程序包管理器控制台"中执行以下语句生成实体类:
Scaffold-DbContext "Data Source=192.168.12.34;Initial Catalog=数据库名称;User ID=登录名;Password=密码" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Force
1.3 ef core互相转换存在的一些缺陷
在我使用这种方式的过程中,发现了ef core的几个缺点。
- 当整个解决方案下,子项目比较多时,我要在程序包管理器控制台以及各个项目的切换上花费很多时间才能顺利执行命令,这就很麻烦。
- 我无法快速的获取某个实体类的建表语句(比如我希望只是生成sql,但不执行迁移),以及快速的从数据库表生成某个实体类(ef core可以自定义的根据表的集合生成指定的实体类,但是在项目中已存在该实体类的情况,要么直接生成失败,要么根据force条件直接覆盖项目中原有的实体类,这太粗暴了,有时候会将我手动修改过的实体类覆盖掉,我希望能自己控制这个生成的过程)。
- 很多数据库的efcore驱动不支持实体类注释迁移到数据库表的注释,这么重要的功能不知道为啥不实现。
- 整个ef core自成一派,没有提供接口给到我们,这样就无法将数据库表和c#实体类互转的功能集成到我们自己的系统里。
- 要获取迁移生成的sql以供备份,还要执行命令
Script-Migration -From 20220610.cs -To :"202206101.cs"
,执行这命令还要找到2个变更点,这还是很麻烦。
2 IDbGenerator横空出世
基于以上痛点,在找不到合适组件的情况下,我在SummerBoot框架中定义了IDbGenerator接口,实现了四种数据库(sqlserver,mysql,oracle(仅支持12),sqlite)表和c#实体类的互相转换。具体数据库表字段类型和c#类型之间的映射关系,我则是参考了各个ef core驱动里的实现,确保和ef core生成的表或者实体类相一致。接下来介绍整个使用过程。
2.1 通过nuget包添加SummerBoot引用
PM> Install-Package SummerBoot
2.2 在startup.cs类中注册服务
services.AddSummerBoot();
services.AddSummerBootRepository(it =>
{
//-----------以下为必填参数---------
//注册数据库类型,比如SqliteConnection,MySqlConnection,OracleConnection,SqlConnection
it.DbConnectionType = typeof(MySqlConnection);
//添加数据库连接字符串
it.ConnectionString = "";
});
2.3 根据实体类自动生成/修改数据库表
2.3.1 定义一个数据库实体类
实体类注解大部分来自于系统命名空间System.ComponentModel.DataAnnotations 和 System.ComponentModel.DataAnnotations.Schema,比如表名Table,列名Column,主键Key,主键自增DatabaseGenerated(DatabaseGeneratedOption.Identity),不映射该字段NotMapped,注释Description等,接下来以Customer为例
public class Customer
{
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { set; get; }
/// <summary>
/// 姓名
/// </summary>
[Description("姓名")]
public string Name { set; get; }
/// <summary>
/// 年龄
/// </summary>
[Description("年龄")]
public int Age { set; get; }
/// <summary>
/// 会员号
/// </summary>
[Description("会员号")]
public string CustomerNo { set; get; }
/// <summary>
/// 总消费金额
/// </summary>
[Description("总消费金额")]
public decimal TotalConsumptionAmount { set; get; }
}
2.3.2 注入IDbGenerator接口,调用GenerateSql方法生成建表或者修改表结构的sql
public class TestController : Controller
{
private readonly IDbGenerator dbGenerator;
public TestController(IDbGenerator dbGenerator)
{
this.dbGenerator = dbGenerator;
}
[HttpGet("GenerateSql")]
public async Task<IActionResult> GenerateSql()
{
var generateSqls = dbGenerator.GenerateSql(new List<Type>() { typeof(Customer) });
return Content("ok");
}
}
2.3.3.1 如果数据库中不存在该表名的表
这里以mysql为例,生成的sql如下:
CREATE TABLE test.`Customer` (
`Id` int NOT NULL AUTO_INCREMENT,
`Name` text NULL ,
`Age` int NOT NULL ,
`CustomerNo` text NULL ,
`TotalConsumptionAmount` decimal(18,2) NOT NULL ,
PRIMARY KEY (`Id`)
)
ALTER TABLE test.`Customer` MODIFY `Name` text NULL COMMENT '姓名'
ALTER TABLE test.`Customer` MODIFY `Age` int NOT NULL COMMENT '年龄'
ALTER TABLE test.`Customer` MODIFY `CustomerNo` text NULL COMMENT '会员号'
ALTER TABLE test.`Customer` MODIFY `TotalConsumptionAmount` decimal(18,2) NOT NULL COMMENT '总消费金额'
虽然分成了2部分,没有生成的非常完美,但是不影响使用。
2.3.3.2 如果数据库中已存在该表名的表
那么生成的sql为,新增/更新字段的sql或者新增/更新注释的sql,为了避免数据丢失,不会有删除字段的sql,这里以Customer表举例,如果原本数据库里已经有了customer表,接着我们更新实体类,添加了一个地址的属性
public class Customer
{
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { set; get; }
/// <summary>
/// 姓名
/// </summary>
[Description("姓名")]
public string Name { set; get; }
/// <summary>
/// 年龄
/// </summary>
[Description("年龄")]
public int Age { set; get; }
/// <summary>
/// 会员号
/// </summary>
[Description("会员号")]
public string CustomerNo { set; get; }
/// <summary>
/// 总消费金额
/// </summary>
[Description("总消费金额")]
public decimal TotalConsumptionAmount { set; get; }
/// <summary>
/// 地址
/// </summary>
[Description("地址")]
public string Address { set; get; }
}
,那么此时生成的sql为
ALTER TABLE test.`Customer` ADD `Address` text NULL
ALTER TABLE test.`Customer` MODIFY `Address` text NULL COMMENT '地址'
虽然分成了2部分,没有生成的非常完美,但是不影响使用。
2.3.3.3 可以选择执行这些sql
把生成sql和执行sql分成2部分操作,对于日常而言是更方便的,我们可以快速拿到要执行的sql,进行检查,确认没问题后,可以保存下来,在正式发布应用时,留给dba审查。执行sql的代码如下
var generateSqls = dbGenerator.GenerateSql(new List<Type>() { typeof(Customer) });
foreach (var sqlResult in generateSqls)
{
dbGenerator.ExecuteGenerateSql(sqlResult);
}
2.3.4 表的命名空间
sqlserver里命名空间即schemas,oracle里命名空间即模式,sqlite和mysql里命名空间即数据库,
如果要定义不同命名空间下的表,可类似添加[Table("CustomerWithSchema", Schema = "test1")]注解即可。
[Table("CustomerWithSchema", Schema = "test1")]
public class CustomerWithSchema
{
public string Name { set; get; }
public int Age { set; get; } = 0;
/// <summary>
/// 会员号
/// </summary>
public string CustomerNo { set; get; }
/// <summary>
/// 总消费金额
/// </summary>
public decimal TotalConsumptionAmount { set; get; }
}
那么此时生成的sql为
CREATE TABLE test1.`CustomerWithSchema` (
`Name` text NULL ,
`Age` int NOT NULL ,
`CustomerNo` text NULL ,
`TotalConsumptionAmount` decimal(18,2) NOT NULL
)
2.3.5 自定义实体类字段到数据库字段的类型映射或名称映射
这里统一使用column注解,如[Column("Age",TypeName = "float")]
public class Customer : BaseEntity
{
public string Name { set; get; }
[Column("Age",TypeName = "float")]
public int Age { set; get; } = 0;
/// <summary>
/// 会员号
/// </summary>
public string CustomerNo { set; get; }
/// <summary>
/// 总消费金额
/// </summary>
public decimal TotalConsumptionAmount { set; get; }
}
生成的sql如下
CREATE TABLE `Customer2` (
`Id` int NOT NULL AUTO_INCREMENT,
`Name` text NULL ,
`Age` float NOT NULL ,
`CustomerNo` text NULL ,
`TotalConsumptionAmount` decimal(18,2) NOT NULL ,
PRIMARY KEY (`Id`)
)
2.4 根据数据库表自动生成实体类
2.4.1 注入IDbGenerator接口,调用GenerateCsharpClass方法生成c#类的文本
参数为数据库表名的集合和生成的实体类的命名空间
public class TestController : Controller
{
private readonly IDbGenerator dbGenerator;
public TestController(IDbGenerator dbGenerator)
{
this.dbGenerator = dbGenerator;
}
[HttpGet("GenerateClass")]
public async Task<IActionResult> GenerateClass()
{
var generateClasses = dbGenerator.GenerateCsharpClass(new List<string>() { "Customer" },"Test.Model");
return Content("ok");
}
}
生成的c#实体类如下,新建一个类文件并把文本黏贴进去即可
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Test.Model
{
[Table("Customer")]
public class Customer
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("Id")]
public int Id { get; set; }
/// <summary>
///姓名
/// </summary>
[Column("Name")]
public string Name { get; set; }
/// <summary>
///年龄
/// </summary>
[Column("Age")]
public int Age { get; set; }
/// <summary>
///会员号
/// </summary>
[Column("CustomerNo")]
public string CustomerNo { get; set; }
/// <summary>
///总消费金额
/// </summary>
[Column("TotalConsumptionAmount")]
public decimal TotalConsumptionAmount { get; set; }
/// <summary>
///地址
/// </summary>
[Column("Address")]
public string Address { get; set; }
}
}
3.与仓储接口配合使用
首先定义仓储接口,接口的具体实现类会由SummerBoot框架自动生成
[AutoRepository]
public interface ICustomerRepository:IBaseRepository<Customer>
{
}
接着就可以直接注入使用,整个增删改查操作如下所示
[ApiController]
[Route("[controller]")]
public class HomeController : ControllerBase
{
private readonly ICustomerRepository customerRepository;
private readonly IDbGenerator dbGenerator;
public HomeController(ICustomerRepository customerRepository, IDbGenerator dbGenerator)
{
this.customerRepository = customerRepository;
this.dbGenerator = dbGenerator;
}
[HttpGet("test")]
public IActionResult Test()
{
var results= dbGenerator.GenerateSql(new List<Type>() { typeof(Customer) });
var generateClasses = dbGenerator.GenerateCsharpClass(new List<string>() { "Customer" }, "Test.Model");
//执行ddl操作
foreach (var databaseSqlResult in results)
{
dbGenerator.ExecuteGenerateSql(databaseSqlResult);
}
var cusotmer = new Customer()
{
Name = "三合",
Age = 3,
CustomerNo = "00001",
Address = "福建省",
TotalConsumptionAmount = 999
};
//增
customerRepository.Insert(cusotmer);
//改
cusotmer.Age = 5;
customerRepository.Update(cusotmer);
//也可以这样改
customerRepository.Where(it => it.Name == "三合").SetValue(it => it.Age, 6).ExecuteUpdate();
//查
var dbCustomer= customerRepository.FirstOrDefault(it => it.Name == "三合");
//删
customerRepository.Delete(dbCustomer);
//也可以这样删
customerRepository.Delete(it=>it.Name== "三合");
return Content("ok");
}
}
自动生成数据库表与仓储接口配合使用,就会使我们的整个开发过程顺畅无比,犹如行云流水。
4.结尾
更多用法,可参考SummerBoot文档,也可以加入QQ群:799648362反馈建议。同时各位看官,如果你觉得这篇文章还不错的话,请记得一键三连哦(推荐+关注+github star)
net core天马行空系列-可用于依赖注入的,数据库表和c#实体类互相转换的接口实现的更多相关文章
- net core天马行空系列-各大数据库快速批量插入数据方法汇总
1.前言 hi,大家好,我是三合.我是怎么想起写一篇关于数据库快速批量插入的博客的呢?事情起源于我们工作中的一个需求,简单来说,就是有一个定时任务,从数据库里获取大量数据,在应用层面经过处理后再把结果 ...
- net core天马行空系列: 一个接口多个实现类,利用mixin技术通过自定义服务名,实现精准属性注入
系列目录 1.net core天马行空系列:原生DI+AOP实现spring boot注解式编程 2.net core天马行空系列: 泛型仓储和声明式事物实现最优雅的crud操作 哈哈哈哈,大家好,我 ...
- Java Web系列:Spring依赖注入基础
一.Spring简介 1.Spring简化Java开发 Spring Framework是一个应用框架,框架一般是半成品,我们在框架的基础上可以不用每个项目自己实现架构.基础设施和常用功能性组件,而是 ...
- NET Core源代码通过Autofac实现依赖注入
查看.NET Core源代码通过Autofac实现依赖注入到Controller属性 阅读目录 一.前言 二.使用Autofac 三.最后 回到目录 一.前言 在之前的文章[ASP.NET Cor ...
- net core天马行空系列: 泛型仓储和声明式事物实现最优雅的crud操作
系列目录 1.net core天马行空系列:原生DI+AOP实现spring boot注解式编程 哈哈哈哈,大家好,我就是那个高产似母猪的三合,长久以来,我一直在思考,如何才能实现高效而简洁的仓储模式 ...
- .Net Core 3.0 内置依赖注入:举例
原文:.Net Core 3.0 内置依赖注入:举例 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn ...
- net core天马行空系列:SummerBoot,将SpringBoot的先进理念与C#的简洁优雅合二为一
系列目录 1.net core天马行空系列:原生DI+AOP实现spring boot注解式编程 2.net core天马行空系列: 泛型仓储和声明式事物实现最优雅的crud操作 3.net core ...
- net core天马行空系列:移植Feign,结合Polly,实现回退,熔断,重试,超时,做最好用的声明式http服务调用端
系列目录 1.net core天马行空系列:原生DI+AOP实现spring boot注解式编程 2.net core天马行空系列: 泛型仓储和声明式事物实现最优雅的crud操作 3.net core ...
- 在.NET Core控制台程序中使用依赖注入
之前都是在ASP.NET Core中使用依赖注入(Dependency Injection),昨天遇到一个场景需要在.NET Core控制台程序中使用依赖注入,由于对.NET Core中的依赖注入机制 ...
随机推荐
- C语言,最大公约数---更相减损术
// 最大公约数 更相减损法 int commonDivisor() { int i,k,n=0; printf("请输入两个不同的正整数,用,隔开\n"); scanf(&quo ...
- AcWing 1222. 密码脱落
题目链接 题目描述: X星球的考古学家发现了一批古代留下来的密码. 这些密码是由A.B.C.D 四种植物的种子串成的序列. 仔细分析发现,这些密码串当初应该是前后对称的(也就是我们说的镜像串). 由于 ...
- AgileConfig 1.6.0 发布 - 支持服务注册与发现
大家好,好久没有输出博文了,一是因为比较忙,另外一个原因是最近主要的精力是在给 AgileConfig 添加一个新的功能:服务注册与发现. 先说说为什么会添加这个功能.我自己的项目是用 Consul ...
- 论文翻译:2021_Towards model compression for deep learning based speech enhancement
论文地址:面向基于深度学习的语音增强模型压缩 论文代码:没开源,鼓励大家去向作者要呀,作者是中国人,在语音增强领域 深耕多年 引用格式:Tan K, Wang D L. Towards model c ...
- vulnhub devguru渗透笔记
devguru渗透笔记 信息收集 kali ip 目标ip 首先我们扫描一下开放端口 nmap -A -p- 192.168.20.143 Starting Nmap 7.91 ( https://n ...
- python基础练习题(题目 学习使用auto定义变量的用法)
day28 --------------------------------------------------------------- 实例042:变量作用域 题目 学习使用auto定义变量的用法 ...
- Nginx下载文件指定文件名称
配置 location ^~/TEMP/ { alias/share/files/; if ($request_uri ~* ^.*\/(.*)\.(txt|doc|pdf|rar|gz|zip|do ...
- Ansible Notes: Tower Credential的本质
Ansible AWX/Tower credential 的本质 Ansible Tower (社区版本叫AWX)用credential这个资源来对象来存储playbook运行过程中用到的机密信息.比 ...
- macOS 安装 Nebula Graph 看这篇就够了
本文首发于 Nebula Graph Community 公众号 背景 刚学习图数据的内容,当前网上充斥大量的安装文档,参差不齐,部署起来令人十分头疼. 现整理一份比较完整的安装文档,供大家学习参考, ...
- javaScript深拷贝和浅拷贝简单梳理
在了解深拷贝和浅拷贝之前,我们先梳理一下: JavaScript中,分为基本数据类型(原始值)和复杂类型(对象),同时它们各自的数据类型细分下又有好几种数据类型 基本数据类型 数字Number 字符串 ...