Asp.net Core 系列之--2.ORM初探:Dapper实现MySql数据库各类操作

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 Sort(string column, bool asc = true)
        {
            ColumnName = column;
            ASC = asc;
        }
        public string ColumnName { get; set; }
        public bool ASC { get; set; }
    }

查询语句的组装

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: 1);
        }

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

  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分支可以找到。

原文地址:https://www.cnblogs.com/ChuanGoing/p/11401321.html

时间: 2024-11-02 02:08:05

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

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

原文:asp.net core系列 36 WebAPI 搭建详细示例 一.概述 HTTP不仅仅用于提供网页.HTTP也是构建公开服务和数据的API强大平台.HTTP简单灵活且无处不在.几乎任何你能想到的平台都有一个HTTP库,因此HTTP服务可以覆盖广泛的客户端,包括浏览器,移动设备和传统的桌面应用程序. ASP.NET Web API 是一个框架,基于.NET Framework 或.NET Core 之上构建 Web API. 从本章开始学习Web API系列时,先从一个示例开始,下面使用A

ASP.NET CORE系列【五】webapi整理以及RESTful风格化

原文:ASP.NET CORE系列[五]webapi整理以及RESTful风格化 介绍 什么是RESTful?  这里不多做赘述,详情请百度! 哈哈,本来还想巴拉巴拉介绍一些webapi, RESTful的, 还是算了,咱们直接上干货!(原因是懒!哈哈) 使用 以前使用过mvc的人对webapi 应该都很熟悉,先看一段熟悉的代码 大伙发现了什么没?跟以往mvc大多数相同,但有些地方不同 ,我们来一起看看有何区别 1.首先SysUsersController上面有一段代码 [Produces("a

ASP.NET CORE系列【一】搭建ASP.NET CORE项目

原文:ASP.NET CORE系列[一]搭建ASP.NET CORE项目 为什么要使用 ASP.NET Core? NET Core 刚发布的时候根据介绍就有点心里痒痒,微软的尿性都懂的,新东西bug太多,现在2.0也发布很久了,决定研究一下. ASP.NET Core官方文档https://docs.microsoft.com/en-us/aspnet/core/getting-started ASP.NET Core 具有如下优点: 生成 Web UI 和 Web API 的统一场景. 集成

ASP.NET CORE系列【二】使用Entity Framework Core进行增删改查

原文:ASP.NET CORE系列[二]使用Entity Framework Core进行增删改查 介绍 EntityFrameworkCore EF core 是一个轻量级的,可扩展的EF的跨平台版本.对于EF而言 EF core 包含许多提升和新特性,同时 EF core 是一个全新的代码库,并不如 EF6 那么成熟和稳定.EF core 保持了和EF相似的开发体验,大多数顶级API都被保留了下来,所以,如果你用过EF6,那么上手EF core你会觉得非常轻松和熟悉,EF core 构建在一

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

大家好,我是架构师张飞洪,专注于.NET平台十年有余. 工作之余喜欢阅读和写作,学习的内容包括数据结构/算法.网络技术.Linux系统原理.数据库技术原理,设计模式.前沿架构.微服务.容器技术等等…… 喜欢但不限于,Java.C.C++.Python.Javascript……Wait……不装了,因为我也还在学习的路上,愿你我一起终生学习. 定调 这里先给整个文章的系列定一个调调,起名深入浅出ASP.NET Core系列.深入的目的是希望能了解底层机制,浅出是为了学习能不让自己那么枯燥,给自己定个

目录导航「深入浅出ASP.NET Core系列」

希望给你3-5分钟的碎片化学习,可能是坐地铁.等公交,积少成多,水滴石穿,谢谢关注. 入门篇 1.1课程介绍「深入浅出ASP.NET Core系列」 1.2环境安装「深入浅出ASP.NET Core系列」 1.3创建项目「深入浅出ASP.NET Core系列」 1.4部署到IIS「深入浅出ASP.NET Core系列」 1.5准备CentOS和Nginx环境「深入浅出ASP.NET Core系列」 1.6部署到CentOS「深入浅出ASP.NET Core系列」 2.1命令行和JSON的配置「深

asp.net core 系列 14 错误处理

一.概述 本文介绍处理 ASP.NET Core 应用中常见错误的一些方法.主要是关于:开发环境异常页:非开发环境配置自定义异常处理页:配置状态代码页(没有正文响应,http状态400~599的). 1.1 开发环境异常页 要将应用配置为显示有关异常的详细信息的页面,请使用开发环境异常页.要环境设置为 Development,具体查看:asp.net core系列9环境.下面向 Startup.Configure 方法添加代码行: if (env.IsDevelopment()) { //注意:

asp.net core 系列 15 中间件

原文:asp.net core 系列 15 中间件 一.概述 中间件(也叫中间件组件)是一种装配到应用管道以处理请求和响应的软件. 每个组件:(1)选择是否将请求传递到管道中的下一个组件;(2)可以在管道中的下一个组件之前和之后执行工作. 请求委托用于生成请求管道. 请求委托会处理每个 HTTP 请求.使用以下方法配置请求委托:Run,  Map, Use扩展方法.可以将单个请求委托作为匿名方法(称为内联中间件in-line middleware) 或者可以在可重用类中定义.这些可重用的类和内联

asp.net core 系列 16 Web主机 IWebHostBuilder

原文:asp.net core 系列 16 Web主机 IWebHostBuilder 一.概述 在asp.net core中,Host主机负责应用程序启动和生存期管理.host主机包括Web 主机(IWebHostBuilder)和通用主机(IHostBuilder).Web 主机是适用于托管 Web 应用:通用主机(ASP.NET Core 2.1 或更高版本)是适用于托管非 Web 应用:在未来的版本中,通用主机将适用于托管任何类型的应用,包括 Web 应用. 通用主机最终将取代 Web