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中的依赖注入机制 ...
随机推荐
- uni-app 解析后台接口返回的HTML
正常使用rich-text是可以解决问题的,但是在支付宝小程序中不显示,在文档中看到" 支付宝小程序 nodes 属性只支持使用 Array 类型.如果需要支持 HTML String,则需 ...
- Blazor 国际化多语言界面 (I18nText )
在实际使用中,我们经常会遇到需要把程序界面多种语言切换,适应不同地区使用者的需求,本文介绍一个我初学Blazor接触到的库,边撸边讲解. 包名: Toolbelt.Blazor.I18nText ht ...
- python---十进制转换成n进制
""" 十进制转换成n进制 例子: 100转换成8进制-----144 256除8 商32 余0 32除8 商4 余0 4除8 商0 余4 每次结果的余数进栈, 最后出栈 ...
- ubuntu连接不到WiFi
ubuntu连接不到WiFi 在软件与更新中,进入附加驱动. 搜到对应的无线网卡驱动,安装后在重启电脑.
- 另类终端「GitHub 热点速览 v.22.15」
作者:HelloGitHub-小鱼干 除了编译器之外,终端也是我们日常打交道的软件之一.但,你用它看过股票吗?OpenBBTerminal 不仅能让你看股票,还能让你用科学的方法进行股票投资.说到投资 ...
- NodeJs学习日报day6——路由模块
const express = require('express') const app = express() app.get('/user', function(req, resp) { resp ...
- Java学习day17
继续学习了IO流的一些常用类以及GUI基础 做了自己的第一个Frame窗口 在做第一个Frame窗口时程序报错:java: 无法从静态上下文中引用非静态 变量 this 查看后发现不小心把MyFram ...
- CoreWCF 1.0 正式发布,支持 .NET Core 和 .NET 5+ 的 WCF
CoreWCF 1.0 正式发布,支持 .NET Core 和 .NET 5+ 的 WCF https://devblogs.microsoft.com/dotnet/corewcf-v1-relea ...
- Libco Hook 机制浅析
Libco Hook 机制浅析 之前的文章里我们提到过 Libco 有一套 Hook 机制,可以通过协程的让出(yield)原语将系统的阻塞系统调用改造为非阻塞的,这篇文章我们将深入解析 Hook 机 ...
- Node.js躬行记(19)——KOA源码分析(上)
本次分析的KOA版本是2.13.1,它非常轻量,诸如路由.模板等功能默认都不提供,需要自己引入相关的中间件. 源码的目录结构比较简单,主要分为3部分,__tests__,lib和docs,从名称中就可 ...