ABP框架實戰系列(一)-持久層介紹篇
ABP框架實戰系列(一)-持久層介紹篇
資料持久化
在開始持久層的介紹之前,我們先引入一個基礎概念:持久化
狹義的理解: “持久化”僅僅指把域物件永久儲存到資料庫中;廣義的理解,“持久化”包括和資料庫相關的各種操作(持久化就是將有用的資料以某種技術儲存起來,將來可以再次取出來應用,資料庫技術,將記憶體資料一檔案的形式儲存在永久介質中(磁碟等)都是持久化的意思。
但是僅僅的持久化會使專案不可維護或者後期維護不利,簡單的儲存功能已經完全滿足不了現在軟體開發的模組性、可維護性、
擴充套件性、分層性原則,所以就需要一種技術框架,將業務層和資料庫之間儲存的操作做到可維護性、擴充套件性、分層性,於是就出現“持久層”的概念
資料持久層
持久層:設計目標是為整個專案提供一個銜接高低層、統一、安全和併發的資料持久機制,完成對各種資料庫進行持久化的程式設計工作,併為系統業務邏輯提供服務。資料持久層提供了資料訪問方法,能夠使程式設計師避免手動編寫程式訪問資料持久層,使其專注於業務邏輯的開發,並且能夠在不同的專案中重用對映框架,大大簡化了資料增刪改查等功能的開發過程,同時又不喪失多層結構的天然優勢,具備可伸縮性和可擴充套件性
ORM
ORM資料持久層的一種子實現,它通過將對映的機制,把資料庫中的一條記錄當做程式的一個類處理,這樣在CURD的處理上,真正實現了面向物件開發,也將軟體的後期維護週期大大縮短
市面上 .Net Core ORM 工具多如牛毛,就列舉如下四種常見框架,使用方式大同小異,此次鏈上一篇很不錯的EF Core 的入門教程:
EntityFrameWork Core(推薦關注該公眾號,乾貨滿滿)
[FreeSql]
[Nhibernate-core]
[MyBatis]
ORM優缺點
- 優點
- ORM最大的優勢,隱藏了資料訪問細節,“封閉”的通用資料庫互動,ORM的核心。他使得我們的通用資料庫互動變得簡單易行,並且完全不用考慮該死的SQL語句。快速開發,由此而來。
- ORM使我們構造固化資料結構變得簡單易行。在ORM年表的史前時代,我們需要將我們的物件模型轉化為一條一條的SQL語句,通過直連或是DB helper在關係資料庫構造我們的資料庫體系。而現在,基本上所有的ORM框架都提供了通過物件模型構造關係資料庫結構的功能。
- 缺點
- 對於複雜查詢,ORM仍然力不從心。雖然可以實現,但是不值的。檢視可以解決大部分calculated column,case ,group,having,order by, exists
ABP 框架的 Entity FrameWork Core
DbContext
EF核心需要定義來自DbContext類。在ABP,我們應該從abpdbcontext獲得,如下所示
public class MyDbContext : AbpDbContext
{
public DbSet<Product> Products { get; set; }
public MyDbContext(DbContextOptions<MyDbContext> options)
: base(options)
{
}
}
建構函式必須得到 DbContextOptions
Configuration(配置資料庫連線字串)
ABP Module Zero 中,將路徑配置在appsettings.json檔案中 配置格式基本如下
"ConnectionStrings": {
"Default": "Data Source = 10.28.253.2;Initial Catalog = EventCloudDb;User Id = sa;Password = Ecsgui123;"
}
而對配置檔案的載入、以及DbContext的註冊是通過如下程式碼完成的(ABP 預設生成)
程式碼中,EFModule 繼承自 ABPModule,表名EF 這個模組,是一個可插拔的模組,該模組下有三個初始化函式PreInitialize(預初始化)、Initialize(初始化)、PostInitialize(StartUp 完成之後執行)。
其中,PreInitilize方法中,包含ABP 框架下對模組中的AbpEFCore模組的註冊。
[DependsOn(
typeof(UniversalCoreModule),
typeof(AbpZeroCoreEntityFrameworkCoreModule))]
public class UniversalEntityFrameworkModule : AbpModule
{
/* Used it tests to skip dbcontext registration, in order to use in-memory database of EF Core */
public bool SkipDbContextRegistration { get; set; }
public bool SkipDbSeed { get; set; }
public override void PreInitialize()
{
if (!SkipDbContextRegistration)
{
Configuration.Modules.AbpEfCore().AddDbContext<UniversalDbContext>(options =>
{
if (options.ExistingConnection != null)
{
UniversalDbContextConfigurer.Configure(options.DbContextOptions, options.ExistingConnection);
}
else
{
UniversalDbContextConfigurer.Configure(options.DbContextOptions, options.ConnectionString);
}
});
}
}
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(typeof(UniversalEntityFrameworkModule).GetAssembly());
}
public override void PostInitialize()
{
if (!SkipDbSeed)
{
SeedHelper.SeedHostDb(IocManager);
}
}
}
在熟悉了上述DbContext載入配置的過程後,我們就相對應的會來到建立DbContext部分,這部分內容,ABP 模板在UniversalDbContextFactory中,實現了物件DbContext的建立
1、建立DbContextOptionBuilder建立
2、載入配置檔案資訊到記憶體
3、初始化Builder 資訊
4、new 一個新的 DbContext物件
public class Git_ECSGUI_UniversalDbContextFactory : IDesignTimeDbContextFactory<Git_ECSGUI_UniversalDbContext>
{
public Git_ECSGUI_UniversalDbContext CreateDbContext(string[] args)
{
var builder = new DbContextOptionsBuilder<Git_ECSGUI_UniversalDbContext>();
var configuration = AppConfigurations.Get(WebContentDirectoryFinder.CalculateContentRootFolder());
Git_ECSGUI_UniversalDbContextConfigurer.Configure(builder, configuration.GetConnectionString(Git_ECSGUI_UniversalConsts.ConnectionStringName));
return new Git_ECSGUI_UniversalDbContext(builder.Options);
}
}
ABP 的倉儲
講完EF Core中的DbContext在ABP中的建立過程,我們將視線轉到Repositories(倉庫)中過來,他是領域層和持久之間的橋樑,儲存庫用於從高層抽象資料訪問。
在ABP 框架中領域和持久化層之間的媒介,使用一種類似集合的介面來訪問實體,每個實體(或者聚合根)使用給一個分離的倉庫
預設倉儲
在ABP裡,一個倉儲類實現IRepository<TEntity,TPrimaryKey>介面。ABP預設地為每個實體型別自動建立一個預設倉儲。你可以直接注入IRepository
public class PersonService : IPersonService
{
private readonly IRepository<Person> _personRepository;
public PersonService(IRepository<Person> personRepository)
{
_personRepository = personRepository;
}
public void CreatePerson(CreatePersonInput input)
{
person = new Person { Name = input.Name, EmailAddress = input.EmailAddress };
_personRepository.Insert(person);
}
}
自定義倉儲
只有當實體需要建立一個自定義的倉儲方法時,才需要你建立一個倉儲類。
public abstract class Git_ECSGUI_UniversalRepositoryBase<TEntity, TPrimaryKey> : EfCoreRepositoryBase<Git_ECSGUI_UniversalDbContext, TEntity, TPrimaryKey>
where TEntity : class, IEntity<TPrimaryKey>
{
protected Git_ECSGUI_UniversalRepositoryBase(IDbContextProvider<Git_ECSGUI_UniversalDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
// Add your common methods for all repositories
}
/// <summary>
/// Base class for custom repositories of the application.
/// This is a shortcut of <see cref="Git_ECSGUI_UniversalRepositoryBase{TEntity,TPrimaryKey}"/> for <see cref="int"/> primary key.
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
public abstract class Git_ECSGUI_UniversalRepositoryBase<TEntity> : Git_ECSGUI_UniversalRepositoryBase<TEntity, int>, IRepository<TEntity>
where TEntity : class, IEntity<int>
{
protected Git_ECSGUI_UniversalRepositoryBase(IDbContextProvider<Git_ECSGUI_UniversalDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
// Do not add any method here, add to the class above (since this inherits it)!!!
}
要實現自定義儲存庫,只需從上面建立的應用程式特定的基礎儲存庫類中獲得。
public interface IUniversalTaskRepository : IRepository<Task>
{
List<Task> GetTaskByAssignedPersonId(long taskId);
}
public UniversalTaskRepository:Git_ECSGUI_UniversalRepositoryBase, IUniversalTaskRepository
{
public UniversalTaskRepository(IDbContextProvider<Git_ECSGUI_UniversalDbContext> dbContextProvider) : base(dbContextProvider)
{
}
/// <summary>
/// 獲取某個使用者分配了哪些任務
/// </summary>
/// <param name="personId">使用者Id</param>
/// <returns>任務列表</returns>
public List<Task> GetUniversalTaskId(long taskId)
{
var query = GetAll();
if (taskId > 0)
{
query = query.Where(t => t.taskid == taskId);
}
return query.ToList();
}
}
倉儲的注意事項
- 倉儲方法中,ABP自動進行資料庫連線的開啟和關閉。
- 倉儲方法被呼叫時,資料庫連線自動開啟且啟動事務。
- 當倉儲方法呼叫另外一個倉儲的方法,它們實際上共享的是同一個資料庫連線和事務。
- 倉儲物件都是暫時性的,因為IRepository介面預設繼承自ITransientDependency介面。所以,倉儲物件只有在需要注入的時候,才會由Ioc容器自動建立新例項。
- 預設的泛型倉儲能滿足我們大部分的需求。只有在不滿足的情況下,才建立定製化的倉儲。