zoukankan      html  css  js  c++  java
  • 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);
            }
    View Code

      上面贴出的两个方法: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; }
        }
    View Code
     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();
            }
        }
    View Code

    定义条件

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

    这里只贴出了"相等"条件,详细代码请查看篇尾给出的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;
        }
    View Code

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

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

  • 相关阅读:
    PHP的MySQL扩展:PHP访问MySQL的常用扩展函数
    PHP的MySQL扩展:MySQL数据库概述
    JQuery笔记:JQuery和JavaScript的联系与区别
    《千与千寻》给读者带来了什么?
    Canvas入门(3):图像处理和绘制文字
    Canvas入门(2):图形渐变和图像形变换
    Canvas入门(1):绘制矩形、圆、直线、曲线等基本图形
    python第三十二天----6.3作业中…………
    python第三十一天-----类的封装、继承,多态.....
    python第三十天-类
  • 原文地址:https://www.cnblogs.com/ChuanGoing/p/11401321.html
Copyright © 2011-2022 走看看