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、使用表達式
IDbSet的Find方法會返回一個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:該方法包含兩個參數skip和take,標識跳過 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);
}
