揭开.NET 2.0配置之谜(一)
此文是譯文,原文是Jon Rista,Unraveling the Mysteries of .NET 2.0 Configuration,由于這篇文章比較長,所以我就分為幾部分來翻譯。
以前沒有翻譯過外文,看英文文章倒是也看得懂,現(xiàn)在才發(fā)現(xiàn)要翻譯出來,而且質(zhì)量要過關(guān)還是挺難的(對(duì)我來說)。若翻譯有不當(dāng)之處,請不吝賜教,以免此譯文誤導(dǎo)他人,在此謝過。不廢話了,let's start!
致謝:560889223,他幫忙解釋了population的翻譯問題!
引言
.NET的美妙特點(diǎn)之一是它的XML配置功能。在.NET 1.x時(shí)代,常見的應(yīng)用程設(shè)置、數(shù)據(jù)庫連接字符串、ASP.NET Web服務(wù)器配置和基本的自定義配置數(shù)據(jù)可以存儲(chǔ)在.config文件中。自定義配置節(jié)可以使用一些基本自定義結(jié)構(gòu),允許少數(shù)幾種信息存儲(chǔ)在.config文件中。然而更復(fù)雜的配置,最常見的實(shí)現(xiàn)是自定義XML結(jié)構(gòu)和自定義解析代碼。盡管有多種不同性能的方法完成同樣的事情,這種代碼將變得相當(dāng)復(fù)雜。
隨著.NET 2.0,自己編寫(可能很復(fù)雜、低性能、繁瑣)代碼來管理自定義XML配置結(jié)構(gòu)的時(shí)代已經(jīng)結(jié)束了。.NET 2.0內(nèi)置的XML配置子系統(tǒng)自定義配置能力已經(jīng)大大革新,擁有一些非常有用的和節(jié)省時(shí)間的功能。幾乎任何XML配置結(jié)構(gòu)你可能需要相對(duì)更少的工作且更容易。此外,反序列化.config中的XML總是可以重載的。這使得任何XML結(jié)構(gòu)可以不失去.NET 2.0配置支持的其它高級(jí)功能。
該系列
這是該系列的第一篇文章。開始使用.NET 2.0所要求的所有核心概念和知識(shí)點(diǎn)都在這第一篇文章中提供。但是,.NET 2.0可用的配置框架是廣泛的。從本文可以看到,它有許多功能和一些隱藏功能,可以填補(bǔ)框架的空白和疏漏。要繼續(xù)學(xué)習(xí)有關(guān).NET 2.0的配置框架,其它文章可以在以下鏈接找到:
寫此系列的緣由(The mystery)
在略微超過一年的時(shí)間里我一直在使用.NET 2.0,可悲的是,我花了許多不眠之夜去修修補(bǔ)補(bǔ)自定義配置模型。自定義XML配置處理不斷持續(xù)的性能下降,每個(gè)應(yīng)用程序需要一個(gè)新的模型,這依賴于配置是如何存儲(chǔ)的以及如何需要訪問(從哪里)。直到8個(gè)月前左右,我用Reflector鑿開.NET 2.0框架的一些程序集,我遇到一個(gè)奇妙的小類:System.Configuration.ConfigurationSection。挖掘更深一點(diǎn),我發(fā)現(xiàn)為數(shù)眾多的框架類都繼承自ConfigurationSection和一些其它的類。自那時(shí)起,我花了幾個(gè)不眠之夜吸收盡可能多的.NET 2.0的新配置特性。
我只想說,在互聯(lián)網(wǎng)上關(guān)于自定義配置節(jié)的真正能力的資料非常少。通過Internet搜索、數(shù)小時(shí)的用Reflector研究框架代碼、持續(xù)的實(shí)驗(yàn)和用自定義配置節(jié),我終于知道了我需要知道的。這是生命的救星。最終,能輕松容易地創(chuàng)建和管理自定義XML配置、簡單易用、易于管理并且性能優(yōu)越……我將在與大家分享這個(gè)圣杯。我要求的唯一回報(bào)就是讓任何人和你認(rèn)識(shí)的用.config文件的人——他們應(yīng)該更好地寫.NET代碼,看到這篇文章從而節(jié)省通過數(shù)個(gè)月的挖掘代碼、互聯(lián)網(wǎng)論壇和博客來學(xué)習(xí)它。
有關(guān)配置的主題
本文的目標(biāo)是覆蓋所有的.NET 2.0的配置,以及揭露一些更嚴(yán)密保護(hù)的秘密(標(biāo)注:無正式文檔的,無事實(shí)證明的…種種原因),可以節(jié)省你大量的時(shí)間和麻煩。首先,我們總覽核心命名空間,它揭露了所有的自定義配置瘋狂之處。然后,我們將進(jìn)入具體執(zhí)行和使用自定義配置。討論的議題如下:
1.附錄A:配置結(jié)構(gòu)的級(jí)聯(lián)
2.附錄B:包含外部配置文件
1、命名空間:System.Configuration
新的.NET 2.0的配置精華核心是System.Configuration命名空間。默認(rèn)情況下,當(dāng)引用了System.dll程序集,此命名空間是可用的。它包括.NET 1.1的所有功能,包括舊的ConfigurationSettings類(現(xiàn)在.NET 2.0不推薦使用了)。然而,為了獲得.NET 2.0的新特性,你必須添加對(duì)System.Configuration.dll程序集的引用。在這個(gè)程序集里你會(huì)發(fā)現(xiàn)新的配置系統(tǒng)的核心,ConfigurationManager靜態(tài)類。
ConfigurationManager類是一個(gè)全局的訪問一個(gè)應(yīng)用程序的配置的入口。由于類是靜態(tài)的,其所有的成員也是靜態(tài)的。這使得讀取如AppSettings,ConnectionStrings和自定義配置節(jié)輕而易舉。雖然這類似于ConfigurationSettings類它也提供一些能夠更加安全地訪問應(yīng)用程序的配置的新功能。這些新功能,也可以允許將配置設(shè)置保存到任何配置節(jié):自定義或其他的。
除了ConfigurationManager類是自定義配置節(jié)的生命線。下面列出的基類可以幫助你編寫自己的配置對(duì)象模型。還將有更多關(guān)于這方面的。除了一個(gè)基類集是一個(gè)校驗(yàn)集,可以用來確保你的自定義配置節(jié)的準(zhǔn)確性。此外,如果你的需要很簡單,只需要用一些預(yù)制的配置。
基本類型
- ConfigurationSection - 配置節(jié)基類
- ConfigurationSectionCollection - 配置節(jié)集合的基類
- ConfigurationSectionGroup - 配置節(jié)組的基類
- ConfigurationSectionGroupCollection - 配置節(jié)組集合的基類
- ConfigurationElement - 配置元素的基類
- ConfigurationElementCollection - 配置元素集合的基類
- ConfigurationConverterBase - 自定義轉(zhuǎn)換器基類*1
- ConfigurationValidatorBase - 自定義驗(yàn)證器基類*2
支持類型
- ConfigurationManager - 提供對(duì)客戶端應(yīng)用程序配置文件的訪問
- Configuration - 表示一個(gè)應(yīng)用程序的配置
- ConfigurationProperty - 表示屬性或配置元素的子元素
- ConfigurationPropertyAttribute - 以聲明方式指示.NET Framework,以實(shí)例化配置屬性
- ConfigurationPropertyCollection - 配置屬性的集合
- ConfigurationPropertyOptions - 指定要應(yīng)用于屬性的選項(xiàng)
驗(yàn)證類型
- CallbackValidator - 提供對(duì)對(duì)象的動(dòng)態(tài)驗(yàn)證
- CallbackValidatorAttribute - 指定用于代碼驗(yàn)證的CallbackValidator對(duì)象
- IntegerValidator - 對(duì)Int32值進(jìn)行驗(yàn)證
- IntegerValidatorAttribute - 以聲明的方式指示.NET Framework對(duì)配置屬性執(zhí)行整數(shù)驗(yàn)證
- LongValidator - 對(duì)Int64值進(jìn)行驗(yàn)證。
- LongValidatorAttribute - 以聲明的方式指示.NET Framework對(duì)配置屬性執(zhí)行長整型驗(yàn)證
- PositiveTimeSpanValidator - 對(duì)TimeSpan對(duì)象進(jìn)行驗(yàn)證。
- PositiveTimeSpanValidatorAttribute - 以聲明的方式指示.NET Framework對(duì)配置屬性執(zhí)行時(shí)間驗(yàn)證
- RegexStringValidator - 根據(jù)正則表達(dá)式提供的規(guī)則提供字符串驗(yàn)證
- RegexStringValidatorAttribute - 以聲明方式指示.NET Framework使用正則表達(dá)式在配置屬性中執(zhí)行字符串驗(yàn)證
- StringValidator - 對(duì)字符串進(jìn)行驗(yàn)證
- StringValidatorAttribute - 以聲明的方式指示.NET Framework對(duì)配置屬性執(zhí)行字符串驗(yàn)證
- SubclassTypeValidator - 驗(yàn)證一個(gè)對(duì)象是否是指定類型的派生類*3
- SubclassTypeValidatorAttribute - 以聲明方式指示.NET Framework對(duì)配置屬性執(zhí)行驗(yàn)證
- TimeSpanValidator - 對(duì)TimeSpan對(duì)象進(jìn)行驗(yàn)證
- TimeSpanValidatorAttribute - 以聲明的方式指示.NET Framework對(duì)配置屬性執(zhí)行時(shí)間驗(yàn)證
轉(zhuǎn)換器類型
- CommaDelimitedStringCollectionConverter - 將以逗號(hào)分隔的字符串值和CommaDelimitedStringCollection對(duì)象相互轉(zhuǎn)換
- GenericEnumConverter - 在字符串和枚舉類型之間進(jìn)行轉(zhuǎn)換
- InfiniteIntConverter - 在字符串和標(biāo)準(zhǔn)無限或整數(shù)值之間轉(zhuǎn)換
- InfiniteTimeSpanConverter - 在字符串和標(biāo)準(zhǔn)無限TimeSpan值之間轉(zhuǎn)換
- TimeSpanMinutesConverter - 轉(zhuǎn)換以分鐘表示的時(shí)間跨度
- TimeSpanMinutesOrInfiniteConverter - 轉(zhuǎn)換以分鐘表示(或作為標(biāo)準(zhǔn)的無限時(shí)間跨度)的TimeSpan
- TimeSpanSecondsConverter - 轉(zhuǎn)換以秒表示的時(shí)間跨度
- TimeSpanSecondsOrInfiniteConverter - 轉(zhuǎn)換以秒表示的TimeSpan,或?qū)⑵滢D(zhuǎn)換為標(biāo)準(zhǔn)的無限時(shí)間跨度
- TypeNameConverter - 在類型和字符串值之間轉(zhuǎn)換
- WhiteSpaceTrimStringConverter - 將字符串轉(zhuǎn)換為它的規(guī)范化格式
預(yù)制的配置節(jié)
- AppSettingsSection - 為<appSettings> 配置節(jié)提供配置系統(tǒng)支持
- ConnectionStringsSection - 提供對(duì)<connectionStrings>配置節(jié)的編程訪問
- ProtectedConfigurationSection - 提供對(duì)configProtectedData配置節(jié)的編程訪問
- IgnoreSection - 為不是由System.Configuration類型處理的配置節(jié)提供包裝類型定義
預(yù)制配置集合*4
- CommaDelimitedStringCollection - 與CommaDelimitedStringCollectionConverter結(jié)合使用(譯注:表示以逗號(hào)分隔的字符串元素的集合)
- KeyValueConfigurationCollection - 用于在配置節(jié)中配置鍵/值對(duì)(譯注:包含KeyValueConfigurationElement對(duì)象的集合)
- NameValueConfigurationCollection - 用于在配置節(jié)中配置名稱/值對(duì)(譯注:包含NameValueConfigurationElement對(duì)象的集合)
注意:
- *1自定義轉(zhuǎn)換器用于在XML文件中字符串表示與配置對(duì)象模型中強(qiáng)(譯注:natively,或者翻譯為“原生”?下同)類型之間轉(zhuǎn)換
- *2自定義驗(yàn)證器是用來驗(yàn)證在配置對(duì)象模型強(qiáng)類型數(shù)據(jù)的準(zhǔn)確性
- *3這關(guān)系到配置對(duì)象模型的概念,將在下一節(jié)討論
- *4這些常見的配置集合可用于自定義配置節(jié)
2、對(duì)象模型配置概念
在我們接下來創(chuàng)建一些自定義配置之前,有必要先來學(xué)習(xí)一些對(duì)象模型配置的概念。.NET 2.0配置系統(tǒng)最終提供了一組對(duì)象表示配置設(shè)置的結(jié)構(gòu)和強(qiáng)類型訪問配置數(shù)據(jù)。這與在XML文件中存儲(chǔ)和檢索配置的更多通用的方法相反,它們通常需要通過DOM或讀取流讀取值,通過DOM或者寫流將改變寫回文件。在我私人寫的更高級(jí)的配置系統(tǒng)中,包括一些緩存機(jī)制來加速讀取和寫配置值。創(chuàng)建一個(gè)高度可定制的配置文件,同時(shí)保持良好的性能一直是一個(gè)難點(diǎn)。
.NET 2.0的配置對(duì)象模型不在需要那些龐大的處理XML配置數(shù)據(jù)的方法。作為一個(gè)簡單的例子,將ConnectionStrings節(jié)對(duì)通用配置元素的配置管理整合進(jìn)ConfigurationManager對(duì)象,通過一個(gè)唯一的名字非常容易查找和訪問一個(gè)指定的數(shù)據(jù)庫連接串。這是因?yàn)橛幸粋€(gè).NET 集合類,它列出連接字符串配置的對(duì)象。每一個(gè)連接字符串對(duì)象都被一個(gè)名字鍵標(biāo)記。.NET類對(duì)XML元素的映射如下所示:
| ConnectionStringsSection | <connectionStrings> |
| ConnectionStringSettingsCollection | [implicitly created] |
| ConnectionStringSettings | <add name="MyConnection" connectionString="blahblah"> |
訪問MyConnetcion就如下面所示代碼一樣簡單:
string myConnectionString = ConfigurationManager.ConnectionStrings["MyConnection"].ConnectionString;ConnectionStrings非常簡單,所以讓我們來看一個(gè)經(jīng)常使用,但不那么明顯的.NET 2.0配置系統(tǒng):<system.web>配置組。你可能不知道它,但是但這一個(gè)復(fù)雜的配置節(jié)使用了一組類,用這組類我們可以創(chuàng)建自定義的配置節(jié)。讓我們以class>emement關(guān)系的形式來看一下System.Web.Configuration對(duì)象模型:
| Class: | Element: |
| SystemWebSectionGroup | <system.web> |
| AuthenticationSection | <authentication> |
| AuthorizationSection | <authorization> |
| CustomErrorsSection | <customErrors> |
| CustomErrorsCollection | [implicitly created] |
| CustomError | <error statusCode="404" redirect="..."> |
| HttpModulesSection | <httpModules> |
| HttpModuleActionCollection | [implicitly created] |
| HttpModuleAction | <add name="myModule" type="..."> |
| HttpHandlersSection | <httpHandlers> |
| HttpHandlerActionCollection | [implicitly created] |
| HttpModuleActionCollection | <add verb="*" type="..." path="..."> |
這只是System.Web ASP.NET配置節(jié)組提供的一套完整的配置節(jié)的一小部分。這些設(shè)置都可以通過一個(gè)精巧包裝的對(duì)象模型訪問,它歸根于System.Web.Configuration.SystemWebSectionGroup類。在事件的要求下,使用這個(gè)對(duì)象模型甚至可以將更新和保存回web.config文件,假設(shè)該代碼有改變和保存的權(quán)限。
.NET 2.0配置系統(tǒng)用這個(gè)對(duì)象模型為我們處理解析、驗(yàn)證、安全和population(譯注:關(guān)于 Population:這個(gè)詞原型是Populate,有填充的含義。例如老外有時(shí)候表述給下拉列表增加列表項(xiàng)時(shí),就說“Populate the list”。我想在文章里應(yīng)該是指反序列話時(shí)往配置文件中寫入配置元素吧)。除了編寫自定義配置節(jié),這是相當(dāng)簡單的,它完全去除了對(duì)XML的考慮,當(dāng)在應(yīng)用程序中使用你的配置時(shí)。不僅如此,在應(yīng)用程序的任何地方都可以直接訪問這個(gè)精美的包裝、強(qiáng)類型、安全對(duì)象模型,而無需擔(dān)心向注冊表那樣給自定義XML配置文件查找或存儲(chǔ)文件路徑。為不一致、不靈活、低性能的自定義配置管理器煩惱的時(shí)代一去不復(fù)返了。
3、編寫一個(gè)基本的配置節(jié)
如果在這之前的東西嚇到了你,別擔(dān)心。編寫代碼來提供自定義配置節(jié)到你的應(yīng)用程序非常簡單。處理難看的XML、安全檢查、類型轉(zhuǎn)換等等絕大部分問題,已經(jīng)被.NET 2.0框架中已有代碼處理了。由于System.Configuration的一組基類集,要?jiǎng)?chuàng)建一個(gè)簡單的配置節(jié)的實(shí)際代碼量非常非常少。讓我們開始創(chuàng)建一個(gè)含有一個(gè)字符串值、一個(gè)布爾值和一個(gè)時(shí)間跨度值的簡單配置節(jié)。每一個(gè)配置節(jié)都必須繼承ConfigurationSection基類,讓我從以下內(nèi)容開始:
代碼#region Using Statements using System; using System.Configuration; #endregionnamespace Examples.Configuration {/// <summary>/// An example configuration section class./// </summary>public class ExampleSection: ConfigurationSection{#region Constructorsstatic ExampleSection(){// Predefine properties here}#endregion// Declare static property fields here// Declare expose properties here} }一旦你開始定義一個(gè)配置節(jié)類,你將必須定義有效的配置屬性。一個(gè)配置屬性,通過ConfigurationProperty類表示,描述了一個(gè)配置項(xiàng),它將在你的配置節(jié)中可用。有兩種方法定義配置屬性,編程式(programmatic)和聲明式(declarative)。這兩種方法我個(gè)人都喜歡用,因?yàn)槁暶魇椒椒ㄓ兄谧悦枋龅拇a,編程式方法更嚴(yán)謹(jǐn)。這保證了只有你期望的確切的對(duì)象模型生成和支持,但是維護(hù)起來有些乏味,因?yàn)閷?duì)一個(gè)配置屬性兩者都要更新。注:在這文章中,作為一個(gè)完整的例子我將使用這兩種方法。
讓我們開始往我們的示例配置節(jié)中填寫代碼。首先定義靜態(tài)屬性字段,然后在類的靜態(tài)構(gòu)造器中創(chuàng)建那些字段。最后通過編寫C#屬性暴露配置數(shù)據(jù)。自定義的配置節(jié)完整源碼應(yīng)該看起來像下面這樣:
代碼#region Using Statements using System; using System.Configuration; #endregionnamespace Examples.Configuration {/// <summary>/// An example configuration section class./// </summary>public class ExampleSection: ConfigurationSection{#region Constructors/// <summary>/// Predefines the valid properties and prepares/// the property collection./// </summary>static ExampleSection(){// Predefine properties heres_propString = new ConfigurationProperty("stringValue",typeof(string),null,ConfigurationPropertyOptions.IsRequired);s_propBool = new ConfigurationProperty("boolValue",typeof(bool),false,ConfigurationPropertyOptions.None);s_propTimeSpan = new ConfigurationProperty("timeSpanValue",typeof(TimeSpan),null,ConfigurationPropertyOptions.None);s_properties = new ConfigurationPropertyCollection();s_properties.Add(s_propString);s_properties.Add(s_propBool);s_properties.Add(s_propTimeSpan);}#endregion#region Static Fieldsprivate static ConfigurationProperty s_propString;private static ConfigurationProperty s_propBool;private static ConfigurationProperty s_propTimeSpan;private static ConfigurationPropertyCollection s_properties;#endregion#region Properties/// <summary>/// Gets the StringValue setting./// </summary>[ConfigurationProperty("stringValue", IsRequired=true)]public string StringValue{get { return (string)base[s_propString]; }}/// <summary>/// Gets the BooleanValue setting./// </summary>[ConfigurationProperty("boolValue")]public bool BooleanValue{get { return (bool)base[s_propBool]; }}/// <summary>/// Gets the TimeSpanValue setting./// </summary>[ConfigurationProperty("timeSpanValue")]public TimeSpan TimeSpanValue{get { return (TimeSpan)base[s_propTimeSpan]; }}/// <summary>/// Override the Properties collection and return our custom one./// </summary>protected override ConfigurationPropertyCollection Properties{get { return s_properties; }}#endregion} }一旦你完成了,就是這樣。此自定義配置節(jié)準(zhǔn)備就緒。如果你喜歡寫更少的代碼,你可以不用構(gòu)造器和靜態(tài)字段,將每個(gè)值用過一個(gè)字符串鍵存儲(chǔ)在默認(rèn)的屬性集合中。這將導(dǎo)致配置節(jié)本身要求更少的代碼,但是要求整理上更少的定義。上述屬性將被下面的代碼的替代,靜態(tài)字段和構(gòu)造器可以刪掉。順便說一下,這就是純粹的聲明式方法:
代碼#region Properties/// <summary>/// Gets the StringValue setting./// </summary>[ConfigurationProperty("stringValue", IsRequired=true)]public string StringValue{get { return (string)base["stringValue"]; }}/// <summary>/// Gets the BooleanValue setting./// </summary>[ConfigurationProperty("boolValue")]public bool BooleanValue{get { return (bool)base["boolValue"]; }}/// <summary>/// Gets the TimeSpanValue setting./// </summary>[ConfigurationProperty("timeSpanValue")]public TimeSpan TimeSpanValue{get { return (TimeSpan)base["timeSpanValue"]; }}#endregion一個(gè)快速筆記,ConfigurationProperty真正是什么。默認(rèn),除非一個(gè)自定義元素明確地寫出,所有的ConfigurationProperty都定義在一個(gè)App.config或Web.config文件中。創(chuàng)建和自定義配置元素集合將在后面討論。
4、使用自定義配置節(jié)
現(xiàn)在你已經(jīng)為你的自定義配置寫了一個(gè)類,則你需要在一個(gè)App.config文件中定義自定義節(jié)。你必須添加必要的XML,以便他能被.NET 2.0配置系統(tǒng)解析。App.config文件要改成下面的那樣,ExampleSection才可用,假定上述代碼已經(jīng)被編譯成Examples.Configuration.dll:
代碼<configuration><configSections><section name="example" type="Examples.Configuration.ExampleSection, Examples.Configuration" /></configSections><examplestringValue="A sample string value."boolValue="true"timeSpanValue="5:00:00"/> </configuration><configSections>及其子節(jié)點(diǎn)的一個(gè)快速解釋。與內(nèi)置的配置節(jié)不一樣,他們是隱式定義的,自定義配置節(jié)必須顯式地定義。這是通過<configSections>元素和它相應(yīng)的<section>子元素來完成的。應(yīng)當(dāng)指出,一個(gè)節(jié)的名字一般是很重要的,不能任意選擇的。一會(huì)兒你就知道為什么了。一個(gè)配置節(jié)映射到一個(gè)完全限定的類名,其后是程序集名。選擇性地,你可以指定區(qū)域性、版本、公鑰(用于程序集的簽名)值,如果你想確保只在特定的程序集的特定版本中搜索,當(dāng)你的.config被解析時(shí)。在上面的例子中,字符串逗號(hào)之前的那部分是類名,后面那部分是程序集名(不包括.dll后綴)。
最后,你可以在代碼中用ConfigurationManager類使用你的自定義配置了。ConfigurationManager提供了一個(gè)叫做GetSection的方法,它允許你訪問任何已經(jīng)定義了的自定義設(shè)置。當(dāng)你訪問自定義配置節(jié)是,通常最好用動(dòng)態(tài)轉(zhuǎn)換和檢查是否為空,就像這樣:
訪問自定義配置節(jié)private string m_string; private bool m_bool; private TimeSpan m_timespan;void GetExampleSettings() {ExampleSection section = ConfigurationManager.GetSection("example") as ExampleSection;if (section != null){m_string = section.StringValue;m_bool = section.BooleanValue;m_timespan = section.TimeSpanValue;} }這里最重要注意的是選擇代表一個(gè)配置節(jié)根元素的名字。在App.config文件的例子中,節(jié)被定義為 “example”。因?yàn)檎{(diào)用GetSection()時(shí)加載節(jié)查找名字‘example’,在App.config中任何試圖為“example“重命名為其他名字將導(dǎo)致GetExampleSettings()失敗。這是不可以任意選擇一個(gè)自定義配置節(jié)的名子,除非明確地設(shè)計(jì)調(diào)整以保持一致,可能是通過使用另一個(gè)配置節(jié)。
本文轉(zhuǎn)自吳秦博客園博客,原文鏈接:http://www.cnblogs.com/skynet/archive/2010/03/20/1690475.html,如需轉(zhuǎn)載請自行聯(lián)系原作者
總結(jié)
以上是生活随笔為你收集整理的揭开.NET 2.0配置之谜(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CSS_class标签
- 下一篇: java创建两个foo方法_Java类实