ASP.NET Core 中的 ORM 之 Dapper

时间:2020-01-16 10:41:58   收藏:0   阅读:123

Dapper 简介

Dapper是.NET的一款轻量级ORM工具(GitHub),也可称为简单对象映射器。在速度方面拥有微型ORM之王的称号。
它是半自动的,也就是说实体类和SQL语句都要自己写,但它提供自动对象映射。是通过对IDbConnection接口的扩展来操作数据库的。

优点

使用 Dapper

下面简单创建一个Web API应用并通过Dapper访问MySQL数据。

  1. 创建MySQL测试数据

    CREATE SCHEMA `ormdemo` ;
    
    CREATE TABLE `ormdemo`.`category` (
      `Id` INT NOT NULL AUTO_INCREMENT,
      `name` VARCHAR(45) NOT NULL,
      PRIMARY KEY (`Id`));
    
    CREATE TABLE `ormdemo`.`product` (
      `Id` INT NOT NULL AUTO_INCREMENT,
      `Name` VARCHAR(45) NOT NULL,
      `Price` DECIMAL(19,2) NULL,
      `Quantity` INT NULL,
      `CategoryId` INT NOT NULL,
      PRIMARY KEY (`Id`),
      INDEX `fk_product_category_idx` (`CategoryId` ASC),
      CONSTRAINT `fk_product_category`
        FOREIGN KEY (`CategoryId`)
        REFERENCES `ormdemo`.`category` (`Id`)
        ON DELETE CASCADE
        ON UPDATE NO ACTION);  
    
    INSERT INTO `ormdemo`.`category` (`Name`) VALUES("Phones");
    INSERT INTO `ormdemo`.`category` (`Name`) VALUES("Computers");
    
    INSERT INTO `ormdemo`.`product` (`Name`,`Price`,`Quantity`,`CategoryId`) VALUES("iPhone8",4999.99,10,1);
    INSERT INTO `ormdemo`.`product` (`Name`,`Price`,`Quantity`,`CategoryId`) VALUES("iPhone7",2999.99,10,1);
    INSERT INTO `ormdemo`.`product` (`Name`,`Price`,`Quantity`,`CategoryId`) VALUES("HP750",6000.00,5,2);
    INSERT INTO `ormdemo`.`product` (`Name`,`Price`,`Quantity`,`CategoryId`) VALUES("HP5000",12000.00,10,2);
  2. 创建Web API应用并添加NuGet引用

    Install-Package MySql.Data
    Install-Package Dapper
  3. 新建一个Product类

    public class Category
    {
        public int Id { get; set; }
    
        public string Name { get; set; }
    }
    
    public class Product
    {
        public int Id { get; set; }
    
        public string Name { get; set; }
    
        public int Quantity { get; set; }
    
        public decimal Price { get; set; }
    
        public int CategoryId { get; set; }
    
        public virtual Category Category { get; set; }
    }
  4. 新建一个DBConfig类用于创建并返回数据库连接

    using MySql.Data.MySqlClient;
    using System.Data;
    using System.Configuration;
    
    public class DBConfig
    {
        //ConfigurationManager.ConnectionStrings["Connection"].ConnectionString;
        private static string DefaultSqlConnectionString = @"server=127.0.0.1;database=ormdemo;uid=root;pwd=Open0001;SslMode=none;";
    
        public static IDbConnection GetSqlConnection(string sqlConnectionString = null)
        {
            if (string.IsNullOrWhiteSpace(sqlConnectionString))
            {
                sqlConnectionString = DefaultSqlConnectionString;
            }
            IDbConnection conn = new MySqlConnection(sqlConnectionString);
            conn.Open();
            return conn;
        }
    }
  5. 创建简单的仓储接口和类

    public interface IProductRepository
    {
        Task<bool> AddAsync(Product prod);
        Task<IEnumerable<Product>> GetAllAsync();
        Task<Product> GetByIDAsync(int id);
        Task<bool> DeleteAsync(int id);
        Task<bool> UpdateAsync(Product prod);
    }
    public class ProductRepository : IProductRepository
    {
        public async Task<IEnumerable<Product>> GetAllAsync()
        {
            using (IDbConnection conn = DBConfig.GetSqlConnection())
            {
                return await conn.QueryAsync<Product>(@"SELECT Id
                                                ,Name
                                                ,Quantity
                                                ,Price
                                                ,CategoryId
                                            FROM Product");
            }
        }
    
        public async Task<Product> GetByIDAsync(int id)
        {
            using (IDbConnection conn = DBConfig.GetSqlConnection())
            {
                string sql = @"SELECT Id
                                    ,Name
                                    ,Quantity
                                    ,Price 
                                    ,CategoryId
                                FROM Product
                                WHERE Id = @Id";
                return await conn.QueryFirstOrDefaultAsync<Product>(sql, new { Id = id });
            }
        }
    
        public async Task<bool> AddAsync(Product prod)
        {
            using (IDbConnection conn = DBConfig.GetSqlConnection())
            {
                string sql = @"INSERT INTO Product 
                                (Name
                                ,Quantity
                                ,Price
                                ,CategoryId)
                            VALUES
                                (@Name
                                ,@Quantity
                                ,@Price
                                ,@CategoryId)";
                return await conn.ExecuteAsync(sql, prod) > 0;
            }
        }
    
        public async Task<bool> UpdateAsync(Product prod)
        {
            using (IDbConnection conn = DBConfig.GetSqlConnection())
            {
                string sql = @"UPDATE Product SET 
                                    Name = @Name
                                    ,Quantity = @Quantity
                                    ,Price= @Price
                                    ,CategoryId= @CategoryId
                               WHERE Id = @Id";
                return await conn.ExecuteAsync(sql, prod) > 0;
            }
        }
    
        public async Task<bool> DeleteAsync(int id)
        {
            using (IDbConnection conn = DBConfig.GetSqlConnection())
            {
                string sql = @"DELETE FROM Product
                                WHERE Id = @Id";
                return await conn.ExecuteAsync(sql, new { Id = id }) > 0;
            }
        }
    }

    在Startup ConfigureServices方法里面配置依赖注入

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddTransient<IProductRepository, ProductRepository>();
    
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    }
  6. 在Controller里面调用仓储方法

    [Route("api/[controller]")]
    [ApiController]
    public class ProductController : ControllerBase
    {
        private readonly IProductRepository _productRepository;
        public ProductController(IProductRepository productRepository)
        {
            _productRepository = productRepository;
        }
    
        [HttpGet]
        public async Task<IActionResult> Get()
        {
            var data = await _productRepository.GetAllAsync();
            return Ok(data);
        }
    
        [HttpGet("{id}")]
        public async Task<IActionResult> Get(int id)
        {
            var data = await _productRepository.GetByIDAsync(id);
            return Ok(data);
        }
    
        [HttpPost]
        public async Task<IActionResult> Post([FromBody] Product prod)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
    
            await _productRepository.AddAsync(prod);
            return NoContent();
        }
    
        [HttpPut("{id}")]
        public async Task<IActionResult> Put(int id, [FromBody] Product prod)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
    
            var model = await _productRepository.GetByIDAsync(id);
            model.Name = prod.Name;
            model.Quantity = prod.Quantity;
            model.Price = prod.Price;
            await _productRepository.UpdateAsync(model);
    
            return NoContent();
        }
    
        [HttpDelete("{id}")]
        public async Task<IActionResult> Delete(int id)
        {
            await _productRepository.DeleteAsync(id);
            return NoContent();
        }
    }
  7. 测试API是否可以正常工作

  8. Dapper对存储过程和事务的支持

    存储过程

    using (var connection = My.ConnectionFactory())
    {
        connection.Open();
    
        var affectedRows = connection.Execute(sql,
            new {Kind = InvoiceKind.WebInvoice, Code = "Single_Insert_1"},
            commandType: CommandType.StoredProcedure);
    
        My.Result.Show(affectedRows);
    }

    事务

    using (var connection = My.ConnectionFactory())
    {
        connection.Open();
    
        using (var transaction = connection.BeginTransaction())
        {
            var affectedRows = connection.Execute(sql, new {CustomerName = "Mark"}, transaction: transaction);
    
            transaction.Commit();
        }
    }
  9. Dapper对多表映射的支持

    var selectAllProductWithCategorySQL = @"select * from product p 
        inner join category c on c.Id = p.CategoryId
        Order by p.Id";
    var allProductWithCategory = connection.Query<Product, Category, Product>(selectAllProductWithCategorySQL, (prod, cg) => { prod.Category = cg; return prod; });

使用 Dapper Contrib 或其他扩展

Dapper Contrib扩展Dapper提供了CRUD的方法

  1. 添加NuGet引用Dapper.Contrib

    Install-Package Dapper.Contrib
  2. 为Product类添加数据注解

    [Table("Product")]
    public class Product
    {
        [Key]
        public int Id { get; set; }
    
        public string Name { get; set; }
    
        public int Quantity { get; set; }
    
        public decimal Price { get; set; }
    
        public int CategoryId { get; set; }
    
        public virtual Category Category { get; set; }
    }
  3. 增加一个新的仓储类继承

    public class ContribProductRepository : IProductRepository
    {
        public async Task<bool> AddAsync(Product prod)
        {
            using (IDbConnection conn = DBConfig.GetSqlConnection())
            {
                return await conn.InsertAsync(prod) > 0;
            }
        }
    
        public async Task<IEnumerable<Product>> GetAllAsync()
        {
            using (IDbConnection conn = DBConfig.GetSqlConnection())
            {
                return await conn.GetAllAsync<Product>();
            }
        }
    
        public async Task<Product> GetByIDAsync(int id)
        {
            using (IDbConnection conn = DBConfig.GetSqlConnection())
            {
                return await conn.GetAsync<Product>(id);
            }
        }
    
        public async Task<bool> DeleteAsync(int id)
        {
            using (IDbConnection conn = DBConfig.GetSqlConnection())
            {
                var entity = await conn.GetAsync<Product>(id);
                return await conn.DeleteAsync(entity);
            }
        }
    
        public async Task<bool> UpdateAsync(Product prod)
        {
            using (IDbConnection conn = DBConfig.GetSqlConnection())
            {
                return await conn.UpdateAsync(prod);
            }
        }
    }

    修改Startup ConfigureServices方法里面配置依赖注入

    services.AddTransient<IProductRepository, ContribProductRepository>();

    测试,这样可以少写了不少基本的SQL语句。

  4. 其他一些开源的Dapper扩展

    类库提供的方法
    Dapper.SimpleCRUD Get GetList GetListPaged Insert Update Delete DeleteList RecordCount
    Dapper Plus Bulk Insert Bulk Delete Bulk Update Bulk Merge Bulk Action Async Bulk Also Action Bulk Then Action
    Dapper.FastCRUD Get Find Insert Update BulkUpdate Delete BulkDelete Count
    Dapper.Mapper Multi-mapping

引入工作单元 Unit of Work

仓储模式往往需要工作单元模式的介入来负责一系列仓储对象的持久化,确保数据完整性。网上关于工作单元模式的实现方式有多种,但其本质都是工作单元类通过创建一个所有仓储共享的数据库上下文对象,来组织多个仓储对象。

网上的一些实现方式:

优缺点不作讨论,适合自己的就是最好的,这里采用了另外一种实现方式:

源代码

Github

参考

来源:https://www.cnblogs.com/royzshare/p/9522127.html

原文:https://www.cnblogs.com/frank0812/p/12199707.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!