不过ASP的缺点也逐渐的浮现出来:
1997年时,微软开始针对ASP的缺点(尤其是意大利面型的程序开发方法)准备开始一个新项目来开发,当时ASP.NET的主要领导人Scott Guthrie刚从杜克大学毕业,他和IIS团队的Mark Anders经理一起合作两个月,开发出了下一代ASP技术的原型,这个原型在1997年的圣诞节时被发展出来,并给予一个名称:XSP[2],这个原型产品使用的是Java语言。不过它马上就被纳入当时还在开发中的CLR平台,Scott Guthrie事后也认为将这个技术移植到当时的CLR平台,确实有很大的风险(huge risk),但当时的XSP团队却是以CLR开发应用的第一个团队。
cvfrty_+_-p[;
为了将XSP移植到CLR中,XSP团队将XSP的内核程序全部以C#语言重新撰写(在内部的项目代号是 "Project Cool",但是当时对公开场合是保密的),并且改名为ASP+,作为ASP技术的后继者,并且也会提供一个简单的移转方法给ASP开发人员。ASP+首次的Beta版本以及应用在PDC 2000中亮相,由Bill Gates主讲Keynote(即关键技术的概览),由富士通公司展示使用COBOL语言撰写ASP+应用程序,并且宣布它可以使用Visual Basic.NET、C#、Perl与Python语言(后两者由ActiveState公司开发的互通工具支持)来开发。
在2000年第二季时,微软正式推动.NET策略,ASP+也顺理成章的改名为ASP.NET,经过四年的开发,第一个版本的ASP.NET在2002年1月5日亮相(和.NET Framework 1.0),Scott Guthrie也成为ASP.NET的产品经理(到现在已经开发了数个微软产品,像ASP.NET AJAX和Microsoft Silverlight)。目前最新版本的 ASP.NET 4.0 以及 .NET Framework 4.0 仍在开发中。
cvfrty_+_-p[;
ASP.NET 运行的架构分为几个阶段: q34rfghnjkl,>
当装载(hosting) ASP.NET 的 Web 服务器接收到 HTTP 要求时,HTTP 聆听程序 (HTTP Listener) 会将要求转交给 URL 指定的网站应用程序的工作流程 (Worker Process)[3],ASP.NET 的工作流程处理器 (aspnet_isapi.dll,若是 IIS 5.0 时则是 aspnet_wp.exe) 会解析 URL,并激活位于 System.Web.Hosting 命名空间中的 ISAPIRuntime(视版本)对象,接收 HTTP 要求,并调用 HttpRuntime,运行 HttpRuntime.ProcessRequest(),在 ProcessRequest() 中使用 HttpApplicationFactory 建立新的 HttpApplication (或是指定的 IHttpHandler 处理器),再分派给 Page 中的 ProcessRequest() 或是 IHttpHandler 的 ProcessRequest() 方法,运行之后,再传回到 ISAPIRuntime,以及 aspnet_isapi.dll,最后交由 HTTP Listener 回传给用户端,因为运行程序有如管线般顺畅的运行,因此称为 HTTP Pipeline Mode。 cvfrty_+_-p[;
在 ASP.NET 内部的 HTTP 处理器有:
ssdd%%$#&kkjh4
当 HttpWorkerRequest 调用 ASP.NET 网页 (System.Web.UI 命名空间的 Page 类) 的 Page.ProcessRequest() 方法时,它会依序的引发 Page 内的各个事件,并同时调用在 Page 中所有控件的相关事件,其引发顺序为[4]: OPLK00((/
如果 HttpWorkerRequest 调用的是实现 IHttpHandler 界面的 HTTP 处理程序 时,它只会调用 IHttpHandler.ProcessRequest() 方法,由它来处理程序的输出,不像 Page.ProcessRequest() 会处理事件顺序,因此 HTTP Handler 很适合轻量级的数据处理,像是输出文件数据流或是图片数据流等。 OPLK00((/
ASP.NET 的原始设计构想,就是要让开发人员能够像 VB 开发工具那样,可以使用事件驱动式程序开发模式 (Event-Driven Programming Model) 的方法来开发网页与应用程序,若要以ASP技术来做到这件事的话,用必须要使用大量的辅助信息,像是查询字符串或是窗体字段数据来识别与判断对象的来源、事件流向以及调用的函数等等,需要撰写的代码量相当的多,但 ASP.NET 很巧妙利用窗体字段和JavaScript脚本把事件的传递模型隐藏起来了。 cvfrty_+_-p[;
ASP.NET 的事件模型是由
以及数个 Hidden Field 组合而成,基于 HTTP 模型的限制,所有的网页程序在运行结果输出到用户端后,程序就会退出运行,为了维护在 ASP.NET 网页与控件的状态数据,因此在输出 ASP.NET 控件时,ASP.NET 会将部份状态数据储存到网页的 Hidden Field 中,这类型的状态数据称为 ViewState(ID 为 __VIEWSTATE),在服务器端即会被解译出状态与事件数据。在大多数的内置 Web 控件中都有使用到这个机制,因此在使用大量 ASP.NET Web 控件的网页中,会有许多的 ViewState 会存放在网页中并随着 HTTP 数据流输出到用户端,ViewState 在输出时,会被加密为一组乱码字符串,其金钥值定义在计算机中,并且每一个对象都会被序列化 (serialize) 成字符串(因此若是自定义对象要放到 ViewState 时,则应要让它支持序列化),再输出到 __VIEWSTATE 字段中,在每次的网页来回时都会被传输,较大的 ViewState 会让网页大小膨胀,不利于快速的网络传输,不过 ASP.NET 本身有提供将 ViewState 关闭的功能,因此如果控件不需要状态保存时,可将它关闭以减少输出的大小。 OPLK00((/为确保控件的事件能够确实被引发,让事件驱动能够被运行,因此控件事件引发命令时需要的参数,是交由 JavaScript 脚本在用户端引发时,填入另一个 Hidden Field(ID 为 __EVENTTARGET 以及 __EVENTARGUMENT),并且引发窗体的送出指示 (submit),传送到服务端后,服务端的 HttpApplication 中的工具函数会解析 __EVENTTARGET 和 __EVENTARGUMENT 字段中的信息,并且交由控件所实现的 RaisePostBackEvent() 来引发事件,并由 .NET Framework 内部的事件处理器制接手处理(调用控件设置的事件处理程序)。 ssdd%%$#&kkjh4
在 ASP.NET 运行的时候,经常会有网页的来回动作 (round-trip),在 ASP.NET 中称为 PostBack,在传统的 ASP 技术上,判断网页的来回是需要由开发人员自行撰写,到了 ASP.NET 时,开发人员可以用 Page.IsPostBack 机能来判断是否为第一次运行 (当 ASP.NET 发现 HTTP POST 要求的数据是空值时),它可以保证 ASP.NET 的控件事件只会运行一次,但是它有个缺点(基于 HTTP POST 本身的缺陷),就是若用户使用浏览器的刷新功能 (按 F5 或刷新的按钮) 刷新网页时,最后一次运行的事件会再被运行一次,若要避免这个状况,必须要强迫浏览器清空高速缓存才可以。
ASP.NET 2.0 中有新增三个来回模式: s0oiu76.\"\":
来回模式不仅是 ASP.NET 运作时的内核,它也是 ASP.NET 应用程序的一个主要缺点,尤其是在设计复杂度高的页面时,在网页中隐藏的 ViewState 的大小会相当大,而在每次的来回动作中,都会传送 ViewState 在内的窗体信息,大量的 ViewState 会使得传送的时间拉长,而且每次来回动作都会让整个网页被刷新,而出现闪烁的情况(就算在本地端也一样),但在AJAX技术尚未成熟时,只能够忍受这种因底层限制所带来的问题,在ASP.NET AJAX技术发展出来后,通过UpdatePanel成功的缓解了这个问题(但 ViewState 传送的问题仍然未根本的解决,必须要使用像 Page Method 这样的方式才能彻底的解决)。 cvfrty_+_-p[;
熟悉 ASP 技术的人都知道,代码都是混在 HTML 标签之间,以输出预期需要的 HTML 指令,这个技术在 ASP.NET 中,由各控件的绘制 (Render) 机制包装起来了,绘制机制装载了 HtmlTextWriter 对象,由它来产生 HTML 指令,它会输出至 HttpContext 的 Response 输出数据流中 (即 ASP 技术的 Response.Write()),下列代码为绘制技术的示例: OPLK00((/
为了让控件的输出有层次结构性,ASP.NET 开发了一个可以层次结构化输出控件 HTML 指令的方式,称为控件树 (Control Tree),借由控件树,可以让各个控件的输出可以层次结构化,不论是否是收纳型控件,还是一般型的控件,都可以按照控件的顺序来输出。
ssdd%%$#&kkjh4
状态管理 (state management) 在Web应用程序中,一向是很重要的课题,良好的状态管理可以帮助开发人员发展出具有状态持续能力的应用程序(像是工作流程型应用程序或是电子商务应用程序),但状态管理功能会视应用程序的部署状态以及信息的共用程度来选择,在 ASP.NET 中,分为服务器端状态管理以及用户端状态管理,用户端状态管理为ViewState以及Cookies,服务端状态管理则是Session与Application对象。它们的差异点在于:
Application 对象会在应用程序的 Application_OnStart 事件中初始化,并使用名称来识别数据(它是一个 NameObjectCollectionBase 集合的实现品),它会储存在应用程序的范围内,所有的连接(用户)都可以使用,属于共用型的储存体,适合储存所有用户都可使用的数据,在多人使用的情况下,可以适当的使用 Lock/Unlock 的机制来确保应用程序状态的更新。
连接层级的对象是 Session,以浏览器的运行个体为识别单位,数据依浏览器的运行个体来储存,在浏览器的运行个体第一次连到应用程序时,ASP.NET会设置一个Session ID,并且使用它来识别 Session,每一个 Session 都是 ICollection 与 IEnumerate 的实现,用 key 来识别数据值,并且具有时间的限制 (timeout),若超出时限时服务器会自动清理掉,默认的 Session 时限为 20 分钟。Session ID 的算法是由 RNGCryptoServiceProvider (密码编译乱数产生器提供者)产生,并编码成一个 Session ID 字符串(例如 anf4vuup3xiq0arjlqla2l55 这样的字符串)储存在服务器中,用以识别不同的 Session 个体。
ssdd%%$#&kkjh4
为因应不同的用户端,ASP.NET 设计了不同的 Session ID 存放机制,像是旧式的浏览器或是行动用户端这种不支持本地储存cookie的设备时,ASP.NET 可以直接在 URL 中加上 Session ID 的识别,像是 http://www.acme.com.tw/(anf4vuup3xiq0arjlqla2l55)/profile.aspx 这样的 URL,可以由开发人员自行设置,或是使用 AutoDetect 设置来让 ASP.NET 自行判断要使用的 Session ID 存放方式。 ssdd%%$#&kkjh4
Session ID 的产生方法可以由程序开发人员自定义,借由改写 SessionIDManager 的 CreateSessionID() 方法来自定义。 cvfrty_+_-p[;
状态管理在单一服务器上,可以储存在服务器的存储器中,但若是在大型网站中,使用许多的 Web 服务器来实行负载平衡(Load Balancing)处理时,会有状态储存在哪个位置的问题,因此需要有一个可以在每个 Web 服务器之间做状态储存的媒介,像是独立的服务器或是数据库等等。在 ASP.NET 中支持了四种状态储存的媒介[5]:
ASP.NET 是开发 Web 应用程序的基础架构 (framework),除了它内部的运作方法外,对外也显露了许多的开发支持,让开发人员可以利用它来发展出许多强大的 Web 应用程序解决方案。
ASP.NET 最基础的底层为网页 (Page),网页由 System.Web.UI.Page 类来提供基础支持,包含了页面的事件以及对象绘制的引发点(Page 类本身是一个 HTTP Handler 的实现品)。ASP.NET 网页在微软的官方名称中,称为 Web Form,除了是要和Windows Forms作分别以外,同时也明白的刻划出了它的主要功能:“让开发人员能够像开发 Windows Forms 一样的方法来发展 Web 网页”。因此 ASP.NET Page 所要提供的功能就需要类似 Windows Forms 的窗体,每个 Web Form 都要有一个
区块,所有的 ASP.NET 服务器控件都要放在这个区域中,这样才可以让 ViewState 等服务器控制能够顺畅的运作。 cvfrty_+_-p[;在网页中也可以使用代码,以类似于ASP时代的撰写方式来开发,此种开发方式称为 inline code,在 ASP.NET 的程序开发模式中,inline code ,要放在 区域中,如下列的示例程序:
另一种模式则是将代码和网页分离,这种模式称为代码后置 (Code-Behind),这个方法可以将代码独立到一个文件中,网页可以保持较干净的状态,让维护网页程序的复杂度降低很多,在网页的提示指令 (directive) 中,可以设置代码后置的参数,像是 Inherit、CodeFile、Class 等参数。 cvfrty_+_-p[;
而一个典型的代码后置例子为:
s0oiu76.\"\":
使用代码后置模式的设置时,可以让 ASP.NET 运行引擎在加载网页时,由代码后置参数取得对应的类信息,藉以使用 Reflection 的方式来运行后置的代码。
ASP.NET 可以支持HTML和XHTML两种网页内容,但在Visual Studio.NET中,默认是使用 HTML,但在Visual Studio 2005以后的版本,则一律都改用XHTML格式。 cvfrty_+_-p[;
ASP.NET 的内置控件分为两种:
q34rfghnjkl,>
除了内置的控件之外,ASP.NET 也提供了可以自定义的控件架构,并且支持两种控件开发方法:
ASP.NET 的 Web 控件有时会包装一些用户端脚本 (client-side scripting),在控件被绘制时输出到用户端,这些脚本多数被包装在 DLL 的资源档中,并由 ScriptResource.axd 处理程序来输出,开发人员也可以利用 ClientScriptManager (Page.ClientScript 属性) 中的方法来添加脚本到网页程序中,常用的方法有:
ssdd%%$#&kkjh4
以往在 ASP 中常被使用的五大基本对象,在 ASP.NET 中仍然持续被支持,但它们都换了一个身份来提供:
cvfrty_+_-p[;
导览部件 (navigation controls)
s0oiu76.\"\":应用程序服务 (application services) 是在 ASP.NET 2.0 中才开始提供,它以 Provider-based Pattern 为主,实现出数个网站的常用服务,包含会员服务 (Membership Service)[7]、角色服务 (role service)[8]与设置档服务 (profile service)[9] 等。
OPLK00((/
会员服务由 Membership 以及其数据提供者 MembershipProvider 构成,应用程序使用 Membership 所显露的方法来操作,它会将要求转送给指定的 MembershipProvider 实现来处理,ASP.NET 目前支持来自于数据库的 SqlMembershipProvider (支持 SQL Server) 以及来自于活动目录的 ActiveDirectoryMembershipProvider,开发人员也可以自行由 MembershipProvider 继承来实现自定义的会员服务数据提供者。
角色服务与会员服务类似,由 Role 以及其数据提供者 RoleProvider 构成,应用程序使用 Role 所显露的方法操作,由 RoleProvider 实现提供数据服务,目前内置的 RoleProvider 有来自 活动目录 或 XML 文件的 AuthorizationStoreRoleProvider,由 SQL Server 供应数据的 SqlRoleProvider,以及支持 Windows 角色的 WindowsTokenRoleProvider 三种,开发人员可自行实现 RoleProvider 的方法以发展出自定义的角色服务提供者。 q34rfghnjkl,>
设置档服务是一个特殊的服务,它结合了 .NET Framework 的 CodeDOM 开发模式,以及 System.Web.Profile 命名空间的 ProfileBase、ProfileInfo 与 ProfileManager 等类,组合出完整的设置档支持,其数据来源也是以 Provider-based Pattern 为主,由 ProfileProvider 提供,ASP.NET 内置由 SQL Server 数据库建立的 SqlProfileProvider,其字段系由开发人员在 ASP.NET 组态档中自行定义,再由 ASP.NET 动态产生强型别的字段属性。 q34rfghnjkl,>
设置档服务也是作为 ASP.NET 2.0 的网页组件 (Web Part Framework) 所需要的设置档储存支持[10],Web Part Framework 可以让开发人员可以开发出具备个人化能力 (Personalization) 的网页配置方案,让用户可以用自行新增与拖放的方式来设计自己的网页布置,所需要的设置储存即由设置档服务处理。
另一个需要和应用程序服务配合使用的功能为 Web 事件架构 (Web Event Framework)[11],它需要由应用程序服务提供数据结构,它也有 Provider-based Pattern,并可以支持数种的事件数据提供者。 cvfrty_+_-p[;
除了 ASP.NET 网页以外,.NET Framework 还提供了两种可以由开发人员自行发展处理模型的模块,一种是HTTP Handler,另一种则是HTTP Module[12]。
ssdd%%$#&kkjh4
HTTP Handler (扩展名为 .ashx) 由 System.Web.IHttpHandler 界面定义了必要的方法(可支持异步的 HTTP Handler 称为 HTTP Async Handler,由 System.Web.IHttpAsyncHandler 界面定义),其中最重要的方法是 ProcessRequest() 方法,开发人员必须要实现这个方法,才能够让 HTTP Handler 有作用,它也可以通过 ASP.NET 的组态设置,让 HTTP Handler 可以处理特定的扩展名,它可以被视为 .NET Framework 中的 ISAPI Extensions 实现方法。 q34rfghnjkl,>
HTTP Module 则是由 System.Web.IHttpModule 界面定义,它可以在整个网页生命周期中被调用多次,并实际处理由 HttpApplication 所引发的事件,开发人员需要实现 IHttpModule.Init() 方法,以及处理 HttpApplication 事件需要的代码。它可被视为 .NET Framework 中的 ISAPI Filter 实现方法。 q34rfghnjkl,>
ASP.NET 在一开始的时候是缺乏范本引擎 (template engine) 的,其主因是.NET Framework本身是面向对象,且需要用继承的方式才能够延伸功能,大多数的开发人员都是由 System.Web.UI.Page 继承并定义出新的基类,并撰写要绘制 HTML 的方法,以及在他们的应用程序中修改以继承该类,然而这个方法可能会被用在网站的很多地方,因而会大大的提升混合代码与标记的复杂度,这个方法也只能在运行期才能够以可视化的方式测试,无法在设计时期可视化,其他的开发人员总是使用原有的 ASP 方法(即 指令)来把每个网页需要的部份包到网页中,防止在每个网页中都要撰写相同的导览代码。
在 ASP.NET 2.0 中,推出了主版页面 (master page)的概念[13],它可以让开发人员先行定义外观版型 (*.master),再使用它来套用实际运行的网页,网页与主版页面之间以 ContentPlaceHolder 的 ID 做链接,以套用正确的内容到保留区 (即由 ContentPlaceHolder 包住的区域) 中,开发人员也可以定义在保留区没有套用时需要显示的默认内容。在 ASP.NET 3.5 中更进一步的支持设计时期的嵌套主版页面 (nested master pages),以及把网页的 HEAD 区块纳入 ContentPlaceHolder 的范围。 cvfrty_+_-p[;
与主版页面相关的,还有主题(Theme)以及面板(skin)技术[14],这两个技术允许开发人员或设计人员自行定义网页的样式设置以及套用的样式支持,每个主题中可以包含数个面板档,这些面板档决定了控件要输出时套用的样式,开发人员则可以利用主题来决定不同的外观要使用的样式。
ASP.NET 也允许在应用程序中动态的变更主版页面与主题,但必须要在页面的 PreInit 事件例程设置。 s0oiu76.\"\":
ASP.NET 在 1.x 时,使用的是组件为主的编译方式,一个网页只会产生一个组件,这个方式最大的优点,就是可以自由定义命名空间,且在部署应用程序时会比较方便,但由于 ASP.NET 1.x 所处的时代,如果网站是有许多代码的情况下(即 DLL 档很大),加载的速度会变慢,且占用存储器的量会很多,当时的存储器价格也尚未降到现在的水平。因此在 ASP.NET 2.0 开始,另外提供了一个预先编译(Pre-compilation) 的编译模型,这个编译方法会将每个网页都各自编译成一个组件,其文件名称会是 App_[乱数字符串].dll 命名,在编译时期由 ASP.NET Pre-compilation 工具 (aspnet_compiler.exe) 给定,优点是可以不必加载过量的代码到存储器中,但缺点则是无法自定义命名空间,而且在更新时必须要更新所有的 DLL 档以及网页等,否则会造成名称不一致,让 DLL 无法被加载的问题。
早期 ASP.NET 2.0 仅提供预先编译模式,让它的缺点很快的被暴露出来,因此微软也为 ASP.NET 2.0 开发了沿用 ASP.NET 1.x 的编译模型的工具:Web Application Project,在 Visual Studio 2008 中开始内置,至此,ASP.NET 支持两种编译模式的架构抵定。 cvfrty_+_-p[;
ASP.NET 的安全性支持分为验证 (Authentication)与授权 (Authorization)两个部份。
OPLK00((/
ASP.NET 的验证方式有三种[15]:
ASP.NET 的授权方式有两种[16]: OPLK00((/
一个 URL 授权的设置示例如下: ssdd%%$#&kkjh4
users="Kim"/> roles="Admins"/> users="John"/> users="?"/>> OPLK00((/ ASP.NET 1.0 开始支持 Web Service 的开发,是微软在本地平台上支持 Web Service 发展的第一个实现品,但它却不是微软的第一个 Web Service 开发工具实现品[17],.NET Framework 中提供了一个 WSDL.exe,可以连接 Web Service 下载WSDL定义档,并产生一个 Proxy Class 的源代码,供用户端应用程序使用,若是使用 Visual Studio 开发的话,这个动作会由“加入 Web 参考”的动作在背后处理掉。
ASP.NET Web Service 的发展只是平台的基础,微软在 Web Service 的开发上提供持续的支持,尤其是在 WS-I (Web Service Interoperability) 组织成立后,为符合 WS-I 的 Web Service 标准,微软开发了强化 Web Service 的增强包 Web Service Enhancement (WSE),最新版本为 3.0 版(搭配 ASP.NET 2.0),可支持许多 WS-I 的标准。 ssdd%%$#&kkjh4
由于 Windows Communication Foundation 的推出,微软将 Web Service 的发展重心移到 WCF 上,原有的 ASP.NET Web Service 即给定了一个名称:ASMX Web Service。 ssdd%%$#&kkjh4
ASP.NET 在 2.0 版时,功能已大致底定,成为 Web 应用程序的基础架构,微软开始在 ASP.NET 2.0 上开发扩充的功能,包括 AJAX 的支持、MVC架构的支持以及更容易开发出数据库应用的架构。
OPLK00((/
ASP.NET AJAX 是微软发展的 AJAX Framework,让 ASP.NET 的开发人员得以用很简单的方式就可以开发出支持 AJAX (AJAX-enabled) 的应用程序,包含用户端脚本的支持,以及服务器端的链接等等。 q34rfghnjkl,>
ASP.NET MVC Framework 是微软基于 MVC (Model-View-Controller) 架构所开发的架构,让应用程序各个模型可以在 MVC 架构下运行。 OPLK00((/
ASP.NET MVC Framework 也支持以测试驱动的开发模式 (Test-Driven Development)。 OPLK00((/
ASP.NET Dynamic Data Framework 是微软在 ASP.NET 3.5 中开发的一组类库,封装在 System.Web.DynamicData 命名空间中,并且配合 ASP.NET Routing Model (网页绕送功能) 让开发人员可以很简单的开发出基于 LINQ to SQL 或是 ADO.NET Entity Framework 数据模型的数据库应用程序。 ssdd%%$#&kkjh4
ASP.NET Routing Model (官方译名为 ASP.NET 路由)是一个基于REST规格下的 URL 对应机制,开发人员可以在服务器端设置 URL 的格式,用户可以用由开发人员定义的 URL 格式浏览网页,ASP.NET 会自动将 URL 转换成为内部的 URL 格式,虽然它和 URL Rewriting 很像,但微软认为 ASP.NET Routing 不是 URL Rewriting[18]。 ssdd%%$#&kkjh4
Silverlight 是微软的新一代RIA技术,ASP.NET 3.5 Service Pack 1 (SP1) 中加入了对 Silverlight 2.0 的 ASP.NET 服务器端支持,包含:
ssdd%%$#&kkjh4
目前已有数个工具可支持 ASP.NET 应用程序的开发。 cvfrty_+_-p[;
| 2002年1月16日 | 1.0 | 与Visual Studio .NET一起发行的第一个版本 |
|
| 2003年4月24日 | 1.1 | 与Windows Server 2003和Visual Studio .NET 2003一起发表。 |
|
| 2005年11月7日 | 2.0 |
研发代号为Whidbey,和Visual Studio 2005、Visual Web Developer Express与SQL Server 2005一起发表。 s0oiu76.\"\": |
|
| 2007年11月19日 | 3.5 | 与Visual Studio 2008和Windows Server 2008一起发表 |
|
| 2008年8月11日 | 3.5 Service Pack 1[20] | 于 Visual Studio 2008 Service Pack 1 发表 |
|
词条内容仅供参考,如果您需要解决具体问题
(尤其在法律、医学等领域),建议您咨询相关领域专业人士。
0
收藏到: