设为首页收藏本站Access中国

Office中国论坛/Access中国论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

返回列表 发新帖
查看: 3842|回复: 6
打印 上一主题 下一主题

[ADO/DAO] [转帖]深入ADO(MSDN)

[复制链接]

点击这里给我发消息

跳转到指定楼层
1#
发表于 2004-7-15 17:40:00 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
<转自中文MSDN: http://www.microsoft.com/china/MSDN/library/archives/technic/develop/ADO.asp >



<H1 align=center>深入AD用Visual Studio 6.0来编写Active Data Objects 2.0

Active Data Objects结合了OLE DB的普遍性质—那就是在诸如RDO和DAO模型中可以找到的易于使用的特性.ADO包含了所有可以被OLE DB标准接口描述的数据类型.换而言之,ADO是可扩充的,不需要对你的部件做任何工作.

数据的访问和操纵是任何实际的应用程序的一个固有部分.对于数据来说,无论它是否是关系型的,无论它是否存在一个DBMS,也无论它的存储平台是什么,数据就是数据—一个文件没有必要一定是给定的二进制格式.

开发者群体需要具有简单接口的现代的开发工具以快速访问数据.微软对此问题的回答是Universal Data Access(UDA通用数据访问)体系结构,对此,Stephen Rauch的文章 “Manage Data from Myriad Sources with the Universal Data Access Interfaces”中有详细的阐述(MSJ,1997年9月).简单的说,UDA是一种将OLE DB应用于实际的理论.所有的都被指向一个数据源—一个电子表格,一条电子邮件消息,或一份AS/400文档—由OLE DB接口过滤并以一种通用的格式表示,这样应用程序能总是以同样的方式对数据进行访问.位于OLE DB上的并处理来自应用程序的调用的中间层被称作Active Data Objects(ADO).它是编写针对带有OLE DB提供者的任何类型的数据源的推荐标准.



图 1 OLE DB

让我们仔细看看Visual Studio 6.0所带来的ADO 2.0的新特性.我将向你展示如何用Visual Studio 6.0套件来编写ADO.我将集中于Java语言的Windows Foundation Classes(WFC)的和Visual Basic的内置的ADO支持,对于Visual C++和InterDev将略微提一提.

 

ADO概述


ADO是一个对象模型,它结合了OLE DB易于使用的特性以及在诸如Remote Data Objects(RDO)和Data Access Objects(DAO)的模型中容易找到的通用特性.ADO是一个可以通过IDispatch和vtable函数访问的COM自动化服务器.最重要的是:ADO包含了所有可以被OLE DB标准接口描述的数据类型.换而言之,ADO对象模型具有可扩展性,它不需要你对自己的部件做任何工作.通过通常的ADO编程接口,你可以可视化地处理所有的事,即使那些记录集的信息的格式是你从来没有想到过会见到的.

ADO在其实际运行中得到了很高的评价,内存覆盖,线程安全,分布式事务支持,基于Web的远程数据访问.作为Microsoft UDA策略的一部分,ADO试图成为基于跨平台的,数据源异构的数据访问的标准模型.随着时间的流逝,它将取代其他模型.ADO集中了RDO和DAO的所有最好的特性,并将它们重新组织在一个同样可以提供对事件的充分支持的略微有点不同的对象模型中.如果你想深入的钻研一下Microsoft的数据访问技术之间的不同,你可以看看 “Data Access Technologies”,这是由Robert Green写的MSDN的关于技术性方面的文章.

将你当前的基于RDO的系统移植为ADO的不需要对系统的完全的重构,但它也不是件微不足道的事情.移植系统的难度和复杂性比RDO和ADO的差异大.移植为ADO是很有价值的,但是我建议如果不是绝对必要的话请你不要这样做.

 

从企业的观点来看ADO


UDA给Windows DNA(Distributed Internet Applications分布式互连网应用程序)体系结构以一个数据访问和存取的机制.为进一步了解UDA,你可以看 “Say UDA for All Your Data Acess Needs,”该文是由Aaron Skonnard写的(见Microsoft Interactive Developer,1998年4月).

<FONT face=Verdana>即使是跨了多个硬件平台,任何一个分布式企业系统都有多个数据源,包括Microsoft Access数据库,电子表格和SQL Server表.最近,我参加了一个异构的图象数据库的研究,其内容包括:SQL Server表(包括图象及其描述),通过与名字相关的ASCII码和Word文档对图象进行分类索引,所有者文件中既有图象又有文本.当你需要对付这样的工程时,你会开始认识到统一的方法和对象公共集的重要性了.我曾经见过在相关的工程中使用不同的访问数据方法的开发团队.这种情况是经常发生的,这是因为其中一个团队相当早的提前对子项目进行了开发,或者由于

本帖被以下淘专辑推荐:

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 分享淘帖1 订阅订阅

点击这里给我发消息

2#
 楼主| 发表于 2004-7-15 17:42:00 | 只看该作者
ADO快速教程

ADO对象模型是由相对数量较少的对象组成.不象RDO对象模型,每个主要的ADO对象都能被个别的被创建.这就意味着,举例说吧,你不需要在创建有效的记录集对象前创建一个连接.



Dim cn As New ADODB.Connection

Dim rs As New ADODB.Recordset

cn.Open "Biblio"

rs.Open "select * from titles where title like '%h'", cn

ADO 2.0对象是Connection,Command,Parameter,Recordset,Field,Error和Property.ADO对象模型也包括四个类集:它们分别是一套相关的Error,Parameter,Property和Field对象.让我们来看看每个对象的主要特性.

Connection对象提供连接,该连接连接的程序中存有它正在访问的数据源.属性允许你定义:连接串,命令执行以及连接启动的间隔时间,数据提供者,(无论临时表位置是应该在客户端还是在服务器端),和对于数据的访问权限.方法有关于如下功能的:执行命令,打开和关闭一个连接和管理事务.

在许多地方你可以指定你想使用的提供者:通过Provider属性,在连接字符串中,或者甚至通过Open方法.选择你最喜欢的实现方式,但要保证你没有为同样的连接指定多个的提供者.缺省的提供者(当

你没有指定你自己的提供者的情况下)是MSDASQL,针对ODBC的Microsoft OLE DB提供者.

Command对象定义了一个SQL声明,一个存储过程,或任何其他的你可能想让提供者执行的关于数据的操作.Command的属性有:当前连接,最大允许执行时间和参数集.一个参数就是你将它作为参数传递给命令的值.在这些属性中用以区分一个参数的属性有:方向(输入,输出,或者两者都有),类型当然还有它们的值.

记录集对象也许是最典型的ADO对象,然而它是最复杂的当中的一个.它表示命令执行的结果,并且它大多是以数据库的记录集的形式给出.一个记录集是由以行和域的形式表示的信息组成.它不一定非要映射成关系型数据库的记录.实际上,如同我早些时候解释的那样,ADO是基于OLE DB并且能被用来可视化地访问任何数据源中的数据,这数据源包括非关系型的数据库.记录集提供了缓冲能力,它接受数据的变化,并且将这些变化以批处理的方式传给服务器.你可以浏览并对记录集的内容分类,如同列举和提取行.你也能通过如:删除,添加和刷新操作等任何方式修改数据.记录集存在跟连接的严格的关系,但是这不意味着你总是需要一个打开的连接来获得一个记录集.你也可以按如下方式进行:



Dim RS As New ADODB.Recordset

sql = "select * from authors"

RS.Open sql, "ubs"

既然Connection实际上是数据和命令传送的通道,所以该对象仍然被创建了,除了一个名字叫做Recordset.ActiveConnection的属性外,它是不可见的并工作在后台.

Field对象是一列同类的数据.它提供了一种这样的编程接口,即能让你可以对单个单元的值以及基本的特征如:类型和大小进行读和写.所有给定记录集的Field对象形成了一个Field集.如我将在后面向你说明的, Fields集隐藏着一个引人注目的特性,它将再次证明Recordset对象的灵活性.

最后是Property对象.每个对象都有属性.提供的使用了ADO的对象可能是各种各样的.但不存在这样一套属性即包含了所有可能的OLE DB提供者的静态的属性.因此,任何一个ADO对象都有静态和动态的属性.第一套属性集:包括Name,Type,Value和Attributes,都可以通过如下的语法来访问:

obj.PropertyName

最开始的三个属性都是自我说明性的.Attributes是一种数字式的描述符,它是以按位的跟提供者的能力有关的一些预定义的属性组合.(它类似于COM服务器的组件分类).

动态属性是跟潜在的提供者类型相应的.它们被归进Properties集合,并可以通过名字查询.



obj.Properties("propName")

The New Recordset Object

ADO 2.0包括一些新的功能(见图3).现在可以通过GetString方法把记录集扁平化变成一个字符串.

<BLOCKQUOTE>

Set Variant = recordset.GetString(

StringFormat,

NumRows,

<FONT face=Verdana>ColumnDelimiter, </FON

点击这里给我发消息

3#
 楼主| 发表于 2004-7-15 17:43:00 | 只看该作者
记录集的延续和缓冲

几个月前,一个读者给我发了个EMAIL,这里面有个很奇怪的问题.他说 “我喜欢象记录集这样的结构,并且我在任何时候都用它们.ADO记录集可以用来进行对数据的缓冲而不需要涉及到数据库连接或者,甚至OLE DB提供者?”我迅速对此回答: “不,我认为你不能这样做.记录集无论如何都需要一个连接.”

通过更进一步的思考,我觉得他说到了最重要的问题上.ADO记录集是个灵活的,最优的数据结构.它们能跟OLE DB数据库很好的工作,但是遗憾的是你并不能在任何你需要一个强大的使用方便数据结构的时候使用它们.但这里有个好消息.ADO 2.0给你提供了针对这一问题的新特性.Field集提供了相当新的Append方法,它能在最初就创建一个记录集.



Dim RS As New ADODB.Recordset

RS.CursorLocation = adUseClient

RS.Fields.Append "Name", adBSTR

RS.Fields.Append "City", adBSTR

RS.Open

RS.AddNew

RS!Name = "DinoE"

RS!City = "Redmond"

这里是一个ADO记录集,它就没有数据库连接和OLE DB的概念.ADO记录集总归是个独立的对象了.要紧的是你所选择的客户指针以及将你的新的域添加到记录集的Fields集中去.Append方法需要两个非选择性的参数:域名和类型.如果你想要字符串的话那就用adBSTR.

如果是这样的话,想象使用它的应用程序是个直接的办法.如:你可以写代码读你的老的所有者文件并将它们的数据转到记录集中.

这只是一个由磁盘文件创建记录集的方法.ADO 2.0增加了对记录集的延续性的支持.实际上,你有了个名为Save的新方法,它带有两个参数:输出文件名和数据格式.



Dim rs As New ADODB.Recordset

rs.adUseClient

rs.Save "c:\demo.rst", adPersistADTG

目前,只有adPersistADTG(也就是零)是允许的.只有打开的记录集才能执行存储.存储的数据也可能会受你所使用的过滤器的影响.注意在记录集关闭前该方法不会关闭文件的.在这中间,文件是作为只读方式访问的.这使得ADO应用程序用Save方法来创建平行且延续的缓冲变得更容易了.如果该文件已经存在了,Save方法返回一个错误,并准备执行一个工作环境.而且,该方法将当前行的位置移到第一个记录处.它将所有与记录集相关的东西(包括数据和和图表)都存到磁盘上.然而,它并不存储连接和命令信息.这将Save和GetString区分开来,并使得应用程序能加载先前存入的记录集.使用的方法是:



rs.Open "c:\demo.rst"

再次说明:使用客户端指针对于你来说是重要的.

 

异步获取和事件处理


ADO 2.0也可以让你以这样的方式执行命令:即以异步方式获取数据.这发生在调用Open方法的语法中.



recordset.Open Source, ActiveConnection, CursorType,

LockType, Options

跟通常一样,Source参数被指定一个有效的SQL字符串(甚至可能是一个被存储的过程的名字),一个先前被存储的文件的名字,或者是一个Command对象.Options参数限定命令,并且可能影响提供者处理它的方式.特别是它可以设定值为adExecuteAsync或adFethchAsync.adFetchAysnc意味着Source命令必须是以异步的方式执行,也就是说,该方法是立即返回的并且提供者在操作结束时激活一个事件.就另一方面来说,adFetchAsync使得第一块中的那些行被同步地取出(跟通常一样).其大小由记录集中的CacheSize属性中所存的值来决定.剩下的那些行被异步的载入.

当一个软件以异步的方式运行时,平台应该提供一种方式使得用户知道它什么时候结束.对于这问题有不同的实现方法,Platform SDK使用的是同步对象,Microsoft Internet Explorer Remote Scripting使用的是回调函数.ADO选择的是第三种方法,也许对RAD编程者来说是最合适的一种方法:事件.

<FONT face=Verdana>这当中有两类事件:与Connection对象相关的事件(ConnectionEvent接口)和属于Recordset对象的(RecordEvent接口)(见图5).在许多情况下你的程序将会在特定的操作发生的前后被通知.在一个操作发生后引发的

点击这里给我发消息

4#
 楼主| 发表于 2004-7-15 17:45:00 | 只看该作者
分层目录结构指针和数据成形

如果你处理数据,那么很可能你需要将它从多个表中提取出来.在大多数情况下,你使用JOIN命令合并相关表中的数据,特别是在你访问关系型数据库的情况下.任何由JION命令形成的记录集总是包含了多余的信息.例如,如果你对一个作者的所有书籍感兴趣的话,你可以合并Authors和Title Author表(我在这里指的是Biblio和PUBS数据库).在返回的记录集中,关于作者的信息在每一行中无效地重复.

你合并的嵌套次数越多或越复杂,消除多余信息对于你来说就越重要,分层目录结构指针允许你以基于树形的逻辑来组织你的记录集.该过程也叫做数据成形,并且它可以以两种方式来实现.你可以用类似于SQL的成形语言,或者你可以通过使用Visual Studio 6.0的相应的高级工具.我将在后面提供一个例子.现在我们来看看成形语言.



就某方面来说,成形语言显得跟SQL语言类似.

SHAPE {select au_ID, Author from authors}

APPEND ( {select ISBN from [title author]}

AS chapter RELATE au_id TO au_id)

一般说来,SHAPE命令定义记录集,APPEND子句添加一个子记录集到它当中去.换而言之,记录集可以作为一个域的其他任何数据类型使用.(见图6).父记录集和子记录集是通过域与域间的关系实现连接,该关系同样需要一个名称.



图 6 APPEND 记录集

作为数据成形的结果,父记录集附加了新的一列.每行中的新的域都是指向定义在APPEND从句中所定义的记录集.子记录集只列举那些au_id值跟父记录集中相同的域的是匹配的记录.在前面的代码中,如果给出一个作者的ID,那么存在一个记录集,在它当中有一个叫Chapter的附加的域,它的Value属性是指向一个子记录集,该子记录集中有来自于Title Autor表的所有所请求的域,在本例中,是ISBN.图7举例说明了一些用于分层目录结构记录的定位的Visual Basic源代码.注意我是怎样通过名称来访问Chapter域.为了得到实际的子记录集,还需要另外一个步骤:调用Value属性.

SHAPE命令也可以嵌套.这意味着你能用其他的SHAPE命令作为APPEND的内部命令.跟SHAPE语法打交道如同手工书写SQL查询一样也是件烦人的事情.幸运的是,Visual Studio 6.0给你提供了高级的工具它可以显著地简化SHAPE查询地定义.我将简要地说明这一点.

APPEND不是唯一一个你可以附在SHAPE命令上的从句. 你还可以改用一个COMPUTE从句来代替.COMPUTE能让你对已有的行执行统计功能,或按一个或更多的域进行分组.其预定义的函数有SUM, AVG, MAX和MIN.在最后的记录集里就给出了父行的这些操作的结果.而且,该记录集也包含了一个引用域,它指向由COMPUTE处理的原始行所在实际表.是不是觉得不太明白?让我们来看看一个例子.



假设你有个客户定单表.一天,你想知道每个姓是以A开头的客户的定单的总数.

SHAPE {select custID, last name

from orders where last name

like 'A%''}

COMPUTE (SUM(amount)) AS

chapter

BY custID

图8说明了结果记录集.你有用户的ID,姓,和订单的总数,除了客户定单的列表引用外,其它的都是形式非常简单的.



图 8 COMPUTE 记录集

为在较底的层次上(也就是说,没有用专用工具的情况下)实现数据成形功能,你得指定你的提供者为MSDataShape.



Dim cnn As New ADODB.Connection

cnn.Provider = "MSDataShape"

cnn.Open "Biblio"

 

Visual Basic 6.0 数据环境设计器


<FONT face=Verdana>Visual Basic 6.0的最引人注目的新特性之一就是Data Environment Designer数据环境设计器(见图9).它是个一般的,设计时环境,它能让你可视化地安排三种类型的数据工具:连接,命令和记录集.Data Env

点击这里给我发消息

5#
 楼主| 发表于 2004-7-15 17:46:00 | 只看该作者
在不同的环境下编写ADO

当用Visual Basic 6.0编写ADO时,你不能忽视Hierarchical FlexGrid(HFlexGrid)控件,见图15所示.



图 15 Hierchical FlexGrid

它显然比原来版本的Visual Basic的FlexGrid控件好得多.它得新特性如名称暗示的那样:内置对分层目录结构的记录集的支持.图16显示了一个简单的基于Visual Basic 6.0的程序,它用到了HFlexGrid控件并概述了我目前所涉及的所有的ADO的特性.在该例子中,我定义了一个DataEnvironment对象并用它来从包含了一个作者的所有的书的Biblio中获取一个记录集.Data Environment键和HFlexGrid控件间缺少的连接可在该片段中找到.



图 16 演示程序



Private Sub cmdDataEnv_Click()

Set hflex.DataSource =

DataEnv

Set hflex.DataMember =

DataEnv.RecordsetName

End Sub

该代码自动定义了DataEnvironment对象,在早先我是将它定义来作为HFlexGrid控件的数据源. HFlexGrid控件也可以成功的用于显示平面记录集并且在功能上是跟原来版本的Visual Basic一样的.

可以通过新的名为MsAdoDC.ocx的ActiveX控件来使得ADO的功能可用.它看起来象早先的但目前仍然被支持的Data控件.ADODC被假定为在你喜欢上它后将取代Data控件.如你可能猜到的那样, ADODC具有Data控件中所没有的特性.它是基于OLE DB的并公开了ADO 2.0编程接口.



图 17 ADODC控件

在Visual Studio 6.0中,Visual InterDev是个主要的Web开发工具.它有两个专门的特性: Data Environment对象(相同的东西在Visual Basic中也能被找到)和Recordset设计时控件.该思想是简单的,添加Data Environment对象到工程中,定义你的数据连接,查询和完成其它你所需要的任何工作.当你完成后,你将Recordset设计时控件插入到你的Active Server Pagers中,并从Data Environment集合--Recordsets, Commands, Connections中提取你所需要的数据对象(记录集,命令和连接).



<script language="JavaScript" runat="server">

function InitRS()

{

thisPage.createDE();

var rsTemp = DE.Recordsets('authors');

rsAuthors.setRecordSource(rsTemp);

rsAuthors.open();

}

先前的代码段说明了一个JavaScript过程,它恢复一个记录集并为在页面中进一步使用它而打开它.

在C++下使用ADO,并且通常使用源于C++的高级对象模型,常常引起下面的问题即需要将方法返回的Variant类型转换成语言的本地类型.由于这个原因,ADO 2.0公开了附加的叫做IADORecordBinding的接口,使得你将记录集的特定类型绑定为C++类型.这可以通过如下步骤来完成:

定义一个从CADORecordBinding派生出来的类并在头文件irsint.h里声明它.这个类必须有如下三个方法: BindToRecordSet, AddNew和Update.

将尽可能多的所需要的数据成员添加到类中以映射记录集的域.通过使用其中一个如下的预定义的宏将每个记录集的域跟它相应的数据成员绑定起来, ADO_VARIABLE_LENGTH_BINDING_ENTRY, ADO_FIXED_LENGTH_BINDING_ENTRY和ADO_NUMERIC_BINDING_ENTRY.所有这些对宏的调用必须由下面的给包围起来.



BEGIN_ADO_BINDING

END_ADO_BINDING

当你得到一个记录集,调用BindToRecordset,将其指针传给你的类.

<FONT face=Verdana>现在,根据你的需要对记录集进行处理.重要的是从现在开始你可以对你自己的数据成员而不是

点击这里给我发消息

6#
 楼主| 发表于 2004-7-15 17:47:00 | 只看该作者
Web端的ADO

去年当Internet Explorer 4.0发布时,很少有编程者真正领会到数据绑定特性的重要性.该特性看起来太棒了,但是它依赖于Internet Explorer 4.0并且缺乏适当的例子和应用程序.数据绑定是一项基于Web的技术,它能让你不离开当前页面去访问远程的数据源.换而言之,数据绑定是数据觉察控件的Web版.如果你使用它,你能让数据绑定HTML标签.举例来说吧,你可以将一个<TABLE>连接到一个特定的数据源并且可以让行和列在记录实际上进来的时候进行异步的添加.一旦你建立一个绑定,你(和你的用户)不再需要做其他的事情.

数据绑定是通过一个叫做Data Source Object(DSO)的模块来实现的,它实际上是一个ActiveX控件,它起到的大致作用是一个介于数据库和Web页面的代理.Microsoft为Internet Explorer 4.0提供了两个DS Tabular Data Control (TDC) 和 Advanced Data Control (ADC).TDC只能处理基于文本的数据,而ADC能连接到任意ODBC源.为有个初步的认识,你可以参考Rich Rollmann (MIND, July 1997)写的文章"Data Binding in Dynamic HTML".现在,ADC演变为RDS—它是Web端的UDA和ADO的一部分,它被认为是专门用于Internet的.

其中一个RDS是一个名叫RDS.DataControl 的ActiveX控件,你可以将它放到HTML页面里.通过使用数据绑定技术,你可以在客户端有效地管理记录集.你可以对行进行定位,分类,刷新而不需要进一步跟服务器联系.只有在刷新挂起的变化时你才要回到服务器上.RDS在客户端对数据进行缓存并极大地减少了对往返行程的需要.下面的代码行显示了如何在HTML页面中使用RDS来填写一个表单.



<object id="rds"

classid="clsid:BD96C556-65A3-11D0-983A-00C04FC29E33">

</object>

<table datasrc="#rds">

<thead><tr>

<th>First Column</th>

<th>Second Column</th>

</tr></thead>

<td><span datafld="FieldName1"></span></td>

<td><span datafld="FieldName2"></span></td>

</table>

 

RDS数据工厂


并不是所有的RDS都在客户端.当你通过VBScript或Jscript调用RDS ActiveX控件的方法时,你实际上最后调用的是RDS Data Factory Server (RDSServer.DataFactory对象),它是位于Web的服务器空间的.由于这个因素,数据绑定可以将你绑定到一个相容的浏览器上或一个激活了RDS的服务器上.

RDS Data Factory通过ADO接口以访问下面的数据源的方式来获取请求并完成它们.(见图18).它只是完成查询和更新的工作.



图 18 RDS的体系结构

目前,RDS被定义为ADO的一部分并成为整个UDA体系结构的一个基本的组件.ADO 2.0加强了RDS方面的功能. 对RDSServer.DataFactory对象进行了改进以支持一层自定义的代码,该代码的目的是添加合法性验证的能力和访问权限控制.这个被叫做Handler的新模块可以在执行时对命令字符串和连接参数进行修改.一个Handler可以被一个初始化文件驱动并且通过添加如下的句子来调用:



Handler=progID,arg1,arg2,…, argn

在记录集连接字符串中,需要用ProID来标识Handler,它是执行IdataFactoryHandler接口的COM服务器.

Microsoft提供了缺省的名为MSDFMAP.Handler的Handler,它是在msdfmap.ini文件中规定的.如果你自己想的话,你可以编写自己的Handler(处理).下面是一段对msdfmap.ini文件的摘录,你可以在你的Windows目录下找到它:

<BLOCKQUOTE>

[connect AuthorDatabase]

Access=ReadOnly

Connect="DSN=MyLibraryInfo;UID=MyUserIDWD=MyPassword"

[userlist AuthorDatabase]

Administrator=ReadWrite

<FONT face=Verdana>[sql Au
7#
发表于 2021-10-16 08:54:25 | 只看该作者
学习学习,好文章
您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|站长邮箱|小黑屋|手机版|Office中国/Access中国 ( 粤ICP备10043721号-1 )  

GMT+8, 2024-4-28 16:07 , Processed in 0.089421 second(s), 32 queries .

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表