1. 程式人生 > >本人為巨杉資料庫(開源NoSQL)寫的C#驅動,支援Linq,全部開源,已提交github

本人為巨杉資料庫(開源NoSQL)寫的C#驅動,支援Linq,全部開源,已提交github

一、關於NoSQL的專案需求

     這些年在做AgileEAS.NET SOA 中介軟體平臺的推廣、技術諮詢服務過程之中,特別是針對我們最熟悉的醫療行業應用之中,針對大資料分析,大併發效能的需求,我們也在慢慢的引用NoSQL技術來滿足資料分析與效能等多方面的需要,也進一步完善我們的SOA基石架構風格:

image_thumb[11]

     在早些年,對NoSQL不是很瞭解這前,後端資料儲存都是儲存的單一的關係資料庫之上,但是在很多時間,這並不是最優的,比如在醫療使用者之中針對一個病人的相關資料展示,及相關性分析,關於資料庫就不是最優的,另外一個,電子病歷系統的之中的結構化/半結構化病歷文件的儲存、檢索,以及更高階的應用,結構化病歷資料探勘,之前使用關係資料庫儲存或者使用檔案儲存,很難發揮病歷資料的科研和統計、分析需求。

    在目前我們的醫療資訊化應用之中,我們針對這兩部分資料都引入了NoSQL儲存,針對住院患者的領域相關性資料==》即病人聚合根物件相關資料,我們即在關係資料庫以多表儲存病人資料以及病人相關的醫囑、費用、檢驗、檢查、護理等相關資訊,同時我們也在NoSQL資料庫儲存患者的聚合根物件:

0NO5MFOQW74SFJC`@6R(@_3

    在NoSQL資料庫之中的儲存:

~NHW)0EH_0OC$KA7~8LYITU

    另外在電子病歷應用之中,病歷文件也是直接存入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管理介面:

image

     以下是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", "[email protected]");
            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:[email protected]:2222/his"/>
        <property name="DbName" type="string" value="his"/>
      </object>

七、SequoiaDB的C#驅動原始碼託管、下載

八、聯絡我們

     敏捷軟體工程實驗室,是一家研究、推廣和發展新技術,並致力於提供具有自主智慧財產權的業務基礎平臺軟體,以及基於業務基礎平臺開發的管理軟體的專業軟體提供商。主要業務是為客戶提供軟體企業研發管理解決方案、企業管理軟體開發,以及相關的技術支援,管理及技術諮詢與培訓業務。

     AgileEAS.NET SOA中介軟體平臺自2004年秋呱呱落地一來,我就一直在逐步完善和改進,也被應用於保險、醫療、電子商務、房地產、鐵路、教育等多個應用,但一直都是以我個人在推廣,2010年因為我辭職休息,我就想到把AgileEAS.NET推向市場,讓更多的人使用。

     我的技術團隊成員都是合作多年的老朋友,因為這個平臺是免費的,所以也沒有什麼收入,都是由程式設計師的那種理想與信念堅持,在此我感謝一起奮鬥的朋友。

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人

郵件:[email protected],[email protected],

電話:18629261335。