ABP框架用Dapper實現通過SQL訪問數據庫


ABP的框架(2) - 訪問數據庫

 

為了防止不提供原網址的轉載,特在這里加上原文鏈接:
http://www.cnblogs.com/skabyy/p/7517397.html

本篇我們實現數據庫的訪問。我們將實現兩種數據庫訪問方法來訪問一個SQLite數據庫——使用NHibernate實現的ORM映射訪問和使用Dapper實現的SQL語句訪問。然后完成前一篇未完成的CreateTweetGetTweets接口。

在開始之前,先做一些准備工作,新建Domain層的Module:

public class MyTweetDomainModule : AbpModule { public override void Initialize() { IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly()); } }

同時MyTweetApplicationModule添加對MyTweetDomainModule的依賴:

[DependsOn(typeof(MyTweetDomainModule))] public class MyTweetApplicationModule : AbpModule

安裝NuGet包Abp.NHibernateMyTweet.DomainMyTweet.Infrastructure

下面我們將完成這些步驟來實現數據庫的訪問:

  1. 配置數據庫連接
  2. 新建tweet表以及相應的Model類型
  3. 實現訪問數據的Repository
  4. Dapper實現通過SQL訪問數據庫

使用Fluent NHibernate配置數據庫連接

我們這里使用的數據庫是SQLite數據庫,其他數據庫的配置也是類似的。我們將連接到App_Data文件夾下的一個SQLite數據庫。新建LocalDbSessionProvider類並在構造函數處配置數據庫連接。由於LocalDbSessionProvider實現了接口ISingletonDependency,模塊初始化時LocalDbSessionProvider會以單例的形式注冊到IoC容器。

public class LocalDbSessionProvider : ISessionProvider, ISingletonDependency, IDisposable { protected FluentConfiguration FluentConfiguration { get; private set; } private ISessionFactory _sessionFactory; public LocalDbSessionProvider() { FluentConfiguration = Fluently.Configure(); // 數據庫連接串 var connString = "data source=|DataDirectory|MySQLite.db;"; FluentConfiguration // 配置連接串 .Database(SQLiteConfiguration.Standard.ConnectionString(connString)) // 配置ORM .Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly())); // 生成session factory _sessionFactory = FluentConfiguration.BuildSessionFactory(); } private ISession _session; public ISession Session { get { if (_session != null) { // 每次訪問都flush上一個session。這里有效率和多線程問題,暫且這樣用,后面會改。 _session.Flush(); _session.Dispose(); } _session = _sessionFactory.OpenSession(); return _session; } } public void Dispose() { _sessionFactory.Dispose(); } }

這里每次用到session都只是簡單地把上一次的session flush了,然后打開新的session。這會有效率和多線程沖突的問題。這里只是單純為了展示實現數據庫鏈接的方法而先用的簡單實現。后面做工作單元(UoW)時會解決這個問題。

為了NHibernate能創建SQLite的連接,還需要安裝System.Data.SQLite.CoreMyTweet.Web(其他數據庫的話要安裝其他相應的包)。

新建tweet表以及相應的Model類型

我們用tweet表保存tweet數據。tweet數據表接口以及對應Model屬性如下:

數據庫字段 Model屬性 類型 描述
pk_id PkId string 主鍵
content Content string 內容
create_time CreateTime string 創建時間

使用SQLite工具新建MySQLite.db文件,並新建表tweet
然后將MySQLite.db文件拷貝到App_Data文件夾下。

CREATE TABLE `tweet` ( `pk_id` TEXT, `content` TEXT, `create_time` TEXT NOT NULL, PRIMARY KEY(`pk_id`) );

接下來新建Model類Tweet以及映射TweetMapperTweet繼承Entity<string>,其中的string表示Tweet的主鍵Idstring類型的。TweetMapper繼承ClassMap<Tweet>,上面LocalDbSessionProvider構造函數執行到.Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly()))這個方法時,會用反射的方式搜索程序集中ClassMap<T>的子類,建立Model和數據庫表的映射(Tweettweet表的映射)。

public class Tweet : Entity<string> // 主鍵為string類型 { public string Content { get; set; } public DateTime CreateTime { get; set; } } public class TweetMapper : ClassMap<Tweet> { public TweetMapper() { // 禁用惰性加載 Not.LazyLoad(); // 映射到表tweet Table("tweet"); // 主鍵映射 Id(x => x.Id).Column("pk_id"); // 字段映射 Map(x => x.Content).Column("content"); Map(x => x.CreateTime).Column("create_time"); } }

實現Repository與增查接口

Repository即是DDD中的倉儲,它封裝了數據對象的增刪改查操作。ABP的NhRepositoryBase已經實現了常用的增刪改查功能,因此這里只需要繼承一下就行了。

public interface ITweetRepository : IRepository<Tweet, string> { } public class TweetRepository : NhRepositoryBase<Tweet, string>, ITweetRepository { public TweetRepository() : base(IocManager.Instance.Resolve<LocalDbSessionProvider>()) { } }

最后,修改MyTweetAppService,實現CreateTweet接口和GetTweets接口。

public class CreateTweetInput { public string Content { get; set; } } public class MyTweetAppService : ApplicationService, IMyTweetAppService { public ITweetRepository TweetRepository { get; set; } public object GetTweets(string msg) { return TweetRepository.GetAll().OrderByDescending(x => x.CreateTime).ToList(); } public object CreateTweet(CreateTweetInput input) { var tweet = new Tweet { Id = Guid.NewGuid().ToString("N"), Content = input.Content, CreateTime = DateTime.Now }; var o = TweetRepository.Insert(tweet); return o; } }

大功告成!測試一下。用Postman調用CreateTweet接口插入一條tweet:

然后調用GetTweets查詢:

ABP的依賴注入

可能有同學會疑惑,在MyTweetAppService中只聲明了ITweetRepository類型的屬性TweetRepository,但是並沒有進行賦值,那么這個屬性的對象實例是哪里來的呢?這就涉及到ABP框架的依賴注入策略了。

ABP基於Castle Windsor框架實現自己的依賴注入功能。依賴注入最基本的功能無非是注冊(Register)和解析(Resolve)兩個,注冊功能將對象注冊到IoC容器,解析功能根據類名或接口名獲從IoC容器獲取已注冊的對象。我們可以直接通過IocManager獲得Castle Windsor的IoC容器,直接進行注冊和解析操作。

// 以單例模式注冊類型T IocManager.Register<T>(Abp.Dependency.DependencyLifeStyle.Singleton); // 以臨時對象模式注冊類型T,解析的時候會生成T的一個新對象 IocManager.Register<T>(Abp.Dependency.DependencyLifeStyle.Transient); // 從IoC容器解析已注冊的類型T的對象 var obj = IocManager.Resolve<T>();

還有一些其他方法可以做注冊和解析,具體可以參照ABP的文檔。不過一般都不需要使用這些方法。ABP框架有一套依賴注入的規則,通過編寫應用程序時遵循最佳實踐和一些約定,使得依賴注入對於開發者幾乎是透明的。

ABP的注冊

基本上每個模塊的初始化方法都會有這么一行代碼:

IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());

模塊初始化時,ABP會搜索這個模塊所在的程序集,自動注冊滿足常規注冊條件與實現幫助接口的類。

常規注冊

ABP自動注冊所有Repositories, Domain Services, Application Services, MVC 控制器和Web API控制器。ABP通過判斷是否實現了相應接口來判斷是不是上述幾類。例如下面的MyAppService

public interface IMyAppService : IApplicationService { } public class MyAppService : IMyAppService { }

由於它實現了接口IApplicationService,ABP會自動注冊,我們就可以通過IMyAppService解析出一個MyAppService對象。

通過常規注冊的類的生命期都是transient(臨時的),每次解析時都會生成一個新的臨時對象。

幫助接口

ABP另外提供了ITransientDependencyISingletonDependency兩個接口。這兩個接口前面也有用到過了。實現了ITransientDependency的類會被注冊為transient。而實現了ISingletonDependency的類則被注冊為單例。

ABP的解析

除了手工解析外,還可以通過構造函數和公共屬性注入來獲取類的依賴。這也是最常用的方法。例如:

public class MyAppService : IMyAppService { public ILogger Logger { get; set; } private IMyRepository _repo; public MyAppService(IMyRepository repo) { _repo = repo; } }

ILogger從公共屬性注入,IMyRepository從構造函數注入。注入過程對開發者是透明的,開發者不需要去寫注入的代碼。

QueryService - 使用SQL語句查詢數據

實際開發中,經常需要直接使用SQL進行數據訪問。查詢邏輯比較復雜時直接使用SQL可以避免復雜的Mapper。通常復雜的Mapper會導致低效率的查詢甚至會觸發NHibernate一些奇怪的bug。實際上,在開發中,對於單純的讀取數據的功能(即使查詢邏輯不復雜),我們建議直接使用SQL查詢實現。直接使用SQL查詢在調試時更為方便——直接拷貝SQL語句到SQL客戶端執行即可檢驗該語句是否正確。

下面簡要介紹一下使用Dapper來實現數據庫查詢功能。封裝了sql查詢操作的類我們稱為QueryService。

首先,安裝dapper包到MyTweet.Infrastructure。在MyTweet.Infrastructure實現QueryService的基類BaseQueryService

public class BaseQueryService : ITransientDependency { private ISessionProvider _sessionProvider; protected BaseQueryService(ISessionProvider sessionProvider) { _sessionProvider = sessionProvider; } public IEnumerable<T> Query<T>(string sql, object param = null) { var conn = _sessionProvider.Session.Connection; return conn.Query<T>(sql, param); } }

Dapper給System.Data.IDbConnection接口擴展了Query<T>方法,該方法執行SQL查詢並將查詢結構映射為IEnumerable<T>類型的對象。為了使用這個擴展方法,還需在文件開頭加個using語句。

using Dapper;

QueryService並不在ABP依賴注入的常規注冊規則里,所以讓BaseQueryService實現了ITransientDependency,這樣它的子類都會自動被注冊到IoC容器。

接下來在MyTweet.Domain新建類TweetQueryService,它負責實現具體的SQL查詢。方法SearchTweets實現了查詢包含關鍵詞keyword的所有tweet。

public interface ITweetQueryService { IList<Tweet> SearchTweets(string keyword); } public class TweetQueryService : BaseQueryService, ITweetQueryService { public TweetQueryService() : base(IocManager.Instance.Resolve<LocalDbSessionProvider>()) { } public IList<Tweet> SearchTweets(string keyword) { var sql = @"select pk_id Id, content Content, create_time CreateTime from tweet where content like '%' || @Keyword || '%'"; return Query<Tweet>(sql, new { Keyword = keyword ?? "" }).ToList(); } }

最后在MyTweetAppService實現查詢tweet數據的接口GetTweetsFromQS

public ITweetQueryService TweetQueryService { get; set; } public object GetTweetsFromQS(string keyword) { return TweetQueryService.SearchTweets(keyword); } 

測試一下:

結束

本文介紹了通過NHibernate以及Dapper進行數據庫訪問的方法,簡單說明了ABP依賴注入策略。現在數據庫連接部分的代碼只是單純為了演示的簡單實現,沒有做合理的數據庫Session管理,會有效率和多線程沖突的問題。后面會加上工作單元(Unit of Work)來解決這些問題。

最后,放上代碼鏈接:https://github.com/sKabYY/MyTweet-AbpDemo


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM