米塔颱風襲南韓已釀9死 日本高知暴雨淹大水

摘錄自2019年10月3日自由時報報導

米塔颱風在週三晚間襲擊南韓南部,南韓南部港口城市釜山發生土石流,南韓行政安全部3日表示,截至下午,全國共有9人喪生,但傷亡人數預計會在攀升,因為還有數人下落不明。

南韓國防部表示,米塔風災造成1000多棟房屋損壞,超過1500人提前被撤離。

米塔離開朝鮮半島後,已往日本西部前進,同樣帶來驚人雨量,日本氣象廳警告,儘管米塔颱風可能在4日減弱為溫帶氣旋,但朝著日本北部前進時,強風豪雨可能引發洪水、土石流等災情。

【動画】高知県内で大雨 道路冠水も

— 高知新聞 (公式)Kochinews (@Kochi_news)

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

新北清潔公司,居家、辦公、裝潢細清專業服務

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

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

※超省錢租車方案

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

世界最毒蕈類 澳洲首度發現「火焰茸」

摘錄自2019年10月3日自由時報報導

澳洲詹姆士庫克大學(James Cook University)3日宣布,當地攝影師在凱恩斯(Cairns)的郊區拍到「火焰茸」,經科學家確定是一種又被稱為毒火珊瑚(Poison Fire Coral)的真菌。

外媒稱,火焰茸的原生棲地在日本和南韓的山區,這種鮮紅色的真菌經常被誤認為是可食用的紅珊瑚菌(Clavulinopsis miyabeana)、冬蟲夏草,誤食而中毒甚至死亡。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

NASA最新公布 洞察號錄下「火星震」低沉鳴叫

摘錄自2019年10月3日自由時報報導

美國太空總署(NASA)探測器「洞察號」(InSight)去年底飛抵火星表面至今,,分別從今年5月22日發生的「Sol 173震盪」及7月25日的「Sol 235震盪」錄下,這兩次火星震若換算成常用單位,分別為芮氏規模3.7與3.3。

「洞察號」火星任務最主要的目標是探測火星內部結構,科學家希望藉此理解,所謂「類地行星」(terrestrial planet,又稱為岩質行星)是如何形成的。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

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

※回頭車貨運收費標準

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

數據庫char varchar nchar nvarchar,編碼Unicode,UTF8,GBK等,Sql語句中文前為什麼加N(一次線上數據存儲亂碼排查)

背景

公司有一個數據處理線,上面的數據經過不同環境處理,然後上線到正式庫。其中一個環節需要將數據進行處理然後導入到另外一個庫(Sql Server)。這個處理的程序是老大用python寫的,處理完後進入另外一個庫后某些字段出現了亂碼。
比如這個字符串:1006⁃267X(2020)02⁃0548⁃10
另外一個庫變成:1006?267X(2020)02?0548?10
線上人員反饋回來后老大由於比較忙,一直沒有排查,然後我問了下估計是什麼原因。老大說他python裏面轉了utf8,可能是編碼問題。我當時問了下就沒下文了,因為我不會python,所以這個事情就擱置了。

排查過程

然後這個問題拖了很久,線上也不斷反饋。同時自己也負責這塊,空閑時間就主動去排查了下原因。當然這個排查過程還是比較曲折的,所以就把這個過程分享下,同時回顧下涉及到的知識點。

先說結果:最後經過排查是由於python處理后insert語句插入到Sql Server數據庫保存字段前沒有加N

1.SQL Server數據類型

首先由於數據寫進去出現亂碼,所以第一步就是檢查寫入庫的字段是否設置了正確的數據類型。因為有時候對char與varchar的區別或者varchar與nvarchar的區別不是很在意,所以有可能設置了錯誤的數據類型。至於這幾個字符的數據類型區別是什麼,這裏摘抄官方解釋。

字符數據類型 char(大小固定)或 varchar(大小可變) 。 從 SQL Server 2019 (15.x) 起,使用啟用了 UTF-8 的排序規則時,這些數據類型會存儲 Unicode 字符數據的整個範圍,並使用 UTF-8 字符編碼。 若指定了非 UTF-8 排序規則,則這些數據類型僅會存儲該排序規則的相應代碼頁支持的字符子集。
參數

char [ ( n ) ]
固定大小字符串數據 。 n 用於定義字符串大小(以字節為單位),並且它必須為 1 到 8,000 之間的值 。 對於單字節編碼字符集(如拉丁文),存儲大小為 n 個字節,並且可存儲的字符數也為 n。 對於多字節編碼字符集,存儲大小仍為 n 個字節,但可存儲的字符數可能小於 n。 char 的 ISO 同義詞是 character 。
varchar [ ( n | max ) ]
可變大小字符串數據 。 使用 n 定義字符串大小(以字節為單位),可以是介於 1 和 8,000 之間的值;或使用 max 指明列約束大小上限為最大存儲 2^31-1 個字節 (2GB)。 對於單字節編碼字符集(如拉丁文),存儲大小為 n + 2 個字節,並且可存儲的字符數也為 n。 對於多字節編碼字符集,存儲大小仍為 n + 2 個字節,但可存儲的字符數可能小於 n 。

字符數據類型 nchar(大小固定)或 nvarchar(大小可變) 。 從 SQL Server 2012 (11.x) 起,使用啟用了補充字符 (SC) 的排序規則時,這些數據類型會存儲 Unicode 字符數據的整個範圍,並使用 UTF-16 字符編碼。 若指定了非 SC 排序規則,則這些數據類型僅會存儲 UCS-2 字符編碼支持的字符數據子集。

nchar [ ( n ) ]
固定大小字符串數據。 n 用於定義字符串大小(以雙字節為單位),並且它必須為 1 到 4,000 之間的值 。 存儲大小為 n 字節的兩倍。 對於 UCS-2 編碼,存儲大小為 n 個字節的兩倍,並且可存儲的字符數也為 n。 對於 UTF-16 編碼,存儲大小仍為 n 個字節的兩倍,但可存儲的字符數可能小於 n,因為補充字符使用兩個雙字節(也稱為代理項對)。 nchar 的 ISO 同義詞是 national char 和 national character 。
nvarchar [ ( n | max ) ]
可變大小字符串數據。 n 用於定義字符串大小(以雙字節為單位),並且它可能為 1 到 4,000 之間的值 。 max 指示最大存儲大小是 2^30-1 個字符 (2 GB) 。 存儲大小為 n 字節的兩倍 + 2 個字節。 對於 UCS-2 編碼,存儲大小為 n 個字節的兩倍 + 2 個字節,並且可存儲的字符數也為 n。 對於 UTF-16 編碼,存儲大小仍為 n 個字節的兩倍 + 2 個字節,但可存儲的字符數可能小於 n,因為補充字符使用兩個雙字節(也稱為代理項對)。 nvarchar 的 ISO 同義詞是 national char varying 和 national character varying 。

通過上面的描述我們可以總結:這幾種類型都是存儲字符數據,如果存儲單字節的字符串(比如英文)使用char、varchar,節約空間。如果存儲多字節的字符串(比如包含中文)使用nchar、nvarchar,兼容更多的編碼。雙字節比單字節對應的多了一個n
單字節雙字節中還有一個區別var,表示可變大小字符串數據。可變是指如果某字段插入的值超過了數據頁的長度,該行的字段值將存放到ROW_OVERFLOW_DATA中。但是會造成多餘的I/O,比如一個VARCHAR列經常被修改,而且每次被修改的數據的長度不同,這會引起‘行遷移’(Row Migration)現象。這裏就不展開了,可以去了解下。
所以我們設計數據庫字段的時候需要根據業務設計合理的數據類型,有利於節約空間和時間。而經過我檢查數據庫字段確實設置的nvarchar,所以不存在存儲不了對應編碼問題。而且問了老大他說python裏面他轉了UTF8編碼,所以下一步就是排查是否轉編碼出了問題。

2.編碼
因為我經常寫C#,C#裏面的字符串是Unicode的,當然對於程序員來說這個編碼是透明的,因為是Unicode編碼可以轉換成其它任何編碼,所以我們日常開發的時候並不需要時刻去關注編碼的問題,其底層已經幫我們進行了處理。既然說是python轉了utf8那麼我就去大概看了下python的基礎並試驗了一把。
先找了一條出現亂碼的數據,在原庫取出來然後進行utf8轉碼,然後再解碼。講道理同一個編碼解碼出來存儲應該還是原來的字符串,所以我才會好奇去試驗。試驗后發現果然沒有什麼問題。

關於編碼可以看下這個講解:編碼,因為講的比較形象而且容易理解,所以我這裏就不累述了。
排除python程序編碼問題,那接下來就是要排查從程序插入到數據庫這一段的問題了。

3.SQL Server排序規則
首先插入這一階段我想到的還是編碼問題,所以去查詢了數據庫編碼。使用sql語句查詢數據庫排序規則

SELECT COLLATIONPROPERTY('Chinese_PRC_Stroke_CI_AI_KS_WS', 'CodePage')

對應的字符集編碼
936 :簡體中文GBK
950 :繁體中文BIG5
437 :美國/加拿大英語
932 :日文
949 :韓文
866 :俄文
65001 :unicode UTF-8
查詢了數據排序規則,導入數據庫是默認排序規則,也就是936 GBK編碼。為什麼要看數據庫排序規則,第1點中可見“數據類型僅會存儲該排序規則的相應代碼頁支持的字符子集”。
排序規則微軟解釋:排序規則

SQL Server 中的排序規則可為您的數據提供排序規則、區分大小寫屬性和區分重音屬性。 與諸如 char 和 varchar 等字符數據類型一起使用的排序規則規定可表示該數據類型的代碼頁和對應字符 。
無論你是要安裝 SQL Server 的新實例、還原數據庫備份,還是將服務器連接到客戶端數據庫,都必須了解正在處理的數據的區域設置要求、排序順序以及是否區分大小寫和重音。

所以通過查看排序規則知道,默認編碼是GBK。然後我就猜測到是GBK編碼問題,因為在python3裏面字符串的默認編碼也是Unicode,測試下把1006⁃267X(2020)02⁃0548⁃10轉成GBK。

可以看到是無法轉碼的,gbk識別不了那個短橫杠,然後我編碼成GB18030能夠編碼。說明短橫杠是更高位的編碼,當然unicode是能存儲的。那為什麼在數據庫裏面就成了亂碼呢?而且字段類型是設置的nvarchar啊。

4、大寫字母“N”作為前綴
通過3點的分析,說明了本該存儲成Unicode的編碼被保存成了默認編碼。所以我們只要把保存成Unicode編碼就行了,所以到此已經和python程序沒什麼關係了,帶着懷疑的態度,我將這段字符直接拿到Sql Sever裏面執行,果然也是亂碼。

最後就是在參數前加N執行

這下結果就正常了。細心的你是否發發現v1字段還是亂碼,因為我為了測試varchar單字節,即使我加了N一樣的是亂碼。所以記得存儲中文最好選nvarchar,原因么請看第一點char和varchar的說明中這樣一句話:若指定了非 UTF-8 排序規則,則這些數據類型僅會存儲該排序規則的相應代碼頁支持的字符子集。也就是它只會存儲我當前數據庫的GBK編碼。
最後我還在python裏面插入的sql語句加了N,同樣可以插入成功。

關於加N的解釋,微軟t-sql文檔關於insert說明:鏈接

5.為什麼我們平時很少加N
既然有這樣的問題為什麼我們平時基本沒加過N?原因有幾點:

  • 沒有遇到高位的編碼(直接拼接sql)。
  • 用SqlParameter 參數執行sql會自動加N。
  • 平時使用ORM框架已經幫我規避了這個問題。
    所以我們平時如果是直接使用sql時最好使用參數形式,既能幫我們解決sql注入攻擊,還能幫我們規避不加N導致的編碼問題。

SqlParameter會自動加N?帶着懷疑的態度測試下。
首先寫一個測試程序,然後開啟SQL server跟蹤來查看執行的sql。

       static void Test()
        {
            string server = "127.0.0.1";
            string database = "TestDB";
            string user = "sa";
            string password = "******";
            string connectionString = $"server={server};database={database};User ID={user};Password={password}";
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                connection.Open();
                using (SqlCommand cmd = new SqlCommand())
                {
                    cmd.Connection = connection;
                    cmd.CommandText = "insert into Test1 values('1006⁃267X(2020)02⁃0548⁃10','1006⁃267X(2020)02⁃0548⁃10')";
                    cmd.ExecuteNonQuery();

                    cmd.CommandText = "insert into Test1 values(@v1,@v2)";
                    cmd.Parameters.Add(new SqlParameter
                    {
                        ParameterName = "v1",
                        Value = "1006⁃267X(2020)02⁃0548⁃10"
                    });
                    cmd.Parameters.Add(new SqlParameter
                    {
                        ParameterName = "v2",
                        Value = "1006⁃267X(2020)02⁃0548⁃10"
                    });
                    cmd.ExecuteNonQuery();
                }
            }
        }

查看跟蹤執行的sql,一個是直接傳入拼接sql執行,一個是使用參數方式執行。

總結

通過一次排查亂碼問題,又回顧或者學習了關於數據類型和編碼,以及sql存儲如何避免亂碼問題。平時設計的時候如果是帶中文的字段首先考慮帶n的char類型。同時在直接使用sql進行insert、update的時候注意在要保存為Unicode編碼字符串前面加N。

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

【其他文章推薦】

※超省錢租車方案

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

※回頭車貨運收費標準

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

全球60城市氣候抗議行動開跑 紐澳數十人被捕 阿姆斯特丹數百人阻交通

摘錄自2019年10月7日聯合報報導

「反抗滅絕」在全球發起公民不服從運動7日展開,在澳洲和紐西蘭各地有數十名示威者遭到逮捕。這場運動將為期2週,目的為了要求各國政府採取緊急行動對抗氣候變遷。

數百人在繁忙的雪梨市中心道路上靜坐封路,最後警察拖走示威者終止這場活動,隨後有30人遭到起訴。

在布里斯本,一小群運動人士把自己鎖在一座橋上。警方表示,他們在現場逮捕7人,並全數加以起訴。

同一時間在紐西蘭首都威靈頓,示威者用鎖鏈把自己與一輛閃亮的粉紅色車輛綁住,造成市區部分交通癱瘓。警方表示,今天稍晚有30人遭到逮捕,但無人被起訴。

荷蘭阿姆斯特丹市7日出現數百名氣候變遷倡議人士,他們無視警方的禁令,高喊「反抗」,阻礙市中心交通。警方已警告在先,將逮捕違令人士,防止他們擾亂通勤民眾。

荷蘭警方已核准倡議團體「反抗滅絕」(Extinction Rebellion, XR)的抗議計劃,前提是不能阻礙交通。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

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

巴西東北海岸油污擴散 專家憂影響生物

摘錄自2019年10月8日中央社報導

自今年9月以來,大片黑色和黏稠的原油油污持續被海水拖動到巴西東北海岸沙灘,目前已在阿拉戈斯(Alagoas)、巴伊亞(Bahia)、塞阿拉(Ceara)、馬蘭豪(Maranhao)、巴萊巴(Paraiba)、培南布可(Pernambuco)、彼奧(Piaui)、北大河(Rio Grande do Norte)和謝吉貝(Sergipe)9州共61個城市超過132個地點發現油污紀錄,至少8隻海洋動物遭油污覆蓋全身窒息死亡。

巴西環保署(Ibama)稱,已經與受影響各州的消防隊合作,採取一系列措施紓緩油污問題,同時與海軍和巴西石油公司(Petrobras)的技術團隊一起試圖查明海上原油油污的來源。

謝吉貝州政府已宣布進入緊急狀態,並要求居民遠離沙灘,即使民眾有意幫忙淨灘活動。生物」。

沒有人知道這些油污來自何處,但專家擔心將對當地生物造成毀滅性的影響。巴西海龜保護計劃(Tamar)也因這個問題而暫停把新孵化出的小海龜放生到大海中。生物學家擔心污染會擴散得更嚴重,危及海龜、海豚和座頭鯨等海洋動物的繁殖。

阿拉戈斯州生物保護研究所長奧里維拉(Bruno Stefanis Santos Pereira de Oliveira)說,當務之急是防止油污擴散和造成更多損害,防止動物和人類接觸油污,避免其他人誤食體積較小的魚貝類而受到污染。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

※幫你省時又省力,新北清潔一流服務好口碑

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

諾貝爾物理獎揭曉 三得主讓宇宙、天文學徹底改觀

摘錄自2019年10月8日聯合報報導

諾貝爾物理獎8日揭曉,總獎金900萬瑞典克朗(約台幣2800萬元),諾貝爾委員會決定,其中一半頒給加拿大裔美籍宇宙學家皮博(James Peebles),表彰他在物理宇宙學的理論發現,另一半頒給瑞士科學家梅爾(Michel Mayor)和奎洛茲(Didier Queloz),表彰他們發現太陽系之外的行星「系外行星」,是人類首次發現系外行星。

皮博現年84歲,目前是美國普林斯頓大學名譽教授,被譽為最有影響力的宇宙學家之一。諾貝爾委員會說,皮博關於宇宙及其數以十億計星系與星系團的理論架構,是「從大爆炸至今,我們了解的宇宙史的基礎」,他的研究使用理論工具和計算過程,詮釋宇宙初期以來的痕跡,這些研究創造了適當條件,促使過50年來的宇宙學「徹底改觀」。

皮博的研究顯示,人類知道的物質如恆星、行星和人類自己,只占宇宙5%,其餘95%都是由「未知的暗物質和暗能量」構成。

梅爾和奎洛茲1995年發現系外行星「飛馬座51b」,與木星大小類似,諾貝爾委員會說,這兩名科學家「帶動天文學的革命」,從那時候開始,科學家在銀河系發現四千多顆系外行星,「如今仍在這個奇異新世界持續探索,發現各色各樣的系外行星,在大小、形態和軌道方面都不同」。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

新北清潔公司,居家、辦公、裝潢細清專業服務

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

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

※超省錢租車方案

MongoDB via Dotnet Core數據映射詳解

用好數據映射,MongoDB via Dotnet Core開發變會成一件超級快樂的事。

一、前言

MongoDB這幾年已經成為NoSQL的頭部數據庫。

由於MongoDB free schema的特性,使得它在互聯網應用方面優於常規數據庫,成為了相當一部分大廠的主數據選擇;而它的快速布署和開發簡單的特點,也吸引着大量小開發團隊的支持。

關於MongoDB快速布署,我在15分鐘從零開始搭建支持10w+用戶的生產環境(二)里有寫,需要了可以去看看。

作為一個數據庫,基本的操作就是CRUD。MongoDB的CRUD,不使用SQL來寫,而是提供了更簡單的方式。

方式一、BsonDocument方式

BsonDocument方式,適合能熟練使用MongoDB Shell的開發者。MongoDB Driver提供了完全覆蓋Shell命令的各種方式,來處理用戶的CRUD操作。

這種方法自由度很高,可以在不需要知道完整數據集結構的情況下,完成數據庫的CRUD操作。

方式二、數據映射方式

數據映射是最常用的一種方式。準備好需要處理的數據類,直接把數據類映射到MongoDB,並對數據集進行CRUD操作。

下面,對數據映射的各個部分,我會逐個說明。

    為了防止不提供原網址的轉載,特在這裏加上原文鏈接:https://www.cnblogs.com/tiger-wang/p/13185605.html

二、開發環境&基礎工程

這個Demo的開發環境是:Mac + VS Code + Dotnet Core 3.1.2。

建立工程:

% dotnet new sln -o demo
The template "Solution File" was created successfully.
cd demo 
% dotnet new console -o demo
The template "Console Application" was created successfully.

Processing post-creation actions...
Running 'dotnet restore' on demo/demo.csproj...
  Determining projects to restore...
  Restored demo/demo/demo.csproj (in 162 ms).

Restore succeeded.
% dotnet sln add demo/demo.csproj 
Project `demo/demo.csproj` added to the solution.

建立工程完成。

下面,增加包mongodb.driver到工程:

cd demo
% dotnet add package mongodb.driver
  Determining projects to restore...
info : Adding PackageReference for package 'mongodb.driver' into project 'demo/demo/demo.csproj'.
info : Committing restore...
info : Writing assets file to disk. Path: demo/demo/obj/project.assets.json
log  : Restored /demo/demo/demo.csproj (in 6.01 sec).

項目準備完成。

看一下目錄結構:

% tree .
.
├── demo
│   ├── Program.cs
│   ├── demo.csproj
│   └── obj
│       ├── demo.csproj.nuget.dgspec.json
│       ├── demo.csproj.nuget.g.props
│       ├── demo.csproj.nuget.g.targets
│       ├── project.assets.json
│       └── project.nuget.cache
└── demo.sln

mongodb.driver是MongoDB官方的數據庫SDK,從Nuget上安裝即可。

三、Demo準備工作

創建數據映射的模型類CollectionModel.cs,現在是個空類,後面所有的數據映射相關內容會在這個類進行說明:

public class CollectionModel
{

}

並修改Program.cs,準備Demo方法,以及連接數據庫:

class Program
{

    private const string MongoDBConnection = "mongodb://localhost:27031/admin";

    private static IMongoClient _client = new MongoClient(MongoDBConnection);
    private static IMongoDatabase _database = _client.GetDatabase("Test");
    private static IMongoCollection<CollectionModel> _collection = _database.GetCollection<CollectionModel>("TestCollection");

    static async Task Main(string[] args)
    
{
        await Demo();
        Console.ReadKey();
    }

    private static async Task Demo()
    
{
    }
}

四、字段映射

從上面的代碼中,我們看到,在生成Collection對象時,用到了CollectionModel

IMongoDatabase _database = _client.GetDatabase("Test");
IMongoCollection<CollectionModel> _collection = _database.GetCollection<CollectionModel>("TestCollection");

這兩行,其實就完成了一個映射的工作:把MongoDB中,Test數據庫下,TestCollection數據集(就是SQL中的數據表),映射到CollectionModel這個數據類中。換句話說,就是用CollectionModel這個類,來完成對數據集TestCollection的所有操作。

保持CollectionModel為空,我們往數據庫寫入一行數據:

private static async Task Demo()
{
    CollectionModel new_item = new CollectionModel();
    await _collection.InsertOneAsync(new_item);
}

執行,看一下寫入的數據:


    "_id" : ObjectId("5ef1d8325327fd4340425ac9")
}

OK,我們已經寫進去一條數據了。因為映射類是空的,所以寫入的數據,也只有_id一行內容。

但是,為什麼會有一個_id呢?

1. ID字段

MongoDB數據集中存放的數據,稱之為文檔(Document)。每個文檔在存放時,都需要有一個ID,而這個ID的名稱,固定叫_id

當我們建立映射時,如果給出_id字段,則MongoDB會採用這個ID做為這個文檔的ID,如果不給出,MongoDB會自動添加一個_id字段。

例如:

public class CollectionModel
{

    public ObjectId _id { get; set; }
    public string title { get; set; }
    public string content { get; set; }
}

public class CollectionModel
{

    public string title { get; set; }
    public string content { get; set; }
}

在使用上是完全一樣的。唯一的區別是,如果映射類中不寫_id,則MongoDB自動添加_id時,會用ObjectId作為這個字段的數據類型。

ObjectId是一個全局唯一的數據。

當然,MongoDB允許使用其它類型的數據作為ID,例如:stringintlongGUID等,但這就需要你自己去保證這些數據不超限並且唯一。

例如,我們可以寫成:

public class CollectionModel
{

    public long _id { get; set; }
    public string title { get; set; }
    public string content { get; set; }
}

我們也可以在類中修改_id名稱為別的內容,但需要加一個描述屬性BsonId

public class CollectionModel
{

    [BsonId]
    public ObjectId topic_id { get; set; }
    public string title { get; set; }
    public string content { get; set; }
}

這兒特別要注意:BsonId屬性會告訴映射,topic_id就是這個文檔數據的ID。MongoDB在保存時,會將這個topic_id轉成_id保存到數據集中。

在MongoDB數據集中,ID字段的名稱固定叫_id。為了代碼的閱讀方便,可以在類中改為別的名稱,但這不會影響MongoDB中存放的ID名稱。

修改Demo代碼:

private static async Task Demo()
{
    CollectionModel new_item = new CollectionModel()
    {
        title = "Demo",
        content = "Demo content",
    };
    await _collection.InsertOneAsync(new_item);
}

跑一下Demo,看看保存的結果:


    "_id" : ObjectId("5ef1e1b1bc1e18086afe3183"), 
    "title" : "Demo"
    "content" : "Demo content"
}

2. 簡單字段

就是常規的數據字段,直接寫就成。

public class CollectionModel
{

    [BsonId]
    public ObjectId topic_id { get; set; }
    public string title { get; set; }
    public string content { get; set; }
    public int favor { get; set; }
}

保存后的數據:


    "_id" : ObjectId("5ef1e9caa9d16208de2962bb"), 
    "title" : "Demo"
    "content" : "Demo content"
    "favor" : NumberInt(100)
}

3. 一個的特殊的類型 – Decimal

說Decimal特殊,是因為MongoDB在早期,是不支持Decimal的。直到MongoDB v3.4開始,數據庫才正式支持Decimal。

所以,如果使用的是v3.4以後的版本,可以直接使用,而如果是以前的版本,需要用以下的方式:

[BsonRepresentation(BsonType.Double, AllowTruncation = true)]
public decimal price { get; set; }

其實就是把Decimal通過映射,轉為Double存儲。

4. 類字段

把類作為一個數據集的一個字段。這是MongoDB作為文檔NoSQL數據庫的特色。這樣可以很方便的把相關的數據組織到一條記錄中,方便展示時的查詢。

我們在項目中添加兩個類ContactAuthor

public class Contact
{

    public string mobile { get; set; }
}
public class Author
{

    public string name { get; set; }
    public List<Contact> contacts { get; set; }
}

然後,把Author加到CollectionModel中:

public class CollectionModel
{

    [BsonId]
    public ObjectId topic_id { get; set; }
    public string title { get; set; }
    public string content { get; set; }
    public int favor { get; set; }
    public Author author { get; set; }
}

嗯,開始變得有點複雜了。

完善Demo代碼:

private static async Task Demo()
{
    CollectionModel new_item = new CollectionModel()
    {
        title = "Demo",
        content = "Demo content",
        favor = 100,
        author = new Author
        {
            name = "WangPlus",
            contacts = new List<Contact>(),
        }
    };

    Contact contact_item1 = new Contact()
    {
        mobile = "13800000000",
    };
    Contact contact_item2 = new Contact()
    {
        mobile = "13811111111",
    };
    new_item.author.contacts.Add(contact_item1);
    new_item.author.contacts.Add(contact_item2);

    await _collection.InsertOneAsync(new_item);
}

保存的數據是這樣的:


    "_id" : ObjectId("5ef1e635ce129908a22dfb5e"), 
    "title" : "Demo"
    "content" : "Demo content"
    "favor" : NumberInt(100),
    "author" : {
        "name" : "WangPlus"
        "contacts" : [
            {
                "mobile" : "13800000000"
            }, 
            {
                "mobile" : "13811111111"
            }
        ]
    }
}

這樣的數據結構,用着不要太爽!

5. 枚舉字段

枚舉字段在使用時,跟類字段相似。

創建一個枚舉TagEnumeration

public enum TagEnumeration
{
    CSharp = 1,
    Python = 2,
}

加到CollectionModel中:

public class CollectionModel
{

    [BsonId]
    public ObjectId topic_id { get; set; }
    public string title { get; set; }
    public string content { get; set; }
    public int favor { get; set; }
    public Author author { get; set; }
    public TagEnumeration tag { get; set; }
}

修改Demo代碼:

private static async Task Demo()
{
    CollectionModel new_item = new CollectionModel()
    {
        title = "Demo",
        content = "Demo content",
        favor = 100,
        author = new Author
        {
            name = "WangPlus",
            contacts = new List<Contact>(),
        },
        tag = TagEnumeration.CSharp,
    };
    /* 後邊代碼略過 */
}

運行后看數據:


    "_id" : ObjectId("5ef1eb87cbb6b109031fcc31"), 
    "title" : "Demo"
    "content" : "Demo content"
    "favor" : NumberInt(100), 
    "author" : {
        "name" : "WangPlus"
        "contacts" : [
            {
                "mobile" : "13800000000"
            }, 
            {
                "mobile" : "13811111111"
            }
        ]
    }, 
    "tag" : NumberInt(1)
}

在這裏,tag保存了枚舉的值。

我們也可以保存枚舉的字符串。只要在CollectionModel中,tag聲明上加個屬性:

public class CollectionModel
{

    [BsonId]
    public ObjectId topic_id { get; set; }
    public string title { get; set; }
    public string content { get; set; }
    public int favor { get; set; }
    public Author author { get; set; }
    [BsonRepresentation(BsonType.String)]
    public TagEnumeration tag { get; set; }
}

數據會變成:


    "_id" : ObjectId("5ef1ec448f1d540919d15904"), 
    "title" : "Demo"
    "content" : "Demo content"
    "favor" : NumberInt(100), 
    "author" : {
        "name" : "WangPlus"
        "contacts" : [
            {
                "mobile" : "13800000000"
            }, 
            {
                "mobile" : "13811111111"
            }
        ]
    }, 
    "tag" : "CSharp"
}

6. 日期字段

日期字段會稍微有點坑。

這個坑其實並不源於MongoDB,而是源於C#的DateTime類。我們知道,時間根據時區不同,時間也不同。而DateTime並不準確描述時區的時間。

我們先在CollectionModel中增加一個時間字段:

public class CollectionModel
{

    [BsonId]
    public ObjectId topic_id { get; set; }
    public string title { get; set; }
    public string content { get; set; }
    public int favor { get; set; }
    public Author author { get; set; }
    [BsonRepresentation(BsonType.String)]
    public TagEnumeration tag { get; set; }
    public DateTime post_time { get; set; }
}

修改Demo:

private static async Task Demo()
{
    CollectionModel new_item = new CollectionModel()
    {
        /* 前邊代碼略過 */
        post_time = DateTime.Now, /* 2020-06-23T20:12:40.463+0000 */
    };
    /* 後邊代碼略過 */
}

運行看數據:


    "_id" : ObjectId("5ef1f1b9a75023095e995d9f"), 
    "title" : "Demo"
    "content" : "Demo content"
    "favor" : NumberInt(100), 
    "author" : {
        "name" : "WangPlus"
        "contacts" : [
            {
                "mobile" : "13800000000"
            }, 
            {
                "mobile" : "13811111111"
            }
        ]
    }, 
    "tag" : "CSharp"
    "post_time" : ISODate("2020-06-23T12:12:40.463+0000")
}

對比代碼時間和數據時間,會發現這兩個時間差了8小時 – 正好的中國的時區時間。

MongoDB規定,在數據集中存儲時間時,只會保存UTC時間。

如果只是保存(像上邊這樣),或者查詢時使用時間作為條件(例如查詢post_time < DateTime.Now的數據)時,是可以使用的,不會出現問題。

但是,如果是查詢結果中有時間字段,那這個字段,會被DateTime默認設置為DateTimeKind.Unspecified類型。而這個類型,是無時區信息的,輸出显示時,會造成混亂。

為了避免這種情況,在進行時間字段的映射時,需要加上屬性:

[BsonDateTimeOptions(Kind = DateTimeKind.Local)]
public DateTime post_time { get; set; }

這樣做,會強制DateTime類型的字段為DateTimeKind.Local類型。這時候,從显示到使用就正確了。

但是,別高興的太早,這兒還有一個但是。

這個但是是這樣的:數據集中存放的是UTC時間,跟我們正常的時間有8小時時差,如果我們需要按日統計,比方每天的銷售額/點擊量,怎麼搞?上面的方式,解決不了。

當然,基於MongoDB自由的字段處理,可以把需要統計的字段,按年月日時分秒拆開存放,像下面這樣的:

class Post_Time
{

    public int year { get; set; }
    public int month { get; set; }
    public int day { get; set; }
    public int hour { get; set; }
    public int minute { get; set; }
    public int second { get; set; }
}

能解決,但是Low哭了有沒有?

下面,終極方案來了。它就是:改寫MongoDB中對於DateTime字段的序列化類。噹噹當~~~

先創建一個類MyDateTimeSerializer

public class MyDateTimeSerializer : DateTimeSerializer
{
    public override DateTime Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
    
{
        var obj = base.Deserialize(context, args);
        return new DateTime(obj.Ticks, DateTimeKind.Unspecified);
    }
    public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, DateTime value)
    
{
        var utcValue = new DateTime(value.Ticks, DateTimeKind.Utc);
        base.Serialize(context, args, utcValue);
    }
}

代碼簡單,一看就懂。

注意,使用這個方法,上邊那個對於時間加的屬性[BsonDateTimeOptions(Kind = DateTimeKind.Local)]一定不要添加,要不然就等着哭吧:P

創建完了,怎麼用?

如果你只想對某個特定映射的特定字段使用,比方只對CollectionModelpost_time字段來使用,可以這麼寫:

[BsonSerializer(typeof(MyDateTimeSerializer))]
public DateTime post_time { get; set; }

或者全局使用:

BsonSerializer.RegisterSerializer(typeof(DateTime), new MongoDBDateTimeSerializer());

BsonSerializer是MongoDB.Driver的全局對象。所以這個代碼,可以放到使用數據庫前的任何地方。例如在Demo中,我放在Main里了:

static async Task Main(string[] args)
{
    BsonSerializer.RegisterSerializer(typeof(DateTime), new MyDateTimeSerializer());

    await Demo();
    Console.ReadKey();
}

這回看數據,數據集中的post_time跟當前時間显示完全一樣了,你統計,你分組,可以隨便霍霍了。

7. Dictionary字段

這個需求很奇怪。我們希望在一個Key-Value的文檔中,保存一個Key-Value的數據。但這個需求又是真實存在的,比方保存一個用戶的標籤和標籤對應的命中次數。

數據聲明很簡單:

public Dictionary<stringint> extra_info { get; set; }

MongoDB定義了三種保存屬性:DocumentArrayOfDocumentsArrayOfArrays,默認是Document

屬性寫法是這樣的:

[BsonDictionaryOptions(DictionaryRepresentation.ArrayOfDocuments)]
public Dictionary<stringint> extra_info { get; set; }

這三種屬性下,保存在數據集中的數據結構有區別。

DictionaryRepresentation.Document


    "extra_info" : {
        "type" : NumberInt(1), 
        "mode" : NumberInt(2)
    }
}

DictionaryRepresentation.ArrayOfDocuments


    "extra_info" : [
        {
            "k" : "type"
            "v" : NumberInt(1)
        }, 
        {
            "k" : "mode"
            "v" : NumberInt(2)
        }
    ]
}

DictionaryRepresentation.ArrayOfArrays


    "extra_info" : [
        [
            "type"
            NumberInt(1)
        ], 
        [
            "mode"
            NumberInt(2)
        ]
    ]
}

這三種方式,從數據保存上並沒有什麼區別,但從查詢來講,如果這個字段需要進行查詢,那三種方式區別很大。

如果採用BsonDocument方式查詢,DictionaryRepresentation.Document無疑是寫着最方便的。

如果用Builder方式查詢,DictionaryRepresentation.ArrayOfDocuments是最容易寫的。

DictionaryRepresentation.ArrayOfArrays就算了。數組套數組,查詢條件寫死人。

我自己在使用時,多數情況用DictionaryRepresentation.ArrayOfDocuments

五、其它映射屬性

上一章介紹了數據映射的完整內容。除了這些內容,MongoDB還給出了一些映射屬性,供大家看心情使用。

1. BsonElement屬性

這個屬性是用來改數據集中的字段名稱用的。

看代碼:

[BsonElement("pt")]
public DateTime post_time { get; set; }

在不加BsonElement的情況下,通過數據映射寫到數據集中的文檔,字段名就是變量名,上面這個例子,字段名就是post_time

加上BsonElement后,數據集中的字段名會變為pt

2. BsonDefaultValue屬性

看名稱就知道,這是用來設置字段的默認值的。

看代碼:

[BsonDefaultValue("This is a default title")]
public string title { get; set; }

當寫入的時候,如果映射中不傳入值,則數據庫會把這個默認值存到數據集中。

3. BsonRepresentation屬性

這個屬性是用來在映射類中的數據類型和數據集中的數據類型做轉換的。

看代碼:

[BsonRepresentation(BsonType.String)]
public int favor { get; set; }

這段代表表示,在映射類中,favor字段是int類型的,而存到數據集中,會保存為string類型。

前邊Decimal轉換和枚舉轉換,就是用的這個屬性。

4. BsonIgnore屬性

這個屬性用來忽略某些字段。忽略的意思是:映射類中某些字段,不希望被保存到數據集中。

看代碼:

[BsonIgnore]
public string ignore_string { get; set; }

這樣,在保存數據時,字段ignore_string就不會被保存到數據集中。

六、總結

數據映射本身沒什麼新鮮的內容,但在MongoDB中,如果用好了映射,開發過程從效率到爽的程度,都不是SQL可以相比的。正所謂:

一入Mongo深似海,從此SQL是路人。

謝謝大家!

(全文完)

本文的配套代碼在https://github.com/humornif/Demo-Code/tree/master/0015/demo

 

 

微信公眾號:老王Plus

掃描二維碼,關注個人公眾號,可以第一時間得到最新的個人文章和內容推送

本文版權歸作者所有,轉載請保留此聲明和原文鏈接

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

【其他文章推薦】

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

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

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

Tesla Model 3 有對手了!LG Chem 研發長程電動車電池

Tesla 又有新對手了,南韓 LG Chem 目前也在開發最新電池技術,且無論航程或售價均足以與特斯拉研發中的 Model 3 匹敵。   Model S 是目前市面上唯一航程可以達 200 英哩的電動車,但即使是最低售價也高達 7.1 萬美元。特斯拉計畫在 2017 年推出 Model 3,售價將壓低至每輛 3.5 萬美元,但售價仍然不敵 LG Chem 正將推出的電動車。   LG Chem 位於美國的研究單位主管 Prabhakar Patil 證實,正在開發續航力 200 英哩的電池,目前已有幾家汽車業者表示對該項技術有興趣,產品最快於 2017 年就可上市,售價將介於 3 至 3.5 萬美元間。   Patil 雖然沒有明講,但外界猜想對該項技術有興趣的汽車業者,一定包含通用汽車。LG Chem 目前已是通用油電混合動力車 Volt 的電池供應商,且通用之前表示,將推出續航力 200 英哩的電動車,起價最低 3 萬美元,符合 Patil 設定的條件。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

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

挽救電動車產業 印度政府再次開啟投資

    印度政府日前通過一項 23 億美元的電動車產業振興計劃──2020 國家電動車行動計劃(NEMMP 2020 plan),打算重新帶起終止補貼後明顯下滑的電動車銷量。   印度政府過去對電動車的銷售補貼相當成功,全盛時期銷售量甚至超過 10 萬輛,但當新暨再生能源部 MNRE 在 2012 年 3 月停止補貼後,銷售量開始下滑,到了 2013 年,只剩 2.1 萬輛。幾年前,印度有 65 家電動車製造商,現在只剩 10家還在營運。   NEMMP 計劃目標是在 2020 年有 6 到 7 百萬輛電動車上路,而其中 4 到 5 百萬輛為二輪車,若達到該銷量數字,還能節省超過 200 萬噸燃料的使用。   雖然大多數電動車公司在過去兩年來已停止營運,但假如 NEMMP 2020 可在接下來的 3 或 4 個月間實施,預期印度電動車產業仍可復甦。     (圖片來源:)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

※幫你省時又省力,新北清潔一流服務好口碑

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