ChuanGoing 2019-09-10

距离上一篇近一个月时间,断断续续才把本篇码完,后面将加快进度,争取年度内把本系列基本介绍完成,同时督促本人持续学习。

本篇学习曲线:

1.初识Dapper

2.DbConnection

3.CommandBuilder实现单表操作(略)

4.演示

初识Dapper

Dapper是一个轻量级/高性能的ORM,核心功能是利用Emit反射获取IDataReader中的数据。我们可以利用它的对象关系映射实现简单CURD操作,或者直接用SQL语句实现复杂场景的CURD操作。

DbConnection

  顾名思义,数据库连接对象。Dapper提供DbConnection对象的扩展来操作数据库

public virtual int Execute(string sql, object param = null, int? commandTimeout = null, CommandType? commandType = null)
{
return _dbConnection.Execute(sql: sql, param: param, transaction: null, commandTimeout: commandTimeout, commandType: commandType);
} public virtual IEnumerable<TResult> Query<TResult>(string sql, object param = null, int? commandTimeout = null, CommandType? commandType = null)
{
return _dbConnection.Query<TResult>(sql: sql, param: param, transaction: null, commandTimeout: commandTimeout, commandType: commandType);
}

上面贴出的两个方法:Execute方法执行(增删改),Query执行查询操作。由此可以看到,Dapper操作数据库主要是手写SQL,当然我们也可以封装一些常用的方法来提高开发效率。

  当然,本篇重点不在于Dapper的介绍。接下来看看如何对Dapper来封装出我们自己可用的ORM。

CommandBuilder实现单表操作需要实现通用的单表的增删改查,我们得先定义/分解SQL语句:

1.操作的表对象(表)

2.表对象中的列对象(字段)

3.条件

定义字段对象/对象集合

public class Field
{
public Field(string name, object value = null)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentNullException(nameof(name), "invalid name");
}
Name = name;
Value = value;
}
public string Name { set; get; }
public object Value { set; get; }
}
 public class FieldsCollection : IEnumerable<Field>
{
private List<Field> _fields;
public Field this[int index] => _fields[index]; public FieldsCollection()
{
_fields = new List<Field>();
} public int Count => _fields.Count; public void Add(Field field)
{
_fields.Add(field);
} public void Add(params Field[] fields)
{
_fields.AddRange(fields);
} public IEnumerable<Field> GetFields()
{
return _fields;
} public IEnumerator<Field> GetEnumerator()
{
return _fields.GetEnumerator();
} IEnumerator IEnumerable.GetEnumerator()
{
return _fields.GetEnumerator();
}
}

定义条件

public abstract class Filter
{
public Filter(string field)
{
Field = field;
}
public virtual string Field { get; private set; }
}
 /// <summary>
/// 相等过滤
/// </summary>
public class EqualFilter : Filter
{
public EqualFilter(string field, object value)
: base(field)
{
Value = value;
}
public object Value { get; }
}

这里只贴出了"相等"条件,详细代码请查看篇尾给出的Github源码链接

定义排序字段

public class Sort
{
public string Field { get; set; }
public bool Order { get; set; } public Sort(string field, bool order = true)
{
Field = field;
Order = order;
}
}

查询语句的组装

public class QueryParameter
{
private List<Filter> _filters;
private List<Sort> _sorts;
public QueryParameter(FieldsCollection fileds, IEnumerable<Filter> filters = null, IEnumerable<Sort> sorts = null)
{
Fields = fileds.GetFields();
_filters = new List<Filter>();
if (filters != null)
{
_filters.AddRange(filters);
}
_sorts = new List<Sort>();
if (sorts != null)
{
_sorts.AddRange(sorts);
}
} public void AddFilter(Filter filter)
{
_filters.Add(filter);
} public void AddSort(Sort sort)
{
_sorts.Add(sort);
} public IEnumerable<Field> Fields { get; }
public IEnumerable<Filter> Filters => _filters;
public IEnumerable<Sort> Sorts => _sorts;
}

完成以上对象定义后,我们再来看看如何利用上述对象完成增删改查操作

 public SqlCommand GetCommand(TPrimaryKey key)
{
var obj = GetObjectContext<TEntity>();
FieldsCollection fields = new FieldsCollection();
List<Filter> filters = new List<Filter>();
foreach (var prop in obj.Properties)
{
foreach (var attr in prop.Attributes)
{
if (attr is PrimaryKeyAttribute keyAttr)
{
filters.Add(new Equal(prop.Info.Name, key));
}
} fields.Add(new Field(prop.Info.Name));
} QueryParameter queryParameter = new QueryParameter(fields, filters);
return CommandBuilder.QueryCommand(obj.Table, queryParameter, count: );
}

查询方法是根据主键做查询操作,其中数据库上下文对象通过泛型对象反射得到

  public virtual ObjectContext GetObjectContext<T>()
{
var type = typeof(T); string tableKey = ObjectContext.GetTableKey(typeof(T)); return DbContext.ObjectCollection.GetOrAdd(tableKey, entity => new ObjectContext(type));
}

新增方法类似上面的查询,只是SQL语句形式有区别

public SqlCommand InsertCommand(TEntity entity)
{
var obj = GetObjectContext<TEntity>();
FieldsCollection fields = new FieldsCollection();
foreach (var prop in obj.Properties)
{
fields.Add(new Field(prop.Info.Name, prop.Info.GetValue(entity)));
}
var com = CommandBuilder.InsertCommand(obj.Table, fields);
return com;
}

查询/新增方法,可以看到,上面代码通过反射/缓存得到增删改查的参数/值得信息,到这里为止,还没有形成有效的SQL语句。那么如何实现呢?

由于各个数据库(Mysql/Mssql/oracle..)中SQL语法有些差异,因此转化SQL的工作应该交由具体的某种数据库语句生成器去生成。本例采用的是Mysql数据库,因此我们可以看到上诉代码中涉及到CommandBuilder是基于mysql实现的,具体代码在这里就不贴了,详情看篇末Github链接。

 演示

基于上一篇Asp.net Core 系列之--1.事件驱动初探:简单事件总线实现(SimpleEventBus),改写一下CustomersController,repository由直接通过sql语句操作替换为本篇实现的封装代码,然后将事件/事件处理定义、实体/Dto等移到Domain层(为后续介绍铺路)

private readonly IRepository<Customer, Guid> _repository;
private readonly IEventBus _eventBus; public CustomersController(IEventBus eventBus, IRepository<Customer, Guid> repository)
{
_repository = repository;
_eventBus = eventBus;
} // 获取指定ID的客户信息
[HttpGet("{id}")]
public async Task<IActionResult> Get(Guid id)
{
var customer = await _repository.GetAsync(id); if (customer == null)
{
return NotFound();
}
return Ok(customer);
} // 创建新的客户信息
[HttpPost]
public async Task<IActionResult> Create([FromBody] CustomerDto model)
{
var name = model.Name;
if (string.IsNullOrEmpty(name))
{
return BadRequest();
} var customer = new Customer(name);
var result = await _repository.InsertAsync(customer);
await _eventBus.PublishAsync(new CustomerCreatedEvent(name)); return Created(Url.Action("Get", new { id = customer.Id }), customer.Id);
}

运行程序后,正确得到返回数据

Dapper实现ORM基本功能到此算告一段落,读者有兴趣的话可以查阅Dapper源码,后续有机会的话再介绍下它的扩展功能

回顾

回顾一下本篇内容,首先简单介绍了Dapper是什么、能做什么,然后我们基于mysql实现了Dapper的简单对象关系映射,最后利用WinPowershell的Invoke-WebRequest模拟http请求演示了数据的创建与获取。

本篇已涉及到仓储的概念,也是领域模型的重要环节,后续我们将会渐进式的介绍DDD相关概念及设计原理

代码

本篇涉及的源码在Github的https://github.com/ChuanGoing/Start.git 的DapperOrm分支可以找到。

Asp.net Core 系列之--2.ORM初探:Dapper实现MySql数据库各类操作的更多相关文章

  1. Asp.net Core 系列之--1.事件驱动初探:简单事件总线实现(SimpleEventBus)

    ChuanGoing 2019-08-06  前言 开篇之前,简单说明下随笔原因.在园子里游荡了好久,期间也起过要写一些关于.NET的随笔,因各种原因未能付诸实现. 前段时间拜读daxnet的系列文章 ...

  2. Asp.net Core 系列之--3.领域、仓储、服务简单实现

    ChuanGoing 2019-11-11  距离上篇近两个月时间,一方面时因为其他事情耽搁,另一方面也是之前准备不足,关于领域驱动有几个地方没有想通透,也就没有继续码字.目前网络包括园子里大多领域驱 ...

  3. Asp.net Core 系列之--4.事务、日志及错误处理

    ChuanGoing 2019-11-17 这篇原本时想把事务处理.日志处理.错误处理.授权于鉴权一并介绍完的,授权和鉴权我想结合自定义权限来介绍,全部放到这里篇幅可能太长,因此权限部分将会在下篇来介 ...

  4. 【目录】asp.net core系列篇

    随笔分类 - asp.net core系列篇 asp.net core系列 68 Filter管道过滤器 摘要: 一.概述 本篇详细了解一下asp.net core filters,filter叫&q ...

  5. asp.net core系列 36 WebAPI 搭建详细示例

    一.概述 HTTP不仅仅用于提供网页.HTTP也是构建公开服务和数据的API强大平台.HTTP简单灵活且无处不在.几乎任何你能想到的平台都有一个HTTP库,因此HTTP服务可以覆盖广泛的客户端,包括浏 ...

  6. Asp.net Core 系列之--5.认证、授权与自定义权限的实现

    ChuanGoing 2019-11-24 asp.net core系列已经来到了第五篇,通过之前的基础介绍,我们了解了事件订阅/发布的eventbus整个流程,初探dapper ORM实现,并且简单 ...

  7. 1.1专题介绍「深入浅出ASP.NET Core系列」

    大家好,我是IT人张飞洪,专注于.NET平台十年有余. 工作之余喜欢阅读和写作,学习的内容包括数据结构/算法.网络技术.Linux系统原理.数据库技术原理,设计模式.前沿架构.微服务.容器技术等等…… ...

  8. asp.net core系列 30 EF管理数据库架构--必备知识 迁移

    一.管理数据库架构概述 EF Core 提供两种主要方法来保持 EF Core 模型和数据库架构同步.一是以 EF Core 模型为基准,二是以数据库为基准. (1)如果希望以 EF Core 模型为 ...

  9. asp.net core系列 40 Web 应用MVC 介绍与详细示例

    一. MVC介绍 MVC架构模式有助于实现关注点分离.视图和控制器均依赖于模型. 但是,模型既不依赖于视图,也不依赖于控制器. 这是分离的一个关键优势. 这种分离允许模型独立于可视化展示进行构建和测试 ...

随机推荐

  1. Spring Boot (十一): Spring Boot 定时任务

    在实际的项目开发工作中,我们经常会遇到需要做一些定时任务的工作,那么,在 Spring Boot 中是如何实现的呢? 1. 添加依赖 在 pom.xml 文件中只需引入 spring-boot-sta ...

  2. reduce方法应用技巧

    定义和用法 reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值. 注意: reduce() 对于空数组是不会执行回调函数的. 浏览器支持 方法 Chro ...

  3. SpringBootSecurity学习(23)前后端分离版之OAuth2.0 其它模式

    密码模式 前面介绍了授权码模式和刷新令牌两种获取最新令牌的方法,下面来看一下其它模式.首先看密码模式,我们默认配置的三种模式中其实就包含密码模式的支持: 因此我们启动项目,直接使用密码模式即可,访问地 ...

  4. 疑似网络抖动引起的RAC单节点宕机

  5. 编译安装msyql

    环境: ubuntu18.04.2 mysql5.7.21 #创建mysql属组 groupadd mysql useradd -g mysql mysql #查看属组 tail /etc/passw ...

  6. [JZOJ100047] 【NOIP2017提高A组模拟7.14】基因变异

    Description 21 世纪是生物学的世纪,以遗传与进化为代表的现代生物理论越来越多的 进入了我们的视野. 如同大家所熟知的,基因是遗传因子,它记录了生命的基本构造和性能. 因此生物进化与基因的 ...

  7. sqlserver 查看最耗时的前10个存储过程

    SELECT TOP OBJECT_NAME(a.object_id,database_id) SP_Name, DB_NAME(a.database_id) Database_Name, a.cac ...

  8. linux上安装newman

    1. newman的安装依赖nodejs,首先安装node/npm 进入到 /usr/local目录[root@ipha-dev71- local]# cd /usr/local [root@ipha ...

  9. 自定义的Spring Boot starter如何设置自动配置注解

    本文首发于个人网站: 在Spring Boot实战之定制自己的starter一文最后提到,触发Spring Boot的配置过程有两种方法: spring.factories:由Spring Boot触 ...

  10. mysql数据库limit分页,排序操作

    看到网上很多朋友在问,limit分页之后按照字段属性排序的问题,在这里分享一下我的用法: 1.网上答案: 每页显示5个,显示第三页信息,按照年龄从小到大排序 select * from student ...