.NET企業級項目中碰到的國際化成績息爭決辦法。本站提示廣大學習愛好者:(.NET企業級項目中碰到的國際化成績息爭決辦法)文章只能為提供參考,不一定能成為您想要的結果。以下是.NET企業級項目中碰到的國際化成績息爭決辦法正文
企業級的體系和我們平凡桌面、手機上運轉的軟件有著很主要的差別,個中比擬主要的一點就是情況(包含第三方的體系的分歧接口和各體系的分歧版本、平安性、數據等)比擬龐雜,所以豈論在產物保護照樣安排進程中要斟酌的身分都有許多。
偶們的體系的最新版本比來在南非上線,碰到了很多成績。昨天早晨本屌和同事處理了一個比擬“嚴重”的成績(由於影響到體系的焦點功效),發明是由“國際化”的成績所惹起。固然找到成績的緣由其實不艱苦,然則要在客戶的情況處理照樣費了很多精神(由於不克不及從產物中去修復這個成績,即便給一個補釘依照公司的流程至多也須要半個月)。之前我也幫運營團隊處理過一部門這類成績,在任務中也看到過很多,那末這裡一並記載一下吧。願望年夜家在今後的開辟進程中多加留意。
1、數據庫的日期時光格局成績
這應當是簡直一切的體系在安排到分歧國度的客戶情況中都邑碰到的成績,卡逝世很多法式,消耗有數人力。是以這是在數據存儲時必定要非分特別留意的處所。這裡以SQL Server為例。
(1)數據表中的時光格局
關於日期和時光,SQL Server中供給了datetime、datetime2、datetimeoffset、date、time這幾品種型。我小我推舉只應用datetime和datetime2,由於這兩個類型與.NET的法式兼容性最好(都可以直接對應成DateTime)。在.NET中也供給了DateTimeOffset類型,然則在一些比擬老的說話中其實不支撐,為了兼容應用datetime就足夠了。應用其他類型會牽扯到轉型的成績,應盡可能防止以避免發生額定的成績。
須要留意的是,datetime支撐的最小時光是1753年,是以要防止在法式中直接應用DateTime.MinValue傳到數據庫中,不然會發生out-of-range的毛病。
在一些陳舊的體系中,會應用int類型來存儲日期,由於int會占4個字節,所以二者相互轉型是支撐的,應用convert停止轉型便可,普通不會有甚麼成績。
(2)時區的成績
建議在數據庫中一切數據都以UTC時光保留,在顯示的時刻客戶端依據當地的時區停止處置,是以在存儲進程中必定要留意把時光轉換成UTC時光。假如不保留UTC時光,今後在顯示和遷徙的進程中不免會產生凌亂,所以盡可能不要應用本地的時光來保留,即便體系只在一個地域應用。
在設計SSRS報表時,也要留意時區的成績。因為數據庫中存儲的是UTC時光,所以在挪用Reporting Service辦事的時刻應把用戶輸出的時光轉換成UTC時光後傳給報表辦事。在顯示的應用,應用以下代碼格局化單位格:
=System.TimeZone.CurrentTimeZone.ToLocalTime(Fields!DateTime.Value)
不要在報表的存儲進程中對時區停止處置。固然很便利且直不雅,然則當你的體系有幾十張報表而且由分歧的人開辟的時刻就會發明凌亂的局勢。
(3)報表的時光格局
這是微軟斟酌不周招致的成績。因為報表辦事是一個Web Service,所以在現實的運轉情況中不管是日期控件照樣報表中輸入日期的單位格都是應用IE的說話設置,而非Windows的地域。然則在設計報表的時刻它又是應用的Windows的日期格局。假如客戶有這類需求,那末我們至多修正IE的設置。
然則最坑人的處所是要修正的是數據庫地點的辦事器的IE設置,如許做會影響到一切的客戶端,那末這也沒方法。有時刻如許做了也不見得好使,仿佛又與數據庫的銜接用戶的默許說話有關,所以這類成績我們普通不會花太多時光去修改。
好在這只是一個顯示的成績,其實不影響體系的正常功效,也不會失足,所以很少會聽到客戶埋怨這類成績。
(4)運用法式與數據庫之間的日期時光傳遞
我們起首來看上面一段代碼:
DateTime creation_time = DateTime.Now.AddDays(-1);
string sql = string.Format("SELECT * FROM [User] WHERE [CreationTime] < '{0}'", creation_time);
在數據庫中CreationTime是一個datetime類型,而我們單引號之間的內容是一個字符串,此時SQL Server會主動對字符串停止轉型,是以相當於隱式完成了一個convert語句,把{0}處的內容轉型成datetime類型。{0}處現實上是一個字符串,在string.Format辦法裡會隱式挪用creation_time的ToString辦法,這個辦法所前往的日期時光的格局會根據以後線程所處的說話情況來肯定。
在調試的進程中仿佛不會發明成績,然則當產物給一個國外的客戶時,就很有能夠發明SQL Server拋出out-of-range的毛病,年夜意是將varchar轉型成datetime類型時越界。招致這個成績的緣由簡略來講就是由於客戶真個日期格局與數據庫的設置紛歧致(更具體的上面會解釋),此時體系懂得的年、月、日這幾個字段所處的地位不在准確的處所上,是以有時會越出月或許日的界限。
防止這個成績的基本處理方法是在拜訪數據庫的時刻只應用參數情勢,基本不該該湧現非參數的拼接情勢(同時可以免SQL注入和其他bug),假如你在做代碼審查,看到下面這類代碼應當武斷reject歸去。應用參數情勢的代碼年夜概是如許:
SqlCommand command = new SqlCommand("SELECT * FROM [User] WHERE [CreationTime] < @creationTime", connection);
command.Parameters.AddWithValue("creationTime", creation_time);
如許,就會防止將DateTime類型轉型成字符串,直接應用SQL Server兼容的類型拜訪,就防止了格局紛歧致招致的轉型掉敗(或許轉換成毛病的成果)。
我們在體系日記中發明某個模塊頻仍拋出越界的毛病,就是由於沒有應用參數的情勢停止拜訪。與此對應的體系其他模塊都沒有這個類型的毛病。
(5)SQL語句中的日期格局轉型成績
這就是昨天做支撐進程中碰到的一個異常詭異的成績。在我們的產物的SQL語句中有一段相似上面如許的代碼:
DECLARE @startTime VARCHAR(100)
SELECT @startTime = CONVERT(VARCHAR, timeIn, 23), @endTime = CONVERT(VARCHAR, timeIn + 1, 23) FROM...
我們不議論這段代碼寫得如何,我們只關懷為何會失足。在客戶現場中,客戶的描寫是:“在前一天體系照樣好的,然則在換班後(我們的體系履行換班操作後日期會往後加一天),體系就沒法操作。”
剖析日記發明拋出的錯是日期轉型越界,失足的代碼片斷應當就是在這個處所,那末為何會失足呢?
經由一番折騰,發明了SQL Server的一個隱蔽炸彈(還好有測試的同事在其他體系見過這個成績,不然也不會往這個偏向去想):關於每個有權限拜訪數據庫的用戶(包含Windows登錄方法和SQL Server登錄方法),在用戶的屬性中都有一個稱之為“默許說話”的屬性。在我們公司的產物中,根本只會在British English和English這二者之間停止選擇。
這個屬性會影響到SQL語句中牽扯到日期與字符串轉型時的處置。是以,在SQL語句直達型是與這個“默許說話”有關系的。同時,還會與客戶端(拜訪數據庫的辦事地點的機械)的當地時光格局有關。
客戶的數據庫登錄用戶的默許說話選擇是British English,而數據庫辦事器和客戶真個時光格局設置裝備擺設都是南非的時光格局。這類紛歧致能夠會招致一些弗成預知的成績。我們要用戶把默許說話給成English(留意:1. 改了以後要重啟SQL Server的辦事使之失效; 2. 特殊留意修正的是拜訪數據庫的用戶,每一個用戶都有這個屬性),然後成績莫明其妙地就處理了。
然則後來我們在試驗情況中依照這類設置裝備擺設都沒法重現這個成績,我認為有能夠是客戶修正了一些日期格局的緣由所招致。
明天早上我做了一個試驗,把默許說話設置成British English,而客戶真個時光格局是中國的格局,此時下面的timeIn + 1獲得的成果居然是月份加1!如許就使我們豁然開朗:難怪客戶在前一天沒有成績,而過了一天成績才湧現。由於湧現成績的時光是6月12日,因為某種特別的設置裝備擺設(我們至今照樣沒測驗考試出是何種格局),體系把12當做是月份+1,獲得13就越界了,而應用6月11日就不會出越界的成績,但成果是毛病的。
這不言而喻是微軟的一個bug,不管何種設置裝備擺設,都不該該轉型失足,即便是月份+1,應當會進到年的字段去。
下面這段SQL語句的准確寫法應當是應用DATEDIFF停止日期運算,並且更不該該湧現第三個參數“23”這類直接指定格局的輸入。
(6)結論
經由過程下面的剖析,有一點可以確定的是:在編碼進程中,除非僅用於顯示,不然必需要防止日期和字符串的互相轉換!
日期轉型成字符串應當只在客戶端顯示的最初一步停止轉型,而字符串轉換成時光應當只在運用法式中應用DateTime供給的與時光格局相干的辦法停止完成。必定要防止應用字符串停止日期和時光的運算,即便是在SQL語句中也是如斯。
如許應當就可以防止由時光格局所招致的成績,並且也不會碰著(5)外面那種詭異的成績:即便曉得成績出在哪,也不曉得怎樣樣設置才不會出這類成績。
下面是側重論述的與數據庫相干的日期格局成績,上面再提兩種我碰到的小成績。
2、Web Server的日期格局成績
微軟的.NET與JSON格局兼容得不是很好,我碰著了兩個成績。
(1)JSON解析DateTime格局
JSON的時光格局和.NET完整紛歧樣。DateTime說白了就是一個8字節的二進制位,而JSON是以字符串來表現日期的。JSON的日期相似於“/Date(1242357713797+0800)/”。
一種比擬懶的處置方法是直接應用eval語句讓體系主動解析,這類辦法的後果比擬好(機能就不曉得了),轉換後可以依照本地的日期格局停止輸入,比擬便利。
(2).NET解析JSON的日期格局
從JSON的字符串停止解析時,除把中央的數字停止運算外(1970年1月1日+數字*10000作為DateTime類型的Ticks),還要斟酌前面的時區。處置比擬費事。
固然可以想一個偷懶的方法,那就是直接傳字符串而欠亨過JSON的日期格局:既然客戶端顯示的是當地時光格局,那末假如辦事器的時光格局也是一樣的便可以直接用DateTime的Parse就完事了。這類方法合適偷懶人士(好比我),臨時也沒有發明甚麼成績,除接口類型不太悅目之外。
(3)微軟的bug
又是時光格局激發的bug!將Web Server的格局設置為南非的今後,發明一切將DateTime序列化成JSON格局的代碼全都報錯:日期時光轉換越界。在挪用客棧中全體都是體系的代碼,將時光格局設置成美國後成績消逝,是以可以確定這是微軟的一個bug(在4.0版本的.NET Framework中,不曉得能否修改了)。
那末只能讓客戶應用美國的時光格局對付著用了,我們也只能鄙人一個版本處理這個成績。計劃很簡略,直接轉換成字符串傳給客戶端就好了,如許客戶端也不用寫代碼去解析DateTime格局。
因為.NET自己就和JSON的兼容出缺陷,所以應用上述方法也並沒有不當。假設客戶有國際化的需求,其實可以界說一種中央格局(好比傳輸的進程中都以中國的格局為准),客戶端和辦事器依照這個中央格局停止解析,就可以防止由格局不配套發生的轉型毛病。
3、文本的國際化處置
這個說起來很簡略,只需轉變編碼習氣便可以了。在一個資本文件(好比XML格局)中起首以默許說話(好比中文)存儲一切須要顯示出來的字符串和對應的稱號(稱號最好按功效+內容來定名,不要用數字編號,如許可以免在有10000個記載的資本文件中不肯定能否有某個須要的字符串的為難),然後依葫蘆畫瓢制造其他的說話包。
在運用法式中,推舉應用MVVM或許MVP形式停止數據綁定,令其作為靜態資本定位到一個中央的靜態類,由這個類決議應用哪一種說話包。如許做的利益是我們不須要寫代碼去初始化界面上各類文本標簽(試想我們在窗體加載的應用寫一年夜堆語句去設置Label或許Menu的Text?)。應用數據綁定,體系會在須要顯示這個資本的時刻拜訪靜態類去取文本內容。
微軟曾經斟酌到這個成績並把它做得很完美(除一個小bug外我們臨時沒發明其他的bug,被下面的兩個微軟的bug弄怕了吧?這個小bug影響不年夜在此不做解釋),你可以參考這裡:http://msdn.microsoft.com/en-us/kb/ms745650(v=vs.80)
如許做的利益是:在法式編譯後會生成幾個文件夾,這些文件夾案語言的名字來定名(好比zh-cn表現簡體中文),文件夾中有資本文件的dll。在體系啟動的時刻可讓用戶選一種說話,然後設置CultureInfo,如許體系就會主動去響應的文件夾取這個說話包裡的字符串。還有一個很人道化的功效,就是假如在對應說話的資本文件中沒有找到這個字符串,就會從默許的說話包中掏出並顯示。(好比我們產物有繁體中文的說話包,假如有些處所沒有停止翻譯,在繁體中文的說話包中就不會有這個字符串,此時體系會顯示出英文說話包中的字符串。)
以上是我碰到的處置國際化成績的一些典范例子,假如你碰到了一些如許的成績,迎接指出並配合商量。