ASP.NET Core模組化前後端分離快速開發框架介紹之3、資料訪問模組介紹
原始碼
GitHub:https://github.com/iamoldli/NetModular
演示地址
地址:http://129.211.40.240:6220
賬戶:admin
密碼:admin
前端框架演示地址(臨時)
地址:http://progqx5cu.bkt.clouddn.com/skins/index.html#/
賬戶:admin
密碼:admin
目錄
1、開篇
2、快速建立一個業務模組
3、資料訪問模組介紹
簡介
NetModular
的資料訪問模組是基於 Dapper 擴充套件的輕量級的ORM,它本身是完全獨立的,可以在任何專案中直接使用。在NetModular
中也提供了擴充套件,能夠完美的與模組化整合在一起。
支援的功能
- [x] 支援SqlServer、MySql、SQLite資料庫
- [x] 基礎的CRUD方法
- [x] 批量新增、刪除、修改
- [x] 修改指定列
- [x] Lamdba表示式支援
- [x] 多表連線查詢
- [x] 分頁查詢
- [x] 分組查詢
- [x] 倉儲模式
- [x] 工作單元
- [x] 自定義表名、列名
- [x] 支援同步/非同步方法
使用方法
NetModular
本身已經做好了整合,所以業務模組中,可以直接寫程式碼,不用考慮注入的問題,如果想要了解它的整合邏輯的,可以檢視Data.AspNetCore
庫
Node: 資料庫上下文、倉儲和工作單元的注入方式採用的是
Scoped
1、新增資料庫上下文
資料庫上下文需要繼承DbContext
public class MallDbContext : DbContext
{
public MallDbContext(IDbContextOptions options) : base(options)
{
}
}
2、建立實體
實體需要繼承IEntity
介面,在NetModular
中,已經提供了幾個通用的實體基類,並且內部已經實現了對應的功能,比如EntityBase
:
public class EntityBase<TKey> : Entity<TKey> { /// <summary> /// 建立時間 /// </summary> public DateTime CreatedTime { get; set; } = DateTime.Now; /// <summary> /// 建立人 /// </summary> public Guid CreatedBy { get; set; } /// <summary> /// 修改時間 /// </summary> public DateTime ModifiedTime { get; set; } = DateTime.Now; /// <summary> /// 修改人 /// </summary> public Guid ModifiedBy { get; set; } /// <summary> /// 建立人名稱 /// </summary> [Ignore] public string Creator { get; set; } /// <summary> /// 修改人 /// </summary> [Ignore] public string Modifier { get; set; } }
可以看到 EntityBase
已經包含了CreatedTime
,CreatedBy
,ModifiedTime
,ModifiedBy
這四個實體屬性,通過實體繼承了EntityBase
,那麼該實體也包含了這個屬性,同時NetModular
內部已經實現了在新增,修改時,自動設定對應的建立人和修改人編號,所以你不需要你去考慮這些事情了。
另外還有包含軟刪除功能的EntityWithSoftDelete
以及包含上面兩個實體功能的EntityBaseWithSoftDelete
兩個實體,這些都已經封裝好了,可以直接用。
Node: 上面的三個實體基類都會繼承自
Entity
,該實體包含了一個主鍵Id
,主鍵型別支援Guid(預設)、Int、Long三種類型。
可以通過Table特性,設定實體對應的表名稱
以下是一個產品的實體示例:
[Table("Product")]
public partial class ProductEntity : EntityBase
{
/// <summary>
/// 標題
/// </summary>
public string Title { get; set; }
/// <summary>
/// 價格
/// </summary>
public decimal Price { get; set; }
/// <summary>
/// 庫存
/// </summary>
public int Store { get; set; }
/// <summary>
/// 狀態
/// </summary>
public ProductStatus Status { get; set; }
}
實體擴充套件類:
Node: 實體擴充套件類中的屬性,必須新增Ignore
特性,否則屬性會被當成表的列處理。
public partial class ProductEntity
{
/// <summary>
/// sku列表
/// </summary>
[Ignore]
public List<Guid> Skus { get; set; }
}
3、新增倉儲介面
倉儲介面必須繼承IRepository<>
介面
/// <summary>
/// 產品倉儲介面
/// </summary>
public interface IProductRepository : IRepository<ProductEntity>
{
/// <summary>
/// 查詢
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
Task<IList<ProductEntity>> Query(ProductQueryModel model);
}
4、新增查詢模型
查詢模型包含查詢條件,需要繼承QueryModel
類,該類包含了分頁相關的資訊
public class ProductQueryModel : QueryModel
{
public string Title { get; set; }
}
5、新增倉儲實現
倉儲實現需要繼承抽象倉儲RepositoryAbstract<>
,不同資料庫的倉儲實現,需要放到不同的目錄中。因為不同的資料庫難免會有一些查詢,所以我們採用先實現一種資料庫的實現,然後其它資料實現直接繼承它,對於有查詢的方法,採用覆寫的方式實現。
public class ProductRepository : RepositoryAbstract<ProductEntity>, IProductRepository
{
public ProductRepository(IDbContext context) : base(context)
{
}
public async Task<IList<ProductEntity>> Query(ProductQueryModel model)
{
//分頁
var paging = model.Paging();
var query = Db.Find();
query.WhereIf(model.Title.NotNull(), m => m.Title.Contains(model.Title));
//設定預設排序
if (!paging.OrderBy.Any())
{
query.OrderByDescending(m => m.Id);
}
var list = await query.PaginationAsync(paging);
model.TotalCount = paging.TotalCount;
return list;
}
}
上面的例子是一個最簡單的分頁查詢,到此資料訪問的程式碼就寫完了,剩下的就是在服務層呼叫就行了。
其它用法說明
1、CRUD
基礎的CRUD在RepositoryAbstract
中已經實現了,所以可以直接在服務中呼叫
新增
_repository.AddAsync(entity);
批量新增
_repository.AddAsync(entities);
刪除
_repository.DeleteAsync(id);
軟刪除
_repository.SoftDeleteAsync(id);
修改
_repository.UpdateAsync(entity);
獲取
_repository.GetAsync(id);
獲取所有
_repository.GetAllAsync()
是否存在
_repository.ExistsAsync(m => m.Title.Contains("test"))
批量修改
/// <summary>
/// 批量修改狀態
/// </summary>
/// <param name="ids"></param>
/// <param name="status"></param>
/// <returns></returns>
public Task<bool> UpdateStatus(List<Guid> ids, ProductStatus status)
{
return Db.Find(m => ids.Contains(m.Id)).UpdateAsync(m => new ProductEntity { Status = status });
}
批量刪除
/// <summary>
/// 批量刪除
/// </summary>
/// <param name="title"></param>
/// <returns></returns>
public Task<bool> Delete(string title)
{
return Db.Find(m => m.Title.Contains(title)).DeleteAsync();
}
表連線查詢
public async Task<IList<ProductEntity>> Query(ProductQueryModel model)
{
//分頁
var paging = model.Paging();
var query = Db.Find();
query.WhereIf(model.Title.NotNull(), m => m.Title.Contains(model.Title));
//設定預設排序
if (!paging.OrderBy.Any())
{
query.OrderByDescending(m => m.Id);
}
var list = await query.LeftJoin<AccountEntity>((x, y) => x.CreatedBy == y.Id)
.Select((x, y) => new { x, Creator = y.Name })
.PaginationAsync(paging);
model.TotalCount = paging.TotalCount;
return list;
}
分組查詢
Db.Find().GroupBy(m => new { m.Status }).Select(m => new { m.Key.Status, Count = m.Count() });
工作單元
工作單元在服務中注入使用
private readonly IUnitOfWork _uow;
public ArticleService(IUnitOfWork<MalDbContext> uow)
{
_uow = uow;
}
然後通過BeginTransaction
方法開啟事務,Commit
方法提交事務,Rollback
方法回滾事務
_uow.BeginTransaction();
var result = await _accountRepository.AddAsync(account);
if (result)
{
if (model.Roles != null && model.Roles.Any())
{
var accountRoleList = model.Roles.Select(m => new AccountRoleEntity { AccountId = account.Id, RoleId = m }).ToList();
if (await _accountRoleRepository.AddAsync(accountRoleList))
{
_uow.Commit();
return ResultModel.Success();
}
}
else
{
_uow.Commit();
return ResultModel.Success();
}
}
好了,資料庫訪問的用法大致就是這