這款月銷2萬輛的自主精品SUV,車主們是如何評價的?

再來看它的動力表現,搭載的是最大馬力為156ps的1。5T渦輪增壓發動機,匹配6MT或6AT變速箱,低扭表現良好,跑起來給人的感覺是比較輕快的,特別是來自愛信的6AT變速箱換擋平順、邏輯清晰。值得表揚的還有它對於底盤的調校繼承了長安一貫的好功底,有一種不錯的緊緻感,在保證一定舒適性的同時能兼顧到支撐性,方向盤的力度也適中。

在競爭異常激烈的緊湊型SUV陣營中,有一款國產SUV自上市以來銷量就一路攀升,現今的銷量能保持每月1-2萬輛的好成績。

那麼它到底有哪些吸引消費者的魔力呢?各位已經購車的車主們又是怎麼評價的?想知道答案的話就繼續往下看吧:

對於外觀的評分,我認為車主打得並不低,長安CS55的前臉造型看起來比較敦實、有力量感,扁平的前進氣格柵與前大燈採用了一體化設計,格柵使用粗體的“CNANGAN”字樣辨識度很高。車身側麵線條比例協調,B、D柱採用隱藏式設計,讓車頂有一種懸浮的效果,但側面被不少人說看到路虎的影子。

總之外觀見仁見智,我們接着說內飾:

有值得點贊的方面,比如中控台的按鍵布局比較規整,紅黑線條的搭配凸顯運動感,但是用料中規中矩,而且車內異味也略大。同時那塊8英寸的中控屏的設計被吐槽有點雪佛蘭旗下車型的影子。

作為一款家用SUV,空間是消費者比較注重的方面,而長安CS55在這方面的表現還是不負眾望的:

身高為178cm的體驗者在駕駛座上調整好座椅可獲得4指的頭部空間,來到後排頭部空間剩餘3指、腿部空間超過兩拳,並且中央地板隆起度不大,可以給中間乘客提供還算不錯的舒適性,另外後備廂的容量一般但車內儲物格的儲物能力絕對會給你驚喜,日常家用基本是不用擔心的。

再來看它的動力表現,搭載的是最大馬力為156ps的1.5T渦輪增壓發動機,匹配6MT或6AT變速箱,低扭表現良好,跑起來給人的感覺是比較輕快的,特別是來自愛信的6AT變速箱換擋平順、邏輯清晰。

值得表揚的還有它對於底盤的調校繼承了長安一貫的好功底,有一種不錯的緊緻感,在保證一定舒適性的同時能兼顧到支撐性,方向盤的力度也適中。但是美中不足的是發動機的噪音有點大,而且油耗也不是特別理想,畢竟1.5噸的車重擺在那。

它的主要競品車型有同樣大賣的自主緊湊型SUV傳祺GS4:

結語:綜合來看,空間大、動力足是長安CS55能獲得廣大車主滿意的關鍵特點,同時底盤有韌性、操控性還算不錯也能討得車主的歡喜。當然車無完車,內飾與油耗方面還有待提升的空間,不過這份成績已經是屬於自主品牌緊湊型SUV里偏上等水平了,你怎麼看呢?歡迎留言告訴我們喲!本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

※網頁設計最專業,超強功能平台可客製化

※產品缺大量曝光嗎?你需要的是一流包裝設計!

開這款四門轎車,想不被關注都難,奔馳寶馬都得繞道

並不是誰都能觸手可及瑪莎拉蒂Ghibli是特別的它的發展並不存在“延續”曾經的它是跑車雙門兩座跑車和雙門四座跑車分別都是它的前身可是今天的它不再是一款跑車它僅僅是在不同的歷史階段扮演不同的角色它有着一顆3。0T V6的心臟最大馬力350pS最大扭矩500N·m匹配8AT變速箱如果以跑車,甚至超跑的水準去衡量它的動力這還是嫩了一點4門5座的三廂設計讓它顯得更居家儘管它帶有跑車的基

拉風的外觀

奢華的內飾

澎湃的動力

精準的操控

具備這些因素的一定是超跑?

不!也有可能是瑪莎拉蒂Ghibli

極具藝術感的外觀造型

分分鐘能讓你在鬧市中成為焦點

內飾設計舒適、豪華

還有各種真皮面料、高檔木飾等配飾供你選裝

就連車漆顏色、輪轂、剎車卡鉗、音響等配置都可以選配

讓你打造出獨一無二的專屬座駕

正因為它是如此獨特

彷彿時刻都在訴說“生人勿近”

畢竟它的身價

……

並不是誰都能觸手可及

瑪莎拉蒂Ghibli是特別的

它的發展並不存在“延續”

曾經的它是跑車

雙門兩座跑車和雙門四座跑車分別都是它的前身

可是今天的它不再是一款跑車

它僅僅是在不同的歷史階段扮演不同的角色

它有着一顆3.0T V6的心臟

最大馬力350pS

最大扭矩500N·m

匹配8AT變速箱

如果以跑車,甚至超跑的水準去衡量它的動力

這還是嫩了一點

4門5座的三廂設計讓它顯得更居家

儘管它帶有跑車的基本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

這3台不到20萬的中型車,讓開邁騰的年輕人羡慕到想賣車

阿特茲,雖然是一大堆的優點,但都是針對駕駛者而設計的,因此乘客的乘坐體驗就一般了,加上在風噪胎躁方面的控制不是很好,整體的乘坐質感並不高,如果是想給家人更多的舒適體驗,那還是再考慮考慮吧。蒙迪歐,綜合實力很平均,然而在這個價位購車的朋友們多多少少會考慮到油耗這個問題,雖然蒙迪歐的動力數據很好,但在強大動力的加持下,油耗的表現確實是偏高。

今天,跟大家嘮嘮幾款打着運動旗號的中型車,它們分別是:阿特茲,蒙迪歐以及思鉑睿,一看到這幾款車,大家都知道,這些車都有一個通病,就是銷量不高~

其中,蒙迪歐在2017的全年的銷量為:112158台,阿特茲則是:53493台,思鉑睿最少,才19277台,換個說法,這三款車每個月的銷量都是不會破萬的,它們的小眾程度可見一般。

三車外觀如何?

阿特茲自不必說,採用了“魂動”設計語言的它不負“ATENZA”這個寓意為萬眾矚目的稱號。

蒙迪歐,大多朋友戲稱為“小馬丁”的存在,當然,此馬丁非天才DJ Martin Garrix,而是汽車界的紳士-阿斯頓·馬丁,雖然嚴謹的說,它們沒什麼關係,但是蒙迪歐的運動外觀卻是不爭的事實。

思鉑睿,低矮的車身,兇悍的前進氣格柵,這都是它為了區別於傳統舒適性中型車的外觀而設計的。

既然是主打運動,那麼動力是必不可少的較量了

通過三台車的動力系統對比,蒙迪歐在總體上比其它兩款車的動力更加好,畢竟渦輪增壓在這裏佔得便宜不小,但是論起操控,號稱東瀛寶馬的馬自達是三車之中最好的,而屬於買發動機送車殼的本田思鉑睿在這三款車中,發動機與變速箱的匹配程度是最高的。

那麼,運動型車能家用嗎?

在這之前,給大家看看三車的後排空間數據,大家就明白是咋回事了。

阿特茲空間數據

蒙迪歐空間數據

思鉑睿空間數據

通過對比看出,其實三款車型均為中型轎車,在空間方面也不要過於擔心,就算再不濟也是足夠正常使用。

三車的配置上,哪個更優?

給大家帶來的是三款車在相近價位的配置對比,通過對比看出阿特茲的配置更加豐富一點,也更加符合國人買車時“配置豐富”的需求,所以阿特茲在配置對比方面優勢不小。

說了這麼多,這些車都沒有缺點嗎?

當然不是,為你一一道來。

阿特茲,雖然是一大堆的優點,但都是針對駕駛者而設計的,因此乘客的乘坐體驗就一般了,加上在風噪胎躁方面的控制不是很好,整體的乘坐質感並不高,如果是想給家人更多的舒適體驗,那還是再考慮考慮吧。

蒙迪歐,綜合實力很平均,然而在這個價位購車的朋友們多多少少會考慮到油耗這個問題,雖然蒙迪歐的動力數據很好,但在強大動力的加持下,油耗的表現確實是偏高。

思鉑睿,說句老實話,它的裝配水平在這麼多的車之中只能算是一般,而且車內各處的異響也不少,但好在整體的行駛質感做得不錯,在保證一定運動格調的情況下還能兼顧到家庭所需。

對於這三款車型而言,其實取捨也很簡單,假如你是一名極致的操控愛好者,那麼阿特茲絕對可以滿足到你,而針對於蒙迪歐和思鉑睿而言,雖然同樣定位在運動型中級車上,但是它們依然保持着很不錯的舒適性能兼顧到家庭所需,更適合那些以家庭出行為主但又對操控有所要求的朋友。

那麼這幾款車,哪個配置的最值得入手呢?阿特茲,選擇2.5L藍天運動版性價比是最高的,在保證足夠的動力的前提下配置也足夠我們日常所需;而蒙迪歐則可以考慮到1.5T的時尚型,動力能滿足日常所需而且在配置上也能滿足需求;最後輪到思鉑睿,雖然2.4L版本更值得推薦,而且已經自帶了原來的SI運動包圍,但據消息稱2.4L版本已經開始停產,而作為主銷車型的實則為2.0L的低排量版本以及混動版本,而更推薦考慮混動版本。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

公民發動護溪 擺脫後勁溪污名

摘錄自2020年9月7日自由時報報導

逐步淨化擺脫污名的楠梓後勁溪,沿岸社區居民為重建對河岸生活的期待,發動護溪公民行動,在北高雄社區大學協助凝聚共識,啟動社區共同腦力激盪,提出「後勁溪的100種可能」。

提案實踐首部曲,由社區一群年輕志工號召建立「漂浮花園DIY工作坊」,邀請社區民眾一起動動手,將生活中的廢棄物改造成花園浮島,以植物妝點後勁溪河面,希望浮島可以種出點綴河岸美麗的花朵,或是可食用的水耕蔬菜。

想要讓後勁溪回到能「魚蟹同樂」的時代;希望有「音樂和啤酒」,岸邊有啤酒吧和輕爵士音樂;也許能做「水上餐廳」, 一邊用餐、一邊欣賞夕陽美景 ;水面上玩「SUP立式划槳」,讓年輕人更親近這個環境,也帶動周邊的人潮……

北高雄社區大學副校長張豐藤說,活動推出之後,陸續喚起後勁溪沿岸居民的回響,開始願意天馬行空的發揮想像力,對重建河岸生活有了期待。

【其他文章推薦】

※新北市探針選用參考標準?

※如何知道自已的電腦cpu支不支持AVX指令集?

※如何正確使用飲水機?

※攻戰消費者第一視覺,包裝設計很重要!

※滑鼠墊適用各種文宣活動廣告曝光,專業客製服務

※封口機購物網-不怕你比價,就怕你買貴!

※高溫殺菌機最多可達多少溫度?

黃崑茂表示,如果讓養豬場建置完成

黃崑茂表示,如果讓養豬場建置完成,以後臭豬屎尿味隨風四處飄散,裡民就必須要天天忍受聞臭豬屎尿味的痛苦,除了地價會一落千丈,更影響身體健康。

裡民說,上茄苳裡有四百多公頃農田,農作物面積環繞整個社區,嘉南大圳也沿線經過,還有小南海風景區,清澈無污染的灌溉用水源,此地真的不適合設養豬場。

【其他文章推薦】

※無塵擦拭布各大品牌廠商販售比價網!

※如何正確使用飲水機?

※如何知道自已的電腦cpu支不支持AVX指令集?

※掌握產品行銷策略,帶你認識商品包裝設計基本要素

※精密CNC 自動車床設備介紹

※空壓機這裡買最划算!

防止瑞士冰川消融 環團籲政府減碳排

摘錄自2020年9月7日台灣醒報報導

環保人士們聚集在瑞士冰川下,要求瑞士政府採取減碳排行動。過去30年間,瑞士特里安冰川因氣候暖化倒退至少1000公尺,今年8月另一側的圖爾特曼冰川也發生大規模崩落,氣候保育人士為阻止瑞士冰川繼續消融,6日聚集在白朗峰山脈下,呼籲政府減少碳排放,減緩氣候變遷對瑞士自然景觀的影響。

根據《路透社》報導,逾200人6日聚集在位於西歐最高山脈—白朗峰山脈的特里安冰川山腳下,呼籲瑞士當局應採取減少碳排放的行動,以及注意阿爾卑斯山週邊冰架及冰川消融的情況。這場抗議活動發生在瑞士國會爭論減少碳排放新法規的前一日。

在過去的30年間,特里安冰川已後退至少1000公尺。此外,8月份時,與特里安冰川隸屬同一州的圖爾特曼冰川被登山客拍到融化崩塌的影片,估計崩落30萬平方公尺的冰川。

【其他文章推薦】

※客製專屬滑鼠墊、可愛造型L夾、L型資料夾、透明證件套、手提袋,專業印刷設計廠商!  

※示波器鮮為人知的使用技巧?

※掌握產品行銷策略,帶你認識商品包裝設計基本要素

※買不起高檔茶葉,精緻包裝茶葉罐,也能撐場面!

※貨梯使用安全與保養

※高效率洗滌塔活性碳設備有哪些?

※專業模具開發-五金製品代工製作廠

五個Taurus垃圾回收compactor優化方案,減少系統資源佔用

簡介

TaurusDB是一種基於MySQL的計算與存儲分離架構的雲原生數據庫,一個集群中包含多個存儲幾點,每個存儲節點包含多塊磁盤,每塊磁盤對應一個或者多個slicestore的內存邏輯結構來管理. 在taurus的slicestore中將數據划為多個slice進行管理,每個slice的大小是10G,Taurus架構圖如下:

TauruDB的存儲層支持append-only寫和隨機讀,最小數據存儲邏輯單元為plog,每個slice中包含多個plog,默認每個plog的大小為64M。slice中的plog主要用來存放page。Plog中存放中不同版本的page,有些老page已經過期,需要刪除;有些page是新page,需要被保留下來。

Compator主要用來清理plog中過期的page,把一個plog上所有沒有過期的page搬移到一個新plog,老的plog刪除掉。

Compactor的任務需要頻繁訪問內存中索引結構和讀寫plog中的page頁,這兩部分都屬於整個系統中關鍵資源,鎖競爭的壓力比較大,會直接影響性能,所以compactor的優化方案主要圍繞減少內存訪問和磁盤IO,需要考慮以下幾個點:

A、 選取的清理的plog集最優問題,每次回收需要搬運有效page,搬運的有效page數越少,磁盤IO就越小。如何每次調度都選取到垃圾量最大的一批plog;

B、 垃圾量是否分佈均勻,如何讓垃圾集中到一起,回收垃圾集中的plog,提高回收效率;

C、 回收周期是固定的,怎麼樣保證在每個周期內,都能取到最優plog集。

當前,我們提出了幾個可以有效減少page搬運數,從而達到減少IO目的的方案。

關鍵點1:全局調度

全局調度的方案增大plog垃圾量的排序範圍,從slice的範圍增大到slicestore的範圍。因為考慮到後面需要針對單個磁盤進行“加速回收”,所以不擴大到一個存儲節點的全局範圍。

全局調度方案按照slicestore來選取回收plog,先遍歷所有的slicestore,然後在slicestore內部進行垃圾量排序,選取最多的若干個plog進行回收;

優點:有效避免一個slicestore中由於垃圾分佈不均勻引起的plog的無效搬運,減少對plog的讀寫產生的IO;

缺點:排序範圍增大后,排序算法會增加CPU的消耗;

關鍵點2:排序算法優化

原始方案中在slice內部對plog按照垃圾量排序採用C++標準庫排序(std::sort),該算法內部基於快速排序、插入排序和堆排序實現。原始方案中每個slice中最多存有160個plog,小數據量的排序或許效率影響不大,但是一個slicestore中存儲成百上千的slice,排序算法的效率問題就值得關注。

Taurus設計了一種topN算法,能夠提升該場景下的效率。假設需要在n個元素中選取m個最大的元素,兩種算法的時間複雜度和空間複雜度:

C++標準庫排序時間複雜度為O(nlogn),空間複雜度為O(nlogn);

topN算法排序時間複雜度為O(nlogm),空間複雜度為O(1);

Compactor應用場景中,n和m相差幾個數量級,topN算法在時間和空間上都更具優勢。

優點:減少時間複雜度和空間複雜度;

關鍵點3:調度數優化

公共線程池分配給compactor的線程數是固定的,每個周期調度器生成一次任務。原始方案中compactor的每次生成的任務數由slice個數決定,會導致任務隊列中的任務數過多或者過少。過多的話會失去時效性,也就是說plog的垃圾量會隨着時間改變,如果在隊列等待執行的時間太長,可能就不是當前最高垃圾量的plog,同時一次挑選的plog個數太多,會增加算法的時間和空間複雜度;過少的話,compactor線程沒有跑滿,會導致垃圾回收速度降低。

調度數優化也是基於全局調度優化,調度策略只需要保證在一個調度周期內,任務隊列中任務數剛好滿足compactor線程執行。假設有8個slicestore,分配了24個執行線程,每個線程每個調度周期完成一個plog的回收,則每個調度周期每個slicestore只需要生成3個任務。

調度數優化即記錄公共線程池中正在執行和準備執行的任務數,跟據記錄決定本輪調度生成多少個回收任務,從而保證執行線程剛好夠用,且不多不少。

優點:保證垃圾回收速度,最優回收的plog集,有效的減少page的搬運量。

關鍵點4:冷熱分區優化

在數據庫系統中,數據的更新並不是相同頻率,一些數據頁更新或者寫入會更加頻繁(例如系統頁),這部分頁面被稱作熱頁,另外一些更新不頻繁的稱作冷頁。如果把冷頁和熱頁混合放到一起,就會導致更高的寫放大。舉個簡單的例子,假設有100個熱頁和10個冷頁,冷頁基本不更新但是每次垃圾回收,都需要重複把冷頁搬運一次。如果把冷頁單獨寫入一個plog,那麼在垃圾回收階段,就可以減少重複搬運這部分冷頁,達到減少IO放大的效果。

優點:提高垃圾回收的效率和速度。

缺點:需要額外的內存記錄熱度信息。

關鍵點5:磁盤逃生優化

為了後台線程比如備份、快照、垃圾回收、頁面回放等線程有足夠的磁盤空間運行,在磁盤容量使用到達一定閾值,會置Full標誌位,同時停止前台IO。在極端情況下,磁盤使用到閾值前台IO會出現斷崖式下跌為零:

為了避免在磁盤容量達到一定閾值之後前台IO完全停止,在磁盤使用率未達到閾值時,就應該有相應的處理機制。比如說在磁盤使用率未達閾值時,增加如下處理:

A、 降低前台IO,減少磁盤的壓力;

B、 加速垃圾回收,也就是TaurusDB中的compactor機制;

A點不涉及compactor的功能,所以本文先不涉及,下面主要介紹兩種加速機制:

1.修改寫IO優先級

Compactor作為後台線程,考慮到整個系統的效率,正常運行時plog寫IO優先級默認為low。在加速回收階段,plog的IO需要修改為high。

2.提高執行速度

前文可知,公共線程池為compactor分配固定數目執行線程,而且運行過程中只支持擴容不支持恢復。如果壓縮其他後台任務的執行線程,對整個系統的影響太大,量也不易控制。所以不考慮。

考慮到過程中不能擴容,那就初始化就擴容,通過控制調度任務數控制任務執行速度。例如,假設compactor的運行線程在正常場景下為4個,加速狀態下需要增加到8個。可以在公共線程池中先分配8個線程,在正常場景下,控制任務隊列為4個,另外4個線程處於wait狀態,只會佔用文件句柄,並不影響CPU。而在加速狀態下,控制任務隊列中的任務數為8,就可以實現上述的加速邏輯。

所以,提升寫IO的優先級能夠加速page的搬運,提升垃圾回收速度。通過控制調度任務數控制任務執行速度卻很難控制很精準,上下會有一定的小波動。

最後

以上提到的垃圾回收compactor優化方案中,磁盤逃生優化能夠處理磁盤容量緊急情況。目前根據本地測試,在500G的小數據集情況下,IO放大能減少到原來的1/6。系統資源佔用減少到原來的1/3。

 

點擊關注,第一時間了解華為雲新鮮技術~

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

面向對象存儲框架:Obase快速入門

在項目中完成對象建模后,可以使用Obase來進行對象的管理(例如對象持久化),本篇教程將創建一個.NET Core控制台應用,來展示Obase的配置和對象的增刪改查操作。本篇教程旨在指引簡單入門。
本篇教程將以此對象模型展開

class Blog{
    +BlogId:int[文章Id]
    +Url:string[文章地址]
    +Post:sList<Post>[文章評論]
}

class Post{
    +PostId:int[評論Id]
    +Title:string[評論標題]
    +Content:string[評論內容]
    +Blog:Blog[關聯文章]
}

Blog "1"-right-"*"  Post
hide empty member

從NuGet安裝Obase

  • 在VS的NuGet包管理器中添加程序包源:http://nuget.suiyiyun.cn:8081/nuget
  • 在NuGet包管理器中選擇Obase進行安裝

項目搭建

  • 打開 Visual Studio
  • 單擊“創建新項目”
  • 選擇帶有 C# 標記的“控制台應用 (.NET Core)” ,然後單擊“下一步”
  • 輸入“ObaseTutorial” 作為名稱,然後單擊“創建”
  • 添加對freep.Obase.dll的引用

定義領域實體類

	/// <summary>
    /// 文章
    /// </summary>
    public class Blog
    {
        private int blogId;
        private string url;
        private List<Post> posts;

        /// <summary>
        /// 文章Id
        /// </summary>
        public int BlogId { get => blogId; set => blogId = value; }
        /// <summary>
        /// 文章地址
        /// </summary>
        public string Url { get => url; set => url = value; }
       /// <summary>
       /// 文章評論(注意:關聯引用屬性需要定義為virtual)
       /// </summary>
        public virtual List<Post> Posts { get => posts; set => posts = value; }
    }

   /// <summary>
    /// 文章評論
    /// </summary>
    public class Post
    {
        private int postId;
        private string title;
        private string content;
        private int blogId;
        private Blog blog;

        /// <summary>
        /// 評論Id
        /// </summary>
        public int PostId { get => postId; set => postId = value; }
        /// <summary>
        /// 評論標題
        /// </summary>
        public string Title { get => title; set => title = value; }
        /// <summary>
        /// 評論內容
        /// </summary>
        public string Content { get => content; set => content = value; }
        /// <summary>
        /// 文章Id
        /// </summary>
        public int BlogId { get => blogId; set => blogId = value; }
        /// <summary>
        /// 關聯文章(注意:關聯引用屬性需要定義為virtual)
        /// </summary>
        public virtual Blog Blog { get => blog; set => blog = value; }
    }

自定義對象上下文

Obase直接與應用程序進行交互的便是ObectContext(對象上下文),項目中可以根據具體情況定義一個或者多個繼承於ObjectContext的自定義對象上下文。

	using freep.Obase;
	using freep.Obase.ExecuteSql;
	using freep.Obase.Odm.Builder;

	/// <summary>
    /// 自定義對象上下文
    /// </summary>
    public class MyContext : ObjectContext
    {
        /// <summary>
        /// 構造函數
        /// </summary>
        public MyContext() : base("user=root;password=;server=localhost;database=ObaseTutorial;SslMode = none;port=3306;", true)
        {
        }
    }

注意:自定義對象上下文通過繼承父類的構造函數設置數據源連接字符串(此處為了演示方便,直接將連接字符串作為參數進行傳遞,實際項目中可以定義到配置文件中)。

配置對象模型

在對象數據模型生成之前,可以對數據源的類型進行設置,以及對象數據模型的配置,配置的類型包括實體類型,關聯類型,關聯引用,關聯端,屬性等的配置,本篇只展示最基本的實體類型,關聯類型,關聯引用的配置。

    /// <summary>
    /// 自定義對象上下文
    /// </summary>
    public class MyContext : ObjectContext
    {
		/// <summary>
        /// 在即將生成對象數據模型並註冊到對象上下文之前調用此方法
        /// </summary>
        /// <param name="modelBuilder">建模器</param>
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            //設置模型映射目標源的類型(默認不設置未SQLServer)
            modelBuilder.HasTargetSourceType(eDataSource.MySql);
            //配置對象數據模型
            this.ModelConfiguratoin(modelBuilder);
            base.OnModelCreating(modelBuilder);
        }
        
        /// <summary>
        /// 配置對象數據模型
        /// </summary>
        /// <param name="modelBuilder">建模器</param>
        protected virtual void ModelConfiguratoin(ModelBuilder modelBuilder)
        {
        	//配置實體型
            var blogCfg = modelBuilder.Entity<Blog>();
            //設置實體型的映射數據表
            blogCfg.ToTable("Blog");
            //設置實體型的標識屬性
            blogCfg.HasKeyAttribute(p => p.BlogId);
            //設置實體型的標識屬性為自增
            blogCfg.HasKeyIsSelfIncreased(true);

            //配置實體型
            var postCfg = modelBuilder.Entity<Post>();
            //設置實體型的映射數據表
            postCfg.ToTable("Post");
            //設置實體型的標識屬性
            postCfg.HasKeyAttribute(p => p.PostId);
            //設置實體型的標識屬性為自增
            postCfg.HasKeyIsSelfIncreased(true);

            //配置對象間隱式關聯類型
            var blogAssPostCfg = modelBuilder.Association<Blog, Post>();
            //設置關聯類型的映射數據表
            blogAssPostCfg.ToTable("Post");
            //設置關聯映射端1(參照方)的鍵屬性以及在關聯表中映射的字段
            blogAssPostCfg.AssociationEnd<Blog>("End1").HasMapping("BlogId", "BlogId");
            //設置關聯映射端2(被參照方)的鍵屬性以及在關聯表中映射的字段
            //注意:HasDefaultAsNew方法設置一個值,該值指示是否把關聯端對象默認視為新對象。當該屬性為true時,如果關聯端對象未被顯式附加到上下文,該對象將被視為新對象實施持久化。
            blogAssPostCfg.AssociationEnd<Post>("End2").HasMapping("PostId", "PostId").HasDefaultAsNew(true);

            //配置實體類型的關聯引用屬性
            //參數一:關聯引用屬性的名稱 參數二:關聯引用是否具有多重性
            //注:此處在配置Blog實體與Post實體關聯引用屬性Posts
            var blogRefPosts = blogCfg.AssociationReference<Blog, Post>("Posts", true);
            //設置關聯引用的本端
            blogRefPosts.HasLeftEnd("End1");
            //設置關聯引用的對端
            blogRefPosts.HasRightEnd("End2");
            //設置關聯引用屬性延遲加載
            blogRefPosts.HasEnableLazyLoading(true);

            //配置實體類型的關聯引用屬性
            //參數一:關聯引用屬性的名稱 參數二:關聯引用是否具有多重性
            //注:此處在配置Post實體與Blog實體關聯引用屬性Blog
            var postRefBlog = postCfg.AssociationReference<Blog, Post>("Blog", false);
            //設置關聯引用的本端(注意此處Post是作為本端的)
            postRefBlog.HasLeftEnd("End2");
            //設置關聯引用的對端
            postRefBlog.HasRightEnd("End1");
        }
	}

定義對象集

最終對對象的操作和訪問是通過對象上下文提供的對象集,此處我們定義文章和文章評論對象集:

    /// <summary>
    /// 自定義對象上下文
    /// </summary>
    public class MyContext : ObjectContext
    {
        /// <summary>
        /// 文章對象集
        /// </summary>
        public ObjectSet<Blog> Blogs { get; set; }

        /// <summary>
        /// 文章評論對象集  
        /// </summary>
        public ObjectSet<Post> Posts { get; set; }
    }

對象的創建、讀取、更新和刪除

實例化對象上下文
var myContext = new MyContext();
創建
//實例化對象
Blog blog = new Blog()
{
    Url = "https://www.yuque.com/geekfish/obase/getting-started",
    Posts = new List<Post>() {
        new Post (){  Title= "請問Obase怎麼安裝?", Content = "暫時只提供dll文件"}
    }
};
//將對象附加到對象上下文
myContext.Blogs.Attach(blog);
//將對象保存到數據源
myContext.SaveChanges();
讀取
using System.Linq;

//從持久化源查詢數據
Blog firstBlog = myContext.Blogs.OrderBy(p => p.Url).First();
//訪問關聯引用屬性
List<Post> posts = firstBlog.Posts;
更新
 //修改屬性
firstBlog.Url = "http://www.test.com/aa.html";
//將對象保存到數據源
myContext.SaveChanges();
刪除
//刪除指定對象
myContext.Blogs.Remove(firstBlog);
//根據條件刪除指定對象
myContext.Blogs.Delete(p => p.BlogId == 1);
//將對象保存到數據源(只有在保存后,數據才真實刪除)
myContext.SaveChanges();

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※產品缺大量曝光嗎?你需要的是一流包裝設計!

技術人員該如何站好最後一班崗?

挑槽、入槽、跳槽,堪稱每個技術人員必奏的三部曲,而這三部曲在職場中來回奏,便構成了程序人生。

鐵打的硬盤,流水的碼農,離職時見人品,作為技術人員該如何做交接,到底該如何站好最後一班崗呢?

 

1. 人品不夠,文檔來湊。

 

從上家公司離職已經 5 年多啦,記得離職大概沒多久,前技術同事微信告訴我:你寫的交接文檔,在會議上公開表揚,讓其它組作為參考。

當時個人感覺沒啥,就是寫了一堆文檔罷了,近期看到其它團隊交接的效果,那麼的不盡人意。而且秉着吐露真心,認真分享的原則,不妨把那些年寫過的交接文檔,逐一呈現給你,萬一能助你積攢人品、升職加薪呢?

1.1. 作為技術人員離職前的交接,編寫交接進展表為了誰?

離職前的交接,非常能展現人品,最重要的原則是:交接時一定要儘力而為。

盡量能打造屬於自己的交接計劃,按照計劃一步一步去落實,並把交接進展維護在 excel 中,如圖中的《交接進展表.xlsx》。

編寫交接進展表,一方面讓大家明確知曉交接的過程與進度,另一方面可供後人按此方式進行無腦式交接(前人栽樹後人乘涼)。

1.2. 作為技術人員離職前的交接,編寫XX系統_新手入門文檔為了誰?

離職前的交接,希望都能編寫新手入門之類的傻瓜式文檔,該文檔編寫是個一勞永逸的事情。

倘若後續接手你的是一個新同事,那麼就更有價值,按照入門文檔,一步一步就能上手開發、提測、上線,這樣的文檔誰不喜歡?

僅以上面截圖為例,新手入門文檔中包含了系統的簡要說明、功能說明以及功能模塊劃分,可以讓接手的同事對系統有一個全局的認識。

當然,最重要的是要告訴要接手的同事,如何去幹活?文檔中的應用目錄結構介紹以及如何開發、如何編譯、如何提測、如何上線,這幾大塊就顯得很重要。

另外,站在團隊培養人的成本而言,新手入門文檔,不僅僅是為了做好交接,倘若項目組一直就具備該文檔,能夠讓新手快速上手開發業務需求,大概率會降低團隊溝通、培訓的成本。

1.3. 作為技術人員離職前的交接,編寫XX系統_開發生產部署文檔為了誰?

離職前的交接,當接手的同事能夠按照入門文檔開發需求之後,更一步的就是要了解開發、測試、生產部署環境相關的信息。

 

僅以上面截圖為例,開發及生產部署文檔,也可以理解成環境相關的文檔。其主要目的是匯總開發、生產環境部署的機器、應用部署的位置及應用該如何訪問的關鍵信息,讓接手的同事,能夠清楚當需求開發完成時,應用應該如何部署。

1.4. 作為技術人員離職前的交接,編寫XX系統_業務支撐文檔為了誰?

離職前的交接,當接手的同事了解完如何入門開發、開發生產部署環境,接下來就要花大量的時間,去了解支撐的業務。

鑒於支撐的業務會較多,作為接手的同事梳理起來會比較頭疼,那麼一個清晰的文檔索引就很重要。

業務支撐文檔就是把 SVN 或者 Git 上的產品相關的資料,分門別類把路徑整理到文檔中,以便接手的同事查閱,以便進行快速深入。

1.5. 作為技術人員離職前的交接,編寫XX系統_經驗匯總文檔為了誰?

離職前的交接,最重要的是分享前車之鑒,對於要接手的同事而言少走彎路,避免再掉坑,絕對是一筆財富。

僅以上面截圖為例,系統的經驗匯總文檔,主要記錄平時該注意的事項、項目團隊中以往遇到那些坑,以及如何把坑填平的。

有了經驗匯總文檔,無論是接手的同事,還是新招的同事,再去做需求開發,相信同樣問題出錯的概率應該會大幅降低。

不過該經驗文檔離不開一個長期積累的過程,所以程序員要養成一個善於記錄的習慣。

 

2. 人品不夠,分享來湊。

 

離職前的交接,梳理文檔是一方面,隔三差五的組織分享也是必不可少的環節。

如上面截圖所示,主要包含生產部署相關以及業務支撐相關,目的就是把重要的信息,以培訓會議的形式再次同步給大家,讓團隊中的每個人都做到心中有數。

當然,鑒於分享會耗費大家的時間,所以要提前準備好要分享的重要信息,合理安排時間去完成分享。

 

3. 離職之後,保持藕斷絲連。

 

如果你之前負責的是重要項目,即使交接做的很成功,但是之前的老同事,偶爾還會給你打電話諮詢項目的事情,至少會持續一個月甚至更長。

那麼請不要悲傷、憤怒,換個角度去思考,前同事有問題能想到你,說明你在他們心中還是有分量的,或許他們認為你了解的比較透徹,知道問題的解決方案,能夠快速幫其解決問題。

 

今日留情面,他日好相見。

 

互聯網的圈子真的很小,說不定哪一天又在下一家公司相見啦,所以一定要留有情面,把事情交接好,把最後一班崗站好。

最後一班崗站好,大家心中都有你,有機會就會向你拋橄欖枝。

 

曾經的那些人兒,那些事兒。

 

橄欖枝一:發生在 3 年前,上家公司的技術總監去了知名網購平台,電話問我能不能把簡歷發來,是否願意來承擔一些事兒?當我接到電話時,瞬間詫異,技術總監都拿到我手機號啦。

橄欖枝二:發生在去年,上家公司的某位高級經理被挖去了新的公司,擔任 CTO 職位,由於業務擴展,多次問我是不是可以一起搞一搞?

 

估計很多人都接過橄欖枝,聊橄欖枝不是為了裝 B,只是想反思一下橄欖枝背後,是不是和之前交接的過程有點關係呢?

 

4. 鐵打的硬盤,流水的碼農。

 

作為技術人員請不要:這個我交接給他啦,你直接去找他吧!

作為技術人員請不要:惡意製造交接困難,讓交接難上加難!

作為技術人員請做到:站好最後一班崗,今日留情面,他日好相見。

 

本次主要分享了之前交接時的思路以及寫過的一些文檔,如果感覺有一絲參考價值,那請拿去在團隊中實踐,沉澱下來的都是財富。

好了,分享就到這裏,希望對你有幫助。一起聊技術、談業務、噴架構,少走彎路,不踩大坑。歡迎關注「一猿小講」,會持續輸出原創精彩分享,敬請期待!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

※網頁設計最專業,超強功能平台可客製化

※產品缺大量曝光嗎?你需要的是一流包裝設計!

Vue數據雙向綁定原理

Vue數據雙向綁定

Vue是通過數據劫持的方式來實現數據雙向數據綁定的,其中最核心的方法便是通過Object.defineProperty()來實現對屬性的劫持,該方法允許精確地添加或修改對象的屬性,對數據添加屬性描述符中的getter與setter實現劫持。

描述

運行一個Vue實例並將data打印,可以看到對象中對於msg有了get與set,通過他們就可以實現數據的劫持,從而進行數據的更新,在Vue中get與set是通過ES5的Object.defineProperty()方法定義的,該方法的具體功能可以查閱https://github.com/WindrunnerMax/EveryDay/blob/master/JavaScript/defineProperty.md。

<!DOCTYPE html>
<html>
<head>
    <title>數據綁定</title>
</head>
<body>
    <div id="app">
        <div>{{msg}}</div>
    </div> 
</body>
<script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
<script type="text/javascript">
    var vm = new Vue({
        el: '#app',
        data: {
            msg: 'Data'
        },
        created: function() {
            console.log(this.$data); //{__ob__: Observer} 
        }
    })
</script>
</html>
/*
{__ob__: Observer}
    msg: "Data"
    __ob__: Observer {value: {…}, dep: Dep, vmCount: 1}
    get msg: ƒ reactiveGetter()
    set msg: ƒ reactiveSetter(newVal)
    __proto__: Object
*/

分析實現

Vue的雙向數據綁定,簡單點來說分為以下三個部分:

  • Observer: 這裏的主要工作是遞歸地監聽對象上的所有屬性,在屬性值改變的時候,觸發相應的Watcher。
  • Watcher: 觀察者,當監聽的數據值修改時,執行響應的回調函數,在Vue裏面的更新模板內容。
  • Dep: 鏈接Observer和Watcher的橋樑,每一個Observer對應一個Dep,它內部維護一個數組,保存與該Observer相關的Watcher。

根據上面的三部分實現一個功能非常簡單的Demo,實際Vue中的數據在頁面的更新是異步的,且存在大量優化,實際非常複雜。
首先實現Dep方法,這是鏈接Observer和Watcher的橋樑,簡單來說,就是一個監聽者模式的事件總線,負責接收watcher並保存。其中subscribers數組用以保存將要觸發的事件,addSub方法用以添加事件,notify方法用以觸發事件。

function __dep(){
    this.subscribers = [];
    this.addSub = function(watcher){
        if(__dep.target && !this.subscribers.includes(__dep.target) ) this.subscribers.push(watcher);
    }
    this.notifyAll = function(){
        this.subscribers.forEach( watcher => watcher.update());
    }
}

Observer方法就是將數據進行劫持,使用Object.defineProperty對屬性進行重定義,注意一個屬性描述符只能是數據描述符和存取描述符這兩者其中之一,不能同時是兩者,所以在這個小Demo中使用getter與setter操作的的是定義的value局部變量,主要是利用了let的塊級作用域定義value局部變量並利用閉包的原理實現了getter與setter操作value,對於每個數據綁定時都有一個自己的dep實例,利用這個總線來保存關於這個屬性的Watcher,並在set更新數據的時候觸發。

function __observe(obj){
    for(let item in obj){
        let dep = new __dep();
        let value = obj[item];
        if (Object.prototype.toString.call(value) === "[object Object]") __observe(value);
        Object.defineProperty(obj, item, {
            configurable: true,
            enumerable: true,
            get: function reactiveGetter() {
                if(__dep.target) dep.addSub(__dep.target);
                return value;
            },
            set: function reactiveSetter(newVal) {
                if (value === newVal) return value;
                value = newVal;
                dep.notifyAll();
            }
        });
    }
    return obj;
}

Watcher方法傳入一個回調函數,用以執行數據變更后的操作,一般是用來進行模板的渲染,update方法就是在數據變更后執行的方法,activeRun是首次進行綁定時執行的操作,關於這個操作中的__dep.target,他的主要目的是將執行回調函數相關的數據進行sub,例如在回調函數中用到了msg,那麼在執行這個activeRun的時候__dep.target就會指向this,然後執行fn()的時候會取得msg,此時就會觸發msg的get(),而get中會判斷這個__dep.target是不是空,此時這個__dep.target不為空,上文提到了每個屬性都會有一個自己的dep實例,此時這個__dep.target便加入自身實例的subscribers,在執行完之後,便將__dep.target設置為null,重複這個過程將所有的相關屬性與watcher進行了綁定,在相關屬性進行set時,就會觸發各個watcher的update然後執行渲染等操作。

function __watcher(fn){
    this.update = function(){
        fn();
    }
    
    this.activeRun = function(){
        __dep.target = this;
        fn();
        __dep.target = null;
    }
    this.activeRun();
}

代碼示例

這是上述的小Demo的代碼示例,其中上文沒有提到的__proxy函數主要是為了將vm.$data中的屬性直接代理到vm對象上,兩個watcher中第一個是為了打印並查看數據,第二個是之前做的一個非常簡單的模板引擎的渲染,為了演示數據變更使得頁面數據重新渲染,在這個Demo下打開控制台,輸入vm.msg = 11;即可觸發頁面的數據更改,也可以通過在40行添加一行console.log(dep);來查看每個屬性的dep綁定的watcher。

<!DOCTYPE html>
<html>
<head>
    <title>數據綁定</title>
</head>
<body>
    <div id="app">
        <div>{{msg}}</div>
        <div>{{date}}</div>
    </div> 
</body>
<script type="text/javascript">

    var Mvvm = function(config) {
        this.$el = config.el;
        this.__root = document.querySelector(this.$el);
        this.__originHTML = this.__root.innerHTML;

        function __dep(){
            this.subscribers = [];
            this.addSub = function(watcher){
                if(__dep.target && !this.subscribers.includes(__dep.target) ) this.subscribers.push(watcher);
            }
            this.notifyAll = function(){
                this.subscribers.forEach( watcher => watcher.update());
            }
        }


        function __observe(obj){
            for(let item in obj){
                let dep = new __dep();
                let value = obj[item];
                if (Object.prototype.toString.call(value) === "[object Object]") __observe(value);
                Object.defineProperty(obj, item, {
                    configurable: true,
                    enumerable: true,
                    get: function reactiveGetter() {
                        if(__dep.target) dep.addSub(__dep.target);
                        return value;
                    },
                    set: function reactiveSetter(newVal) {
                        if (value === newVal) return value;
                        value = newVal;
                        dep.notifyAll();
                    }
                });
            }
            return obj;
        }

        this.$data = __observe(config.data);

        function __proxy (target) {
            for(let item in target){
                Object.defineProperty(this, item, {
                    configurable: true,
                    enumerable: true,
                    get: function proxyGetter() {
                        return this.$data[item];
                    },
                    set: function proxySetter(newVal) {
                        this.$data[item] = newVal;
                    }
                });
            }
        }

        __proxy.call(this, config.data);

        function __watcher(fn){
            this.update = function(){
                fn();
            }
            
            this.activeRun = function(){
                __dep.target = this;
                fn();
                __dep.target = null;
            }
            this.activeRun();
        }

        new __watcher(() => {
            console.log(this.msg, this.date);
        })

        new __watcher(() => {
            var html = String(this.__originHTML||'').replace(/"/g,'\\"').replace(/\s+|\r|\t|\n/g, ' ')
            .replace(/\{\{(.)*?\}\}/g, function(value){ 
                return  value.replace("{{",'"+(').replace("}}",')+"');
            })
            html = `var targetHTML = "${html}";return targetHTML;`;
            var parsedHTML = new Function(...Object.keys(this.$data), html)(...Object.values(this.$data));
            this.__root.innerHTML = parsedHTML;
        })

    }

    var vm = new Mvvm({
        el: "#app",
        data: {
            msg: "1",
            date: new Date(),
            obj: {
                a: 1,
                b: 11
            }
        }
    })

</script>
</html>

每日一題

https://github.com/WindrunnerMax/EveryDay

參考

https://www.jianshu.com/p/255d4dec710a
https://www.jianshu.com/p/c8186e9e027b
https://www.cnblogs.com/wangjiachen666/p/9883916.html
https://blog.csdn.net/wangshu696/article/details/84570886
https://blog.csdn.net/qq_43051529/article/details/82877673
https://github.com/liutao/vue2.0-source/blob/master/%E5%8F%8C%E5%90%91%E6%95%B0%E6%8D%AE%E7%BB%91%E5%AE%9A.md

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!