新聞中心
NOSQL近來(lái)勢(shì)頭不錯(cuò),MongoDB更是其中的嬌嬌者,自己學(xué)NoSQL的時(shí)候也是參考了大量的資料,最終決定要從MongoDB入手的,最重要的原因有兩點(diǎn):1自己是簡(jiǎn)單的愛(ài)好者,一切問(wèn)題我都在想是否有簡(jiǎn)單的方法解決,寧可停下來(lái)去思考大量時(shí)間,也不愿用笨方法馬上去做,而MongoDB的操作大都很簡(jiǎn)單,2自己是JS的愛(ài)好者,沒(méi)事就喜歡拿一本js的本從頭到尾看一邊,也不管記住多少,也不管用不用得到,就是喜歡,MongoDB以BSON格式存儲(chǔ),所以操作也起來(lái)也算得心應(yīng)手!現(xiàn)在做一個(gè)項(xiàng)目正是用MongoDB做為數(shù)據(jù)庫(kù)的,一開(kāi)始沒(méi)有DAL,BLL直接訪問(wèn)數(shù)據(jù)庫(kù),然后就到UI了,而且BLL是全靜態(tài)的(我喜歡靜態(tài)方法的調(diào)用簡(jiǎn)單,但狠靜態(tài)類(lèi)的不能繼承!),當(dāng)時(shí)考慮的是用MongoDB的驅(qū)動(dòng)去操作太直白了!感覺(jué)沒(méi)必要再寫(xiě)個(gè)DAL!,后來(lái)知道我想法還是很天真的,哈哈!下面就說(shuō)現(xiàn)在的操作方式吧~

創(chuàng)新互聯(lián)公司主要從事網(wǎng)站建設(shè)、成都網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)葉集,10余年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專(zhuān)業(yè),歡迎來(lái)電咨詢(xún)建站服務(wù):028-86922220
一、Module層
[Serializable]
public sealed class user
{
public ObjectId id;
public string n;
public int age;
public Birthday birth;
public sealed class Birthday
{
public int y;
public int m;
public int d;
}
}
咋一看,有幾個(gè)地方不規(guī)范,1類(lèi)名首字母和公開(kāi)字段沒(méi)有大寫(xiě),2公開(kāi)的字段,而沒(méi)有用屬性,3字段命名沒(méi)表達(dá)它的意思?,F(xiàn)在逐個(gè)解釋一下,類(lèi)名和字段沒(méi)大寫(xiě)首字母主要是數(shù)據(jù)庫(kù)里的命名是遵循js的,用js表示時(shí)大家一般會(huì)這樣寫(xiě):
var user={id:ObjectId("123456"),n:"loogn",age:23,birth:{y:1989,m:7,d:7}}
然而,可能有人會(huì)說(shuō)可以用MongoDB.Bson.Serialization.Attributes.BsonElement這樣一個(gè)Attribute關(guān)聯(lián)呢!不過(guò)我不會(huì)那樣做,原因就是太麻煩了!這里可能還是有疑問(wèn),留到第3個(gè)問(wèn)題說(shuō)。
公開(kāi)字段而沒(méi)有用屬性也是不合常理的,學(xué)校老師都交過(guò),不管你能不能理解,反正就是要私有化字段,想公開(kāi)請(qǐng)用屬性,哈哈!不過(guò)我想了很久還是不走尋常路了,目前為止用字段沒(méi)有出現(xiàn)過(guò)缺陷問(wèn)題,我不保證以后不會(huì),但我感覺(jué)幾率十分小,就算真的有什么問(wèn)題必需要用屬性,后面加上{get;set;}也不麻煩吧!屬性畢竟還是方法,用屬性有多余的方法調(diào)用開(kāi)銷(xiāo),而且實(shí)體類(lèi)本來(lái)就是不尋常的類(lèi),一般只表示對(duì)象狀態(tài)(用字段),屬性里如果有邏輯(就像老師常說(shuō)的年齡不能小于0且不能大于150等等!),你會(huì)放到這里做嗎?顯然你都是放在BLL里做!字段命名太短了沒(méi)有表達(dá)它的意思,其實(shí)這個(gè)可以和***個(gè)一起來(lái)說(shuō),MongoDB是無(wú)模式的,同一個(gè)合集可以保多個(gè)不規(guī)則的文檔,如user集合:
{id:1,n:"user1",desc:"我的描述"}
{id:2,n:"user2"}
所以數(shù)據(jù)庫(kù)必須保存文檔的每一個(gè)元素的name(即id,name,desc,id,name),所以元素name越短越節(jié)省空間,本來(lái)是用name更能表達(dá)的,這里用了n,其實(shí)只要把常用的約定一下,絕大部分人都是可以接受的。
在user里還有個(gè)內(nèi)嵌類(lèi)Birthday,而這個(gè)類(lèi)大寫(xiě)了首字母,我是這樣考慮的,內(nèi)嵌類(lèi)名全部按C#命名規(guī)范,因?yàn)槿萜黝?lèi)有一個(gè)該內(nèi)嵌類(lèi)類(lèi)型的字段,這里是birth,但如果找不到合適的縮寫(xiě)怎么辦呢,直接小寫(xiě)內(nèi)嵌類(lèi)名就可以了,如內(nèi)嵌城市類(lèi)City,字段名為city就不會(huì)重復(fù)了。
二、DAL層
在這一層要寫(xiě)一個(gè)基類(lèi),完成這個(gè)基類(lèi)后,其他的各各DAL類(lèi)都是浮云了~,在寫(xiě)基類(lèi)之前有一個(gè)MongoHelper,MongoHelper很簡(jiǎn)單,直接給出代碼且不寫(xiě)解釋?zhuān)?/p>
MongoServer
完了后就可以寫(xiě)B(tài)aseDAL了,如果沒(méi)有泛型,DAL的工作還真是索然無(wú)味,但現(xiàn)在用泛型DAL的工作有趣多了,先承上代碼:
///
/// 數(shù)據(jù)訪問(wèn)層基類(lèi)
///
///
public abstract class BaseDAL
{
protected internal string CollectionName { set; get; }
///
/// 設(shè)置集合名
///
protected abstract string SetCollectionName();
private MongoCollection
///
/// 根據(jù)CollectionName得到MongoCollection對(duì)象
///
protected internal MongoCollection
{
get
{
if (m_collection == null)
{
CollectionName = SetCollectionName();
m_collection = MongoHelper.GetDatabase().GetCollection
}
return m_collection;
}
}
///
/// 根據(jù)query條件得到一個(gè)文檔對(duì)象
///
/// 查詢(xún)條件
/// 預(yù)處理方法
///
public TDocument FindOne(IMongoQuery query, PreprocessHandler
{
var document = Collection.FindOne(query);
if (preprocess != null)
{
preprocess(ref document);
}
return document;
}
///
/// 把MongoCursor轉(zhuǎn)換成IList類(lèi)型
///
/// 文檔游標(biāo)
/// 預(yù)處理方法
///
protected internal IList
{
IList
bool isPreprocess = preprocess != null;
foreach (TDocument document in cursor)
{
var doc = document;
if (isPreprocess)
preprocess(ref doc);
list.Add(doc);
}
return list;
}
///
/// 根據(jù)query查詢(xún)集合
///
/// 條件
/// 預(yù)處理方法
///
public IList
{
var cursor = Collection.Find(query);
if (cursorSettings != null)
{
cursorSettings.Set(cursor);
}
var list = CursorToList(cursor, preprocess);
return list;
}
}
最上面的代碼就是設(shè)置操作哪個(gè)集合,這里有一點(diǎn)感覺(jué)不爽,為什么屬性的get和set不能分別為抽象的呢?!雖然可以把整個(gè)屬性標(biāo)記為abstract,但在實(shí)現(xiàn)類(lèi)中也要寫(xiě)get和set的實(shí)現(xiàn)(set可以是空代碼塊),所以這里回歸原本用了一個(gè)SetCollectionName的抽象方法讓子類(lèi)去設(shè)置自己對(duì)應(yīng)的集合名。
當(dāng)你得到MongoCollection對(duì)象,特別是MongoCollection
public delegate void PreprocessHandler
有很多這樣的情況,得到一個(gè)實(shí)體后總是要先處理一下才可被UI方便的使用,如用戶(hù)頭像為空時(shí),給個(gè)默認(rèn)的,PreprocessHandler就是給BLL處理留個(gè)方便的接口啦!
這里選擇委托而不是其他的元素使程序更靈活(有匿名委托嘛,I like it!),大家注意到了吧,實(shí)體類(lèi)是按引用傳遞的,其實(shí)這里有個(gè)坑,我跳進(jìn)去了,但又爬上來(lái)了!然后這里立了個(gè)牌:"此處有坑,請(qǐng)繞道而行",以免匆忙趕路的你也栽個(gè)跟頭兒。
有這樣一個(gè)情況,如果你把一個(gè)null實(shí)體對(duì)象傳入處理方法,又在處理方法里判斷如果是null就實(shí)體化,這樣是得不到預(yù)期效果的,此null非彼null呀,實(shí)體化后方法里的型參是指向新對(duì)象了,但傳過(guò)來(lái)的實(shí)參還是指向null呢,這不是我們想要的,用ref便可以解決了,也許你會(huì)說(shuō)可以把實(shí)體對(duì)象返回呀,是的,但個(gè)人不喜歡那種寫(xiě)法,那樣處理方法***還要寫(xiě)reurn代碼,調(diào)用方法可能還得寫(xiě)代碼接收,麻煩!
FindOne例子完了,但FindOne遠(yuǎn)遠(yuǎn)沒(méi)完,你可以做各種你喜歡的重載。
再看Find得到多個(gè)文檔的方法,這里我選擇IList<實(shí)體>做為返回值,在BLL決不去操作MongoCursor,但有這樣一個(gè)問(wèn)題,設(shè)置Fields、Limit、排序等都是在MongoCursor上操作的呀,而且這些操作很可能都是從UI傳過(guò)來(lái)的,所以這里用了一個(gè)MongoCursorSettings類(lèi)封裝了這些設(shè)置:
MongoCursorSettings
代碼不用解釋?zhuān)憧梢詳U(kuò)展此類(lèi)做更多設(shè)置而不用修改Find方法。
CursorToList方法也很簡(jiǎn)單,其實(shí)把PreprocessHandler寫(xiě)在DAL層的原因就是這個(gè)方法啦,心細(xì)的你肯定發(fā)現(xiàn)了把PreprocessHandler寫(xiě)在BLL更合理,但那樣文檔集合就要多遍歷一遍,MongoCursor到IList一遍,PreprocessHandler處理IList又一遍!唉,程序員容易嘛~~~
當(dāng)然,你也可以對(duì)Find做各種你喜歡的重載,更要寫(xiě)其他方面(Insert,Update,Remove...)的方法對(duì)BaseDAL類(lèi)進(jìn)行完善。
***讓親看一下User浮云(其他浮云也是這個(gè)樣):
public class User:BaseDAL
{
protected override string SetCollectionName()
{
return "user";
}
}
三、BLL層
有泛型就是意思,看BaseBLL:
///
/// 業(yè)務(wù)邏輯層基類(lèi)
///
///
///
public abstract class BaseBLL
{
protected TDAL dal = new TDAL();
public TDocument FindOne(IMongoQuery query, PreprocessHandler
{
return dal.FindOne(query,preprocess);
}
}
基本上是對(duì)DAL的一個(gè)調(diào)用,無(wú)他!直接到它的子類(lèi),因?yàn)槭沁壿媽?,比浮云多一點(diǎn),可以算是個(gè)神馬吧:
public sealed class User : BLL.BaseBLL
{
public user FindOneByName(string name)
{
var doc = base.FindOne(Query.EQ("u", name), P1);
return doc;
}
///
/// 保證不為null
///
///
private void P1(ref user doc)
{
if (doc == null)
{
doc = new user();
}
P2(ref doc);
}
///
/// 也許可以處理嬰兒,哈哈
///
///
private void P2(ref user doc)
{
if (doc != null)
{
doc.age = 0;
}
}
}
代碼也是很簡(jiǎn)單,其實(shí)這里有一個(gè)想法,很多實(shí)體類(lèi)總是只有一種處理方法,可以在BaseBLL里寫(xiě)一個(gè)PreprocessHandler委托簽名的虛方法做為默認(rèn)處理方法, 在BaseBLL里就調(diào)用該方法,子類(lèi)需要就可重寫(xiě)它,這樣又簡(jiǎn)單了,為了方面查看,兩個(gè)類(lèi)的代碼寫(xiě)在一塊了:
///
/// BaseBLL的默認(rèn)處理方法
///
///
protected virtual void Preprocess(ref TDocument doc)
{
return;
}
///
/// LG.BLL.User重寫(xiě)基類(lèi)方法
///
///
protected override void Preprocess(ref user doc)
{
if (doc == null)
doc = new user();
if (doc.birth == null)
doc.birth = new user.Birthday();
}
到此,BLL事例也完了,當(dāng)然,還要做更多的工作去完善。
四、UI層
這里其實(shí)沒(méi)啥說(shuō)的,為了文章完整性,簡(jiǎn)單提一下。
一般情況下UI是不會(huì)引用DAL的,但因?yàn)锽aseBLL用了泛型參數(shù),而泛型類(lèi)型在DAL里,所以UI也要引用DAL,但永遠(yuǎn)不要用DAL。
還有一點(diǎn),就是邏輯層實(shí)例化的問(wèn)題,比如在同一次Http請(qǐng)求中,很可能不小心或者避免不了new了LG.BLL.User多次,這樣做只是白白浪費(fèi)了內(nèi)存,增加GC壓力,沒(méi)一點(diǎn)好處,所以,再回到BLL層,添加這樣一個(gè)類(lèi):
///
/// 為了在一次請(qǐng)求中同類(lèi)型邏輯對(duì)象只實(shí)例化一次,
/// 只能在http請(qǐng)求上下文中使用
///
public static class B
{
public static T Entity
{
string key = typeof(T).Name;
if (HttpContext.Current != null)
{
var bll = HttpContext.Current.Items[key] as T;
lock (key)
{
if (bll == null)
{
bll = new T();
HttpContext.Current.Items[key] = bll;
}
}
return bll;
}
else
{
return new T();
}
}
}
在UI或確定是有HTTP請(qǐng)求的上下文中都可以這樣調(diào)用了(當(dāng)然,如果基于其他上下文,也可以寫(xiě)一個(gè)類(lèi)似上面的實(shí)例化方法):
User u = B.Entity
到止完結(jié),平時(shí)寫(xiě)文章不多,表達(dá)欠佳,望親們見(jiàn)諒!
本文標(biāo)題:談?wù)凪ongoDB的三層操作
分享地址:http://m.5511xx.com/article/ccdsooj.html


咨詢(xún)
建站咨詢(xún)
