一、關於NoSQL的項目需求
這些年在做AgileEAS.NET SOA 中間件平台的推廣、技術咨詢服務過程之中,特別是針對我們最熟悉的醫療行業應用之中,針對大數據分析,大並發性能的需求,我們也在慢慢的引用NoSQL技術來滿足數據分析與性能等多方面的需要,也進一步完善我們的SOA基石架構風格:
在早些年,對NoSQL不是很了解這前,后端數據存儲都是存儲的單一的關系數據庫之上,但是在很多時間,這並不是最優的,比如在醫療用戶之中針對一個病人的相關數據展示,及相關性分析,關於數據庫就不是最優的,另外一個,電子病歷系統的之中的結構化/半結構化病歷文檔的存儲、檢索,以及更高級的應用,結構化病歷數據挖掘,之前使用關系數據庫存儲或者使用文件存儲,很難發揮病歷數據的科研和統計、分析需求。
在目前我們的醫療信息化應用之中,我們針對這兩部分數據都引入了NoSQL存儲,針對住院患者的領域相關性數據==》即病人聚合根對象相關數據,我們即在關系數據庫以多表存儲病人數據以及病人相關的醫囑、費用、檢驗、檢查、護理等相關信息,同時我們也在NoSQL數據庫存儲患者的聚合根對象:
在NoSQL數據庫之中的存儲:
另外在電子病歷應用之中,病歷文檔也是直接存入NoSQL之中。
在接觸巨杉數據庫之前,我們一直使用MongoDB這款NoSQL產品,這是一款廣為人知的NoSQL產品,使用者眾多,C#的驅動也非常完善,案例也比比皆時。
三、關於巨杉(sequoiadb)數據庫
巨杉數據庫是國人開發的一款企業級NoSQL數據庫,目前已開源,官網http://www.sequoiadb.com/。
初次了解到巨杉(sequoiadb)數據還是源於一個客戶,因為我們項目一直使用MongoDB,客戶就向我們提到巨杉(sequoiadb)數據庫,說國內有人開發了這么一個NoSQL數據庫,並且在平安銀行有過成功應用,並且因為是國人開發,所以應該相比較MongoDB,應該能得到官方的支持,客戶也和巨杉(sequoiadb)官方的人有過接觸,官方也答應可以做一些支持。
根據網上所公開的一些信息,巨杉(sequoiadb)數據庫和MongoDB非常的接近,都是文檔型數據庫,同樣的設計思路,集合和文檔,同樣的文檔格式,Json/Bson。
根據最近一段時間的了解和完善C#驅動的過程來說,相對MongoDB,巨杉(sequoiadb)提供了更加方便的圖形化部署和簡單的Web管理界面:
以下是SequoiaDB與MongoDB及其他NoSQL數據的功能對比:
比較特別是的SequoiaDB支持事務和SQL語法,當然了,這兩點在目前情況下我們都使用使用過。
四、關於SequoiaDB的C#驅動
SequoiaDB官方提供C、C++、JAVA、C#、php、Python驅動以及REST架構風格的接口,據官方的說法是Java的驅動很成熟,但是C#的驅動很簡單,只能支持最基本的Bson格式的接口,如下代碼:
// Insert BsonDocument insertor = new BsonDocument(); insertor.Add("Last Name", "Lin"); insertor.Add("First Name", "Hetiu"); insertor.Add("Address", "SYSU"); BsonDocument sInsertor = new BsonDocument(); sInsertor.Add("Phone", "10086"); sInsertor.Add("EMail", "hetiu@yahoo.com.cn"); insertor.Add("Contact", sInsertor); ObjectId insertID = (ObjectId)coll.Insert(insertor); Assert.IsNotNull(insertID); // Update DBQuery query = new DBQuery(); BsonDocument updater = new BsonDocument(); BsonDocument matcher = new BsonDocument(); BsonDocument modifier = new BsonDocument(); updater.Add("Age", 25); modifier.Add("$set", updater); matcher.Add("First Name", "Hetiu"); query.Matcher = matcher; query.Modifier = modifier; coll.Update(query); // Query DBCursor cursor = coll.Query(query); Assert.IsNotNull(cursor); BsonDocument bson = cursor.Next(); Assert.IsNotNull(bson); Assert.IsTrue(bson["First Name"].AsString.Equals("Hetiu")); Assert.IsTrue(bson["Age"].AsInt32.Equals(25)); // Delete BsonDocument drop = new BsonDocument(); drop.Add("Last Name", "Lin"); coll.Delete(drop); query.Matcher = drop; cursor = coll.Query(query); Assert.IsNotNull(cursor); bson = cursor.Next(); Assert.IsNull(bson);
集合查詢:
for (int i = 0; i < 10; ++i) { string date = DateTime.Now.ToString(); BsonDocument insertor = new BsonDocument(); insertor.Add("operation", "Query"); insertor.Add("date", date); coll.Insert(insertor); } BsonDocument matcher = new BsonDocument(); DBQuery query = new DBQuery(); matcher.Add("operation", "Query"); query.Matcher = matcher; query.ReturnRowsCount = 5; query.SkipRowsCount = 5; DBCursor cursor = coll.Query(query); Assert.IsNotNull(cursor); int count = 0; while (cursor.Next() != null) { ++count; BsonDocument bson = cursor.Current(); Assert.IsNotNull(bson); } Assert.IsTrue(count == 5);
官方的代碼有點簡單,這不符合我們寫代碼的風格,目前業務系統大量的使用對象操作和Linq處理,原始的Bson接口,這個不科學。
五、完善改造SequoiaDB的C#驅動
即然官方的驅動太簡單,不支持對象處理,也不支持Linq,很不科學,那么應該怎么辦呢,其實第一個觀點當然是放棄,我們原本使用MongoDB跑的好好的,為什么要給自己找事呢,但是出於項目運維的觀點,以及支持國人產品的想法,最終決定自己完善和寫一個。
那么如何來寫呢,當然是他山之石,可以攻玉,因為之前做MongoDB開發,原始的驅動配置我們的ORM跑起來也有一些問題,最早我們使用的非MongoDB的官方驅動,而是第三方驅動samus,不支持Decimal類型,但是我們項目之中有大量的Decimal類型,那么辦呢,修改驅動,后來我們又換成了MongoDB的官方驅動,因為XmlIgnore標簽和Id映射的問題也認真的讀過MongoDB的官方驅動,對MongoDB的C#驅動比較熟悉。
所以完善SequoiaDB的C#的思路就變成了結合SequoiaDB的原始驅動和MongoDB的官方驅動,提供一個類似於MongoDB驅動的操作風格的驅動,在SequoiaDB驅動的基礎上提供了,直接操作C#對象的方案和支持Linq進行查詢、修改、刪除的功能。
經本人完善修改之后的驅動的操作風格如下:
Sequoiadb sdb = new Sequoiadb("192.168.23.57:50000"); sdb.Connect("", ""); //求集合空間。 var cs = sdb.GetCollecitonSpace("dbo"); //求集合。 var coll = cs.GetCollection<HFareDetail>(); //執行數據插入。 List<HFareDetail> vList =null; using (AgileHIS.Entities.DbEntities db = new AgileHIS.Entities.DbEntities()) { vList = db.HFareDetails.ToList(); //插入。 foreach (var item in vList) { coll.Insert(item); } System.Console.WriteLine(string.Format("insert {0} records", vList.Count)); System.Console.ReadLine(); } //按條件修改某一條數據的幾個屬性值。 var v1 = vList.FirstOrDefault(); v1.Name = string.Empty; v1.Cash = decimal.Zero; coll.Update(v1, p => p.ID == v1.ID); //按條件指量修改,指定某幾個必,其他屬性全部置空。 coll.Update(p => new HFareDetail { Cash = decimal.Zero, Name = string.Empty, Price = decimal.Zero }, p => p.ChargeTime >DateTime.Now.AddDays(-1)); //依據條件刪除 coll.Delete(p => p.ChargeTime > DateTime.Now.AddDays(-1)); //求Count int count = coll.AsQueryable<HFareDetail>() .Where(p => p.SourceID==0) .Count(); //Linq查詢Take\Skip。 var vList2 = coll.AsQueryable<HFareDetail>() .Where(p => p.CreateTime > DateTime.Now.Date.AddMonths(-12)) .Skip(10).Take(1000) .ToList(); System.Console.WriteLine(string.Format("query {0} records", vList.Count)); //Linq查詢過。 var vFare = coll.AsQueryable<HFareDetail>() .Where(p => p.CreateTime > DateTime.Now.Date.AddMonths(-12)) .FirstOrDefault(); System.Console.WriteLine(vFare); //Linq\聚合運算,目前因為測試驅動報錯,暫未實現 var sum = coll.AsQueryable<HFareDetail>() .Where(p => p.CreateTime > DateTime.Now.Date.AddMonths(-12)) .Sum(p => p.Cash); System.Console.ReadLine();
看看,代碼是不是很清爽,很方便了呢,沒有了bson,只有對象,Linq。
六、SequoiaDB、MongoDB與AgileEAS.NET SOA整合
AgileEAS.NET SOA之前只支持MongoDB,最近要支持SequoiaDB,我們就得考慮對原有代碼的兼容,或者說,更希望自己的醫療系統能夠在業務上同時支持MongoDB和SequoiaDB,達到使用環境之中不管是選擇MongoDB還是選擇SequoiaDB都是同樣的代碼,為此,我們在AgileEAS.NET SOA中間件之中定義了一個IStructDbProvider接口:
using System; using System.Collections.Generic; using System.Linq.Expressions; namespace EAS.Data { /// <summary> /// 結構化數據庫提供者接口定義。 /// </summary> /// <remarks> /// 為AgileEAS.NET SOA 中間件NoSQL數據訪問提供標准接口定義。 /// </remarks> public interface IStructDbProvider { /// <summary> /// 打開連接。 /// </summary> void Connect(); /// <summary> /// 關閉連接。 /// </summary> void Close(); /// <summary> /// 連接是否打開。 /// </summary> bool IsOpen { get; } /// <summary> /// 對象插入。 /// </summary> /// <typeparam name="T">對象類型。</typeparam> /// <param name="item">對象實例。</param> void Insert<T>(T item) where T : class; /// <summary> /// 對象批量插入。 /// </summary> /// <typeparam name="T">對象類型。</typeparam> /// <param name="items">對象實例。</param> void InsertBatch<T>(System.Collections.Generic.IEnumerable<T> items) where T : class; /// <summary> /// 根據條件執行更新操作。 /// </summary> /// <typeparam name="T">對象類型。</typeparam> /// <param name="updater">更新表達式。</param> /// <param name="func">查詢條件。</param> void Update<T>(Expression<Func<T, T>> updater, Expression<Func<T, bool>> func) where T : class; /// <summary> /// 根據條件執行更新操作。 /// </summary> /// <typeparam name="T">對象類型。</typeparam> /// <param name="item">更新對象。</param> /// <param name="func">查詢條件。</param> void Update<T>(T item, System.Linq.Expressions.Expression<Func<T, bool>> func) where T : class; /// <summary> /// 根據條件刪除對象。 /// </summary> /// <typeparam name="T">對象類型。</typeparam> /// <param name="func">條件表達式。</param> void Delete<T>(Expression<Func<T, bool>> func) where T : class; /// <summary> /// 求出Linq查詢表達式。 /// </summary> /// <typeparam name="T">對象類型。</typeparam> /// <returns>對象表達式包裝。</returns> IQueryableWarp<T> Linq<T>() where T : class; /// <summary> /// 根據條件查詢數制。 /// </summary> /// <typeparam name="T">對象類型。</typeparam> /// <param name="where">條件。</param> /// <param name="skip">跳過記錄數。</param> /// <param name="take">取記錄數。</param> /// <returns>查詢結構。</returns> List<T> List<T>(Expression<Func<T, bool>> where, int skip, int take) where T : class; /// <summary> /// 根據條件求單條記錄。 /// </summary> /// <typeparam name="T">對象類型。</typeparam> /// <param name="where">條件。</param> /// <returns>對象實例。</returns> T Single<T>(Expression<Func<T, bool>> where) where T : class; } }
IStructDbProvider字面意思即為結構化數據訪問提供者接口,本接口定義在EAS.MicroKernel.dll程序集之中,AgileEAS.NET SOA中間件同時提供了針對SequoiaDB和MongoDB數據庫的IStructDbProvider實現,EAS.Data.MongoDbProvider和EAS.Data.SequoiaDbProvider,這兩個實現類定義在EAS.Data.NoSQL.dll程序集之中。
因為統計使用了IStructDbProvider接口,我們針對SequoiaDB和MongoDB的操作處理就統計成了如下代碼:
var vContainer = EAS.Context.ContextHelper.GetContext().Container; var dbProvider = vContainer.GetComponentInstance("StructDbProvider") as IStructDbProvider; //執行數據插入。 List<HFareDetail> vList = null; using (AgileHIS.Entities.DbEntities db = new AgileHIS.Entities.DbEntities()) { vList = db.HFareDetails.ToList(); //插入。 foreach (var item in vList) { dbProvider.Insert<HFareDetail>(item); } System.Console.WriteLine(string.Format("insert {0} records", vList.Count)); System.Console.ReadLine(); } //按條件修改某一條數據的幾個屬性值。 var v1 = vList.FirstOrDefault(); v1.Name = string.Empty; v1.Cash = decimal.Zero; dbProvider.Update<HFareDetail>(v1, p => p.ID == v1.ID); //按條件指量修改,指定某幾個必,其他屬性全部置空。 dbProvider.Update<HFareDetail>(p => new HFareDetail { Cash = decimal.Zero, Name = string.Empty, Price = decimal.Zero }, p => p.ChargeTime > DateTime.Now.AddDays(-1)); //依據條件刪除 dbProvider.Delete<HFareDetail>(p => p.ChargeTime > DateTime.Now.AddDays(-1)); //求Count using (var queryWarp = dbProvider.Linq<HFareDetail>()) { int count = queryWarp.Queryable .Where(p => p.SourceID == 0) .Count(); //Linq查詢Take\Skip。 var vList2 = queryWarp.Queryable .Where(p => p.CreateTime > DateTime.Now.Date.AddMonths(-12)) .Skip(10).Take(1000) .ToList(); System.Console.WriteLine(string.Format("query {0} records", vList.Count)); //Linq查詢過。 var vFare = queryWarp.Queryable .Where(p => p.CreateTime > DateTime.Now.Date.AddMonths(-12)) .FirstOrDefault(); System.Console.WriteLine(vFare); //Linq\聚合運算,目前因為測試驅動報錯,暫未實現 var sum = queryWarp.Queryable .Where(p => p.CreateTime > DateTime.Now.Date.AddMonths(-12)) .Sum(p => p.Cash); } System.Console.ReadLine();
具體是使用SequoiaDB還是使用MongoDB由系統配置文件來決定,使用SequoiaDB:
<!--StructDb/SequoiaDb--> <object name="StructDbProvider" assembly="EAS.Data.NoSQL" type="EAS.Data.SequoiaDbProvider" LifestyleType="Thread"> <property name="ConnectionString" type="string" value="192.168.23.57:50000"/> <property name="UserName" type="string" value=""/> <property name="Password" type="string" value=""/> <property name="CollectionSpace" type="string" value="his"/> </object>
使用MongoDB。
<!--StructDb/MongoDb--> <object name="StructDbProvider" assembly="EAS.Data.NoSQL" type="EAS.Data.MongoDbProvider" LifestyleType="Thread"> <property name="ConnectionString" type="string" value="mongodb://sa:sa@127.0.0.1:2222/his"/> <property name="DbName" type="string" value="his"/> </object>
七、SequoiaDB的C#驅動源代碼托管、下載
本人為SequoiaDB所寫的C#驅動,已提交托管到github,項目地址https://github.com/agilelab/SequoiaDB.Charp,歡迎大家下載,也歡迎大家和本人一道完善本驅動。
八、聯系我們
敏捷軟件工程實驗室,是一家研究、推廣和發展新技術,並致力於提供具有自主知識產權的業務基礎平台軟件,以及基於業務基礎平台開發的管理軟件的專業軟件提供商。主要業務是為客戶提供軟件企業研發管理解決方案、企業管理軟件開發,以及相關的技術支持,管理及技術咨詢與培訓業務。
AgileEAS.NET SOA中間件平台自2004年秋呱呱落地一來,我就一直在逐步完善和改進,也被應用於保險、醫療、電子商務、房地產、鐵路、教育等多個應用,但一直都是以我個人在推廣,2010年因為我辭職休息,我就想到把AgileEAS.NET推向市場,讓更多的人使用。
我的技術團隊成員都是合作多年的老朋友,因為這個平台是免費的,所以也沒有什么收入,都是由程序員的那種理想與信念堅持,在此我感謝一起奮斗的朋友。
AgileEAS.NET網站:http://www.agileeas.net
官方博客:http://eastjade.cnblogs.com
github:https://github.com/agilelab/eas
QQ:47920381,AgileEAS.NET
QQ群:113723486(AgileEAS SOA 平台)/上限1000人
199463175(AgileEAS SOA 交流)/上限1000人
212867943(AgileEAS.NET研究)/上限500人
147168308(AgileEAS.NET應用)/上限500人
172060626(深度AgileEAS.NET平台)/上限500人
116773358(AgileEAS.NET 平台)/上限500人
125643764(AgileEAS.NET探討)/上限500人
193486983(AgileEAS.NET 平台)/上限500人
郵件:james@agilelab.cn,mail.james@qq.com,
電話:18629261335。