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. 《构建之法》MSF&需求分析

    第七章 MSF MSF基本原则 推动信息共享与沟通 为共同的远景而工作 充分授权和信任 各司其职,对项目共同负责 交付增量的价值 保持敏捷,预期和适应变化 投资质量 学习所有的经验 与顾客合作 MSF ...

  2. 致所有.Net者和有梦想的朋友们 - 共勉

    这篇文章很早就想写的了,主要是人到了一定的年纪,就想唠叨一些看法,认不认可不重要,重要的是生活给予你的酸甜苦辣,你都想一吐为快. 这里主要基于多年来自己的一个行业感受和以及生活感想,唠叨一下工作以及生 ...

  3. pc vue 项目中的菜单权限控制

    在pc 管理系统这种类型的产品,通常会涉及到账号权限的控制,不同的账号权限能浏览的功能模块是不同的,对应侧边栏菜单模块的显示也会不同. 场景一.(电商类管理系统) 登录 登录后,依次获取账号 toke ...

  4. 安装高可用Hadoop生态 (一 ) 准备环境

    为了学习Hadoop生态的部署和调优技术,在笔记本上的3台虚拟机部署Hadoop集群环境,要求保证HA,即主要服务没有单点故障,能够执行最基本功能,完成小内存模式的参数调整. 1.    准备环境 1 ...

  5. Python移动自动化测试面试 ☝☝☝

    Python移动自动化测试面试   ☝☝☝ Python移动自动化测试面试 学习 教程 1.super 是干嘛用的?在 Python2 和 Python3 使用,有什么区别?为什么要使用 super? ...

  6. 爬虫破解知乎登入(不使用Selenium模块)

    一.分析 知乎完成登入的步骤 首先获得cookies(如果不获得后面验证码无法获得) 获得验证码 提交登入相关内容 前两步简单稍微细心寻找规律即可 其中最难的是第三步应该他前端进行了js加密 这里没什 ...

  7. session与cookie,django中间件

    0819自我总结 一.session与cookie 1.django设置session request.session['name'] = username request.session['age' ...

  8. django中app分组

    08.13自我总结 django中app分组 一.django路由系统app进行分组 1.创建app 使用pycharm创建django的时候, 加上app的名字,后续多个app只需复制粘贴之前app ...

  9. linux系统取证

    目录 0x00 查看系统信息 0x01 用户及组信息 0x02 防火墙及路由信息 0x03 查看网络.端口信息 0x04 系统运行信息查看 0x05 日志查看分析 0x00 查看系统信息 name-a ...

  10. 网站是HTTP?10分钟变成HTTPS!域名免费添加配置SSL证书,变成https//环境

    对于小程序request请求需要https域名.navigator.geolocation定位也需要在https环境下才可以生效等问题: 前端开发越来越需要https环境来来测试一下API接口和各类问 ...