Asp.net Core 系列之--2.ORM初探:Dapper實現MySql數據庫各種操做

ChuanGoing 2019-09-10
html

  距離上一篇近一個月時間,斷斷續續才把本篇碼完,後面將加快進度,爭取年度內把本系列基本介紹完成,同時督促本人持續學習。mysql

本篇學習曲線:git

1.初識Dappergithub

2.DbConnectionsql

3.CommandBuilder實現單表操做(略)shell

4.演示數據庫

 

初識Dapper緩存

  Dapper是一個輕量級/高性能的ORM,核心功能是利用Emit反射獲取IDataReader中的數據。咱們能夠利用它的對象關係映射實現簡單CURD操做,或者直接用SQL語句實現複雜場景的CURD操做。oracle

DbConnectionapp

  顧名思義,數據庫鏈接對象。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分支能夠找到。

相關文章
相關標籤/搜索