分享自己写的基于Dapper的轻量级ORM框架~

1、说明

本项目是一个使用.NET Standard 2.0开发的,基于 Dapper 的轻量级 ORM 框架,包含基本的CRUD以及根据表达式进行一些操作的方法,目前只针对单表,不包含多表连接操作。

github:https://github.com/iamoldli/NetSql

2、使用方法

2.2、安装

Install-Package NetSql

2.2、创建实体

创建Article实体类,继承EntityBase

public class Article : EntityBase
{
    [Column("Title")]
    public string Title1 { get; set; }

    public string Summary { get; set; }

    public string Body { get; set; }

    public Category Category { get; set; }

    public int ReadCount { get; set; }

    public bool IsDeleted { get; set; }

    public DateTime CreatedTime { get; set; }
}

public enum Category
{
    Blog,
    Movie
}

EntityBase是一个定义好的实体基类,包含一个泛型主键标识,默认是 Int 类型的,也可以指定 long 或者 string 类型

 public class Article : EntityBase<string>

2.3、定义数据库上下文(DbContext)

数据库上下文我是模仿的 EF,IDbContextOptions是数据库上下文配置项接口,默认包含了 SqlServer 的实现DbContextOptions,如果使用的是 MySql 或者 SQLite,需要额外安装对应的扩展包

Install-Package NetSql.MySql //MySql
Install-Package NetSql.SQLite //SQLite

这里我定义了一个BlogDbContext上下文,其中包含一个Articles数据集

public class BlogDbContext : DbContext
{
    public BlogDbContext(IDbContextOptions options) : base(options)
    {
    }

    public IDbSet<Article> Articles { get; set; }
}

2.4、数据集(DbSet)使用说明

2.4.1、创建数据库上下文实例

private readonly BlogDbContext _dbContext;
private readonly IDbSet<Article> _dbSet;

public DbSetTests()
{
    _dbContext = new BlogDbContext(new SQLiteDbContextOptions("Filename=./Database/Test.db"));
    _dbSet = _dbContext.Set<Article>();

    //预热
    _dbSet.Find().First();
}

2.4.2、插入

[Fact]
public async void InsertTest()
{
    var article = new Article
    {
        Title1 = "test",
        Category = Category.Blog,
        Summary = "这是一篇测试文章",
        Body = "这是一篇测试文章这是一篇测试文章这是一篇测试文章这是一篇测试文章这是一篇测试文章这是一篇测试文章这是一篇测试文章这是一篇测试文章",
        ReadCount = 10,
        IsDeleted = true,
        CreatedTime = DateTime.Now
    };

    await _dbSet.InsertAsync(article);

    Assert.True(article.Id > 0);
}

2.4.3、批量插入

[Fact]
public void BatchInsertTest()
{
    var sw = new Stopwatch();
    sw.Start();

    var tran = _dbContext.BeginTransaction();

    for (var i = 0; i < 10000; i++)
    {
        var article = new Article
        {
            Title1 = "test" + i,
            Category = i % 3 == 1 ? Category.Blog : Category.Movie,
            Summary = "这是一篇测试文章",
            Body = "这是一篇测试文章这是一篇测试文章这是一篇测试文章这是一篇测试文章这是一篇测试文章这是一篇测试文章这是一篇测试文章这是一篇测试文章",
            ReadCount = 10,
            IsDeleted = i % 2 == 0,
            CreatedTime = DateTime.Now
        };

        _dbSet.InsertAsync(article, tran);
    }

    tran.Commit();

    sw.Stop();

    var s = sw.ElapsedMilliseconds;

    Assert.True(s > 0);
}

2.4.4、根据主键删除

[Fact]
public void DeleteTest()
{
    var b = _dbSet.DeleteAsync(3).Result;

    Assert.True(b);
}

2.4.5、根据表达式删除

[Fact]
public async void DeleteWhereTest()
{
    var b = await _dbSet.Find(m => m.Id > 10).Delete();

    Assert.True(b);
}

[Fact]
public async void DeleteWhereTest()
{
    var b = await _dbSet.Find(m => m.Id > 10)
        .Where(m => m.CreatedTime > DateTime.Now).Delete();

    Assert.True(b);
}

2.4.6、修改

[Fact]
public async void UpdateTest()
{
    var article = await _dbSet.Find().First();
    article.Title1 = "修改测试";

    var b = await _dbSet.UpdateAsync(article);

    Assert.True(b);
}

2.4.7、根据表达式修改实体部分属性

[Fact]
public async void UpdateWhereTest()
{
    var b = await _dbSet.Find(m => m.Id == 1000).Update(m => new Article
    {
        Title1 = "hahahaah",
        ReadCount = 1000
    });

    Assert.True(b);
}

2.4.8、根据主键查询单个实体

[Fact]
public void GetTest()
{
    var article = _dbSet.GetAsync(100).Result;

    Assert.NotNull(article);
}

2.4.9、根据表达式查询单条数据

该方法返回结果集中的第一条数据

[Fact]
public async void GetWehreTest()
{
    var article = await _dbSet.Find(m => m.Id > 100).First();

    Assert.NotNull(article);
}

2.4.10、使用表达式

IDbSetFind方法会返回一个INetSqlQueryable对象,这个对象是模仿的 EF 里面的IQueryable,虽然有些不伦不类,但是是按照适合自己的方式设计的。

INetSqlQueryable目前包含以下方法:

  • Where:用于添加过滤条件
var query =  _dbSet.Find().Where(m => m.Id > 1);
  • WhereIf:根据指定条件来添加过滤条件
var query = _dbSet.Find().WhereIf(id > 1, m => m.Id > 200);
  • OrderBy:用于添加排序规则
var query = _dbSet.Find(m => m.Id > 200 && m.Id < 1000).OrderBy(m => m.Id, SortType.Desc);
  • Limit:该方法包含两个参数skiptake,标识跳过 skip 条数据,取 take 条数据
var query = _dbSet.Find(m => m.Id > 100 && m.Id < 120).Limit(5, 10);
  • Select:选择要返回的列
var query = _dbSet.Find().Select(m => new { m.Id, m.Title1 }).Limit(0, 10);

以上方法都是用于构造INetSqlQueryable的,下面的方法则是执行:

  • Max:查询最大值
var maxReadCount = _dbSet.Find().Max(m => m.ReadCount).Result;
  • Min:查询最小值
var maxReadCount = _dbSet.Find().Min(m => m.ReadCount).Result;
  • Count:查询数量
var count = _dbSet.Find(m => m.Id > 1000).Count().Result;
  • Exists:判断是否存在
var b = _dbSet.Find(m => m.Id > 1000).Exists().Result;
  • First:获取第一条数据
var article = _dbSet.Find(m => m.Id > 100 && m.Id < 120).First().Result;
  • Delete:删除数据
var b = _dbSet.Find(m => m.Id > 1000).Delete().Result;
  • Update:更新数据
var b = await _dbSet.Find(m => m.Id == 1000).Update(m => new Article
{
    Title1 = "hahahaah",
    ReadCount = 1000
});
  • ToList:获取结果集
var list = await _dbSet.Find(m => m.Id > 100 && m.Id < 120).ToList();

3、特性

表别名以及列名

[Table("blog_article")]
public class Article : EntityBase
{
    [Column("Title")]
    public string Title1 { get; set; }

    public string Summary { get; set; }

    public string Body { get; set; }

    public Category Category { get; set; }

    public int ReadCount { get; set; }

    public bool IsDeleted { get; set; }

    public DateTime CreatedTime { get; set; }
}

指定主键

可以通过KeyAttribute来指定某个字段为主键

4、泛型仓储(Repository)

平时开发时用到伪 DDD 比较多,所以框架提供了一个泛型仓储接口IRepository以及一个抽象实现RepositoryAbstract

/// <summary>
/// 判断是否存在
/// </summary>
/// <param name="where"></param>
/// <param name="transaction"></param>
/// <returns></returns>
Task<bool> ExistsAsync(Expression<Func<TEntity, bool>> where, IDbTransaction transaction = null);

/// <summary>
/// 新增
/// </summary>
/// <param name="entity">实体</param>
/// <param name="transaction">事务</param>
/// <returns></returns>
Task<bool> AddAsync(TEntity entity, IDbTransaction transaction = null);

/// <summary>
/// 批量新增
/// </summary>
/// <param name="list"></param>
/// <param name="transaction"></param>
/// <returns></returns>
Task<bool> AddAsync(List<TEntity> list, IDbTransaction transaction = null);

/// <summary>
/// 删除
/// </summary>
/// <param name="id"></param>
/// <param name="transaction"></param>
/// <returns></returns>
Task<bool> DeleteAsync(dynamic id, IDbTransaction transaction = null);

/// <summary>
/// 更新
/// </summary>
/// <param name="entity">实体</param>
/// <param name="transaction">事务</param>
/// <returns></returns>
Task<bool> UpdateAsync(TEntity entity, IDbTransaction transaction = null);

/// <summary>
/// 根据主键查询
/// </summary>
/// <param name="id"></param>
/// <param name="transaction"></param>
/// <returns></returns>
Task<TEntity> GetAsync(dynamic id, IDbTransaction transaction = null);

/// <summary>
/// 根据表达式查询单条记录
/// </summary>
/// <param name="where"></param>
/// <param name="transaction"></param>
/// <returns></returns>
Task<TEntity> GetAsync(Expression<Func<TEntity,bool>> where, IDbTransaction transaction = null);

/// <summary>
/// 分页查询
/// </summary>
/// <param name="paging">分页</param>
/// <param name="where">过滤条件</param>
/// <param name="transaction">事务</param>
/// <returns></returns>
Task<List<TEntity>> PaginationAsync(Paging paging = null, Expression<Func<TEntity, bool>> where = null, IDbTransaction transaction = null);

RepositoryAbstract中包含实体对应的数据集IDbSet以及数据上限为IDbContext

protected readonly IDbSet<TEntity> Db;
protected readonly IDbContext DbContext;

protected RepositoryAbstract(IDbContext dbContext)
{
    DbContext = dbContext;
    Db = dbContext.Set<TEntity>();
}

对于事务,建议使用工作单元IUnitOfWork

public interface IUnitOfWork
{
    /// <summary>
    /// 打开一个事务
    /// </summary>
    /// <returns></returns>
    IDbTransaction BeginTransaction();

    /// <summary>
    /// 提交
    /// </summary>
    /// <returns></returns>
    void Commit();

    /// <summary>
    /// 回滚
    /// </summary>
    void Rollback();
}

项目已经包含了一个实现UnitOfWork

6、仓储使用方法

6.1、定义仓储

public interface IArticleRepository : IRepository<Article>
{
}

6.2、创建仓储实例

private readonly IArticleRepository _repository;

public RepositoryTest()
{
    var dbContext = new BlogDbContext(new SQLiteDbContextOptions("Filename=./Database/Test.db"));
    _repository = new ArticleRepository(dbContext);
}

6.3、新增

[Fact]
public async void AddTest()
{
    var article = new Article
    {
        Title1 = "test",
        Category = Category.Blog,
        Summary = "这是一篇测试文章",
        Body = "这是一篇测试文章这是一篇测试文章这是一篇测试文章这是一篇测试文章这是一篇测试文章这是一篇测试文章这是一篇测试文章这是一篇测试文章",
        ReadCount = 10,
        IsDeleted = true,
        CreatedTime = DateTime.Now
    };

    await _repository.AddAsync(article);

    Assert.True(article.Id > 0);
}

6.4、批量增加

[Fact]
public void BatchInsertTest()
{
    var list = new List<Article>();
    for (var i = 0; i < 10000; i++)
    {
        var article = new Article
        {
            Title1 = "test" + i,
            Category = i % 3 == 1 ? Category.Blog : Category.Movie,
            Summary = "这是一篇测试文章",
            Body = "这是一篇测试文章这是一篇测试文章这是一篇测试文章这是一篇测试文章这是一篇测试文章这是一篇测试文章这是一篇测试文章这是一篇测试文章",
            ReadCount = 10,
            IsDeleted = i % 2 == 0,
            CreatedTime = DateTime.Now
        };
        list.Add(article);
    }
    var sw = new Stopwatch();
    sw.Start();

    _repository.AddAsync(list);

    sw.Stop();
    var s = sw.ElapsedMilliseconds;

    Assert.True(s > 0);
}

6.5、删除

[Fact]
public async void DeleteTest()
{
    var b = await _repository.DeleteAsync(2);

    Assert.True(b);
}

6.6、修改

[Fact]
public async void UpdateTest()
{
    var article = await _repository.GetAsync(2);
    article.Title1 = "修改测试";

    var b = await _repository.UpdateAsync(article);

    Assert.True(b);
}

6.7、分页查询

[Fact]
public async void PaginationTest()
{
    var paging = new Paging(1, 20);
    var list = await _repository.PaginationAsync(paging, m => m.Id > 1000);

    Assert.True(paging.TotalCount > 0);
}

未完待续~

原文地址:https://www.cnblogs.com/oldli/p/9551606.html

时间: 2024-12-19 11:17:10

分享自己写的基于Dapper的轻量级ORM框架~的相关文章

轻量级ORM框架初探-Dapper与PetaPoco的基本使用

一.EntityFramework EF是传统的ORM框架,也是一个比较重量级的ORM框架.这里仍然使用EF的原因在于为了突出轻量级ORM框架的性能,所谓有对比才有更优的选择. 1.1 准备一张数据库表 (1)For MSSQL CREATE TABLE [dbo].[Posts] ( [Id] INT NOT NULL PRIMARY KEY IDENTITY, [CategoryId] INT NOT NULL, [Slug] VARCHAR(120) NOT NULL, [Title] N

.NET轻量级ORM框架Dapper入门精通

一.课程介绍 本次分享课程包含两个部分<.NET轻量级ORM框架Dapper修炼手册>和<.NET轻量级ORM框架Dapper葵花宝典>,阿笨将带领大家一起领略轻量级ORM框架Dapper的魅力. 本次分享课程适合人群范围: 一.<.NET轻量级ORM框架Dapper修炼手册>适合人群如下: 1.我是一个新手,主要工作面向的是MS SQL Server数据库,那么选择修炼手册一定没错. 2.对Dapper从没有过了解或者了解不全面的同学,想通过学习进一步提升对Dappe

c# 轻量级ORM框架 实现(一)

发布一个自己写的一个轻量级ORM框架,本框架设计期初基于三层架构.所以从命名上来看,了解三层的朋友会很好理解. 设计该框架的目的:不想重复的写增删改查,把精力放到功能实现上. 发布改框架的原因:希望给初学者一个参考,希望能给予好的建议,给自己一个展示机会. 在我开始之前,先说明一下,我对"软件工程学"概念东西几乎不通,最高文化程度:初二,所以不喜勿喷. 开始我的orm设计最底层 最底层的是一个DalBase,它是一个抽象的,实现了增删改查的基本操作. 它既然是一个抽象的,那么它的内部就

Android轻量级ORM框架ActiveAndroid入门教程(转)

注:没有找到出处,如有侵犯,请告知 开始ActiveAndroid神奇之旅: 在AndroidManifest.xml中我们需要添加这两个 AA_DB_NAME (数据库名称,这个name不能改,但是是可选的,如果不写的话 是默认的"Application.db"这个值) AA_DB_VERSION (数据库版本号,同样是可选的 – 默认为1) <manifest ...> <application android:name="com.activeandro

c# 轻量级 ORM 框架 之 DBHelper 实现 (三)

周末了比较清闲,把自己的orm框架整理了下,开源了. 已经做出来的东西通常感觉有些简单,一些新手或许听到"框架"一类的词觉得有些"高深",简单来说orm就是把ado的封装. 在介绍这个框架的第一篇博文,已经把DalBase介绍了一下设计思路,本篇的DBHelper对象也是给dalBase来用的,可以说框架的所有定义对象都是为了它. 这里起名叫DBHelper,因为我也是从写SQLHelper开始的,DBHelper只不过是所有类型对ado操作的各种方法的封装,所以本

c# 轻量级ORM框架 之 WhereHelper (二)

上篇文章发布了一些设计orm框架基层的和实现,有朋友提出WhereHelper是亮点,能被认可我表示高兴. 我就把WhereHelper设计思想和代码公开下. WhereHelper 的概念就是再拼接where 条件,为了能兼容各种数据库和参数化查询,故封装了该对象. 首先根据我的框架结构: 1.Common库 这里主要定义了,所有层都访问的类型及常用方法,因为是介绍WhereHelper的实现,对其它就不做详细解释了. WhereHelper定义到这一层是想着UI会用到该查询,故把该类型的定义

c# 轻量级 ORM 框架 之 Model解析 (四)

关于orm框架设计,还有必要说的或许就是Model解析了,也是重要的一个环节,在实现上还是相对比较简单的. Model解析,主要用到的技术是反射了,即:把类的属性与表的字段做映射. 把自己的设计及实现思路写出来也希望能有人给很好的优化建议,同时也给新手一点启发吧. 首先先给Model属性定义特性,先普及一下"特性"的概念和为什么用特性(Attribute). 简单来说,特性是给一个类,或方法,或属性 打上一个标记(或者叫附加信息),具体理解还是看例子比较好吧, 在做类与表之间映射时,我

基于.NET的微软ORM框架视频教程(Entity Framework技术)

基于.NET的微软ORM框架视频教程(Entity Framework技术) 第一讲  ORM映射 第二讲 初识EntifyFramework框架 第三讲 LINQ表达式查询 第四讲 LINQ方法查询 第五讲 LINQ TO Entities 第六讲 ObjectQuery查询(上) 第七讲 ObjectQuery查询(下) 第八讲 Entity中的增删改及事务处理 第九讲 Entity中的存储过程使用(完) 源代码及视频

基于轻量级ORM框架Dapper的扩展说明

这里简单的介绍一下本人基于Dapper作的一些简单的扩展,供大家参考. 为何要使用这款框架,相信大家看到下面排名就清楚了 其实在各大网站上,我们大概都会看到这样的一个对比效果图,在超过500次poco serialization的过程中所表现的性能,我们发现dapper是第二名, 当然第一名谁也无法超越,越底层的当然久越快,同时也就越麻烦. 至于如何使用进行基本的数据操作,我这里就不再阐述,http://www.cnblogs.com/Sinte-Beuve/p/4231053.html这里介绍