??xml version="1.0" encoding="utf-8" standalone="yes"?> ADO.NET是专门ؓ帮助开发h员开发高效的多层数据库应用程序设计的。ADO.NET对象模型可分Zc:一cMؓ“连接的”,一cMؓ“断开q接的”对象,后者允许将查询l果保存在内存中q行处理?/P>
”连接的“对象模型顾名思义Q它是直接与数据库进行连接操?“断开q接”的对象模型可以说是建立在“连接的”对象模型的基础上进行的Q因为它必须先进行一ơ“连接的”操作,才能得到所需的结果?/P>
举个?Q?nbsp; SqlConnection con=new SqlConnection("server=localhost;database=db,uid=sa,pwd=;"); SqlDataAdapter ad=new SqlDataAdapter("select * from table",con); DataSet ds=new Dataset(); ad.Fill(ds,"table"); //注意q里已经从数据库里查询出来的结果放C个Dataset对象里,从此d始,你就开始用“断开q接”的对象模型来对数据库进行操作,DataSet对象是一个在内存中的”虚数据表“,你可以对它进行Q何操作而不影响数据库,可以对它q行排序Q修改,查询Q增加,删除。而如果你xҎ据库内容的话Q也可以通过DataSet对象来操作,很简单,调用它的update()Ҏ卛_完成更新数据库。也可以使用它的GetChanges()Ҏ来获取只更改q的行,它返回一个DataSetQ这个Dataset不同于调用GetChanges()Ҏ的那个,q回的这个只是它的一张子表,也就是更改过的数据,利用此方法可以大大地提高多层ADO.NET应用E序的性能。DataSetc还有一个MergeҎ用来合ƈ两个DataSet对象的数据,ADO.NET默认覆盖被调用Merge()Ҏ的DataSet中的行?/P>
ADO.NETq提供了一U强cd的DATASET对象Q它可以帮助你简化徏立数据访问应用程序的q程。例如:有个表叫table,其中有一列叫column你可以这h讉K此列Q?/P>
vb.net: Dim ds as DataSet Console.WriteLine(ds.table(0).column); (table(0).表示table表中的第1行) c#: DataSet ds; Console.WriteLine(ds.table[0].column); (table[0].表示table表中的第1行) 是不是很z? ^_^ 关于DataSet里面q有好多东西方便用如QDataTable,DataView,DataRow,DataColumn,DataRelation,Constraint一大堆的好东西Q在以后的日志中会提刎ͼ
http://www.cnblogs.com/William_Fire/articles/125819.html
http://www.cnblogs.com/william_fire/articles/126665.html
http://www.cnblogs.com/tintown/archive/2005/03/23/124395.html
http://www.cnblogs.com/tintown/category/12787.html
http://www.cnblogs.com/tintown/archive/2005/04/04/131784.html
http://www.cnblogs.com/tintown/archive/2005/04/04/131784.html?Pending=true#PostPost
http://www.cnblogs.com/tintown/archive/2005/04/07/132876.html
http://blog.sunmast.com/sunmast/articles/816.aspx
ADO.NET对象模型Q?BR>http://www.phome.net/document/net/200504/net111246243813950.html
http://www.phome.net/document/net/200504/net111246243713949.html
http://www.phome.net/document/net/200504/net111246244913952.html
ADO.NET对象模型
http://blog.csdn.net/jabby12/archive/2004/08/02/59221.aspx
可见Q在分层的时候,我们会增加一个实体层Q它的作用如下:
?显C数据和实际的存储区域隔,保证了业务的独立性,提高了可重用性?
?在业务层和表现层之间传递数据。(如果没有实体层的话,我们需要把表的每个字段作ؓ一个参数在它们之间传递,如果修改的话Q将需要媄响到E序的各个层Q?
?提供更大的可收羃性?
2Q?业务实体层的几种选择Ҏ及其优缺炏V?
?NET环境下实C务实体有下面的几U选择Q?
?DataReader BE h最快的d速度Q用于只ȝ场合Q不hOO的概c?
?XML BE 可以与XML Reader和DataSet转换。缺点:性能低,验证、解析、显C、排序等都很复杂?
?Generic DataSet BE 优点Q数据绑定等。缺点:客户端必通过集合来获取数据,没有cdQ实例化开销大,调度性能低?
?Typed DataSet BE 优点Q由cdQ可以进行类型检查。缺点:只能从DataSetl承Q部|不方便Q可扩展性差Q实例化开销大,调度性能低?
?Custom BE 优点Q性能调优Q代码更h可读性,用自定义实体cd义一个良好的接口Q将复杂问题隐藏在其中。缺点:设计开发都很复杂,需要自己去实现CURD操作Q自己去实现数据l定Q工作量很大?
?O/R Mapping的实?它具有自定义cȝ所有优点,同时实现了CRUDQ数据绑定等操作?
关于ObjectSpaces ?U>llblgen
http://www.llblgen.com/defaultgeneric.aspx
http://www.sinzy.net/blog/Read.asp?ID=44&BID=931
http://www.csdn.net/develop/author/NetAuthor/sun2bin/
]]>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconworkingwithtypeddataset.asp
Implementing Data Transfer Object in .NET with a Typed DataSet
Version 1.0.0
GotDotNet community for collaboration on this pattern
Complete List of patterns & practices
Context
You are implementing a distributed application with the .NET Framework. The client application displays a form that requires making multiple calls to an ASP.NET Web service to satisfy a single user request. Based on performance measurements you have found that making multiple calls degrades application performance. To increase performance, you would like to retrieve all the data that the user request requires in a single call to the Web service.
Background
Note: The following is the same sample application that is described in Implementing Data Transfer Object in .NET with a DataSet.
The following is a simplified Web application that communicates with an ASP.NET Web service to deliver recording and track information to the user. The Web service in turn calls a database to provide the data that the client requests. The following sequence diagram depicts the interaction among the application, the Web service, and the database for a typical page.
Figure 1 illustrates the sequence of calls needed to fulfill the entire user request. The first call retrieves the recording information, and the second call retrieves the track information for the specified recording. In addition, the Web service must make separate calls to the database to retrieve the required information.
Database Schema
The schema that is used in the example shown in Figure 2 depicts a recording record that has a one-to-many relationship with a track record.
Implementing a DTO
One way to improve the performance of this user request is to package all the required data into a data transfer object (DTO) that can be sent with a single call to the Web service. This reduces the overhead associated with two separate calls and allows you to use a single connection with the database to retrieve both the recording and the track information. For a detailed description of how this improves performance, see the Data Transfer Object pattern.
Implementation Strategy
A typed DataSet is a generated subclass of System.Data.DataSet. You provide an XML schema file which is then used to generate a strongly-typed wrapper around the DataSet. The following two code samples illustrate the differences. The first sample is implemented with an ordinary DataSet:
DataTable dataTable = dataSet.Tables["recording"]; DataRow row = dataTable.Rows[0]; string artist = (string)row["artist"];
This sample indicates that you need to know the table and column names to access the tables and fields contained in the DataSet. You also have to know the return type of the Artist field to ensure that the correct cast is done. If you do not use the correct type, you will get a runtime error. The following is the same example implemented with a typed DataSet:
Recording recording = typedDataSet.Recordings[0]; string artist = recording.Artist;
This example demonstrates the benefits that the typed interface provides. You no longer have to refer to table or column by name and you do not have to know that the return type of the Artist column is a string. A typed DataSet defines a much more explicit interface that is verifiable at compile time instead of at runtime. In addition to the strongly-typed interface a typed DataSet also can be used in all places a DataSet can be used; therefore, it also can be used as a DTO. It is loaded in a similar fashion as a DataSet and it can be serialized to and from XML. In comparison to an ordinary DataSet you do have to write and maintain an XML schema that describes the typed interface. The Microsoft Visual Studio .NET development system provides a number of tools that simplify the creation and maintenance of the schema.The rest of this implementation strategy outlines the steps required in creating a typed DataSet for the sample application just described.
Creating a Typed DataSet
A typed DataSet is generated from an XML schema. Visual Studio .NET provides a drag-and-drop tool which automates the creation of the schema (see Figure 3) and the generation of the typed DataSet classes. If you do not use Visual Studio.NET, you can write the XML schema and use a command-line tool called XSD.exe to generate the typed DataSet. For detailed instructions on both of these methods, see "Typed DataSets in ADO.NET" from the May 2001 issue of .NET Developer [Wildermuth02].
RecordingDto.xsd
The following is the XML schema for the DTO to be used in this example. It combines both the recording table along with its associated track records in a single typed DataSet named RecordingDto:
<?xml version="1.0" encoding="utf-8" ?> <xs:schema id="RecordingDto" targetNamespace="http://msdn.microsoft.com/practices/RecordingDto.xsd" elementFormDefault="qualified" attributeFormDefault="qualified" xmlns="http://tempuri.org/RecordingDTO.xsd" xmlns:mstns="http://msdn.microsoft.com/practices/RecordingDto.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:codegen="urn:schemas-microsoft-com:xml-msprop"> <xs:element name="RecordingDto" msdata:IsDataSet="true"> <xs:complexType> <xs:choice maxOccurs="unbounded"> <xs:element name="recording" codegen:typedName="Recording" codegen:typedPlural="Recordings" codegen:typedChildren="Track"> <xs:complexType> <xs:sequence> <xs:element name="id" type="xs:long" codegen:typedName="Id" /> <xs:element name="title" type="xs:string" codegen:typedName="Title" /> <xs:element name="artist" type="xs:string" codegen:typedName="Artist" /> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="track" codegen:typedName="Track" codegen:typedPlural="Tracks" codegen:typedParent="Recording"> <xs:complexType> <xs:sequence> <xs:element name="id" type="xs:long" codegen:typedName="Id" /> <xs:element name="title" type="xs:string" codegen:typedName="Title" /> <xs:element name="duration" type="xs:string" codegen:typedName="Duration" /> <xs:element name="recordingId" type="xs:long" codegen:typedName="RecordingId" /> </xs:sequence> </xs:complexType> </xs:element> </xs:choice> </xs:complexType> <xs:unique name="RecordingDTOKey1" msdata:PrimaryKey="true"> <xs:selector xpath=".//mstns:recording" /> <xs:field xpath="mstns:id" /> </xs:unique> <xs:unique name="RecordingDTOKey2" msdata:PrimaryKey="true"> <xs:selector xpath=".//mstns:track" /> <xs:field xpath="mstns:id" /> </xs:unique> <xs:keyref name="recordingtrack" refer="mstns:RecordingDTOKey1"> <xs:selector xpath=".//mstns:track" /> <xs:field xpath="mstns:recordingId" /> </xs:keyref> </xs:element> </xs:schema>
This schema is not the exact file produced by Visual Studio .NET. It is annotated with a number of attributes that are prefixed from the codegen namespace. This modification is desirable because the code that is generated does not adhere to the .NET naming conventions. For example, without the modification, Visual Studio .NET would generate a track class that corresponds to the track table, whereas according to conventions used in the .NET Framework the class should be named Track. To change the name of the class that is generated, you must add the codegen:typedName attribute to the element definition in the XML schema:
<xs:element name="track" codegen:typedName="Track"> </element>
There are a number of other attributes besides codegen:typedName. For a detailed description of all the attributes, see "Typed DataSets in ADO.NET" from the May 2001 issue of .NET Developer [Wildermuth02].
Filling a Typed DataSet from the Database
The following code example demonstrates how to fill a typed DataSet with the data that the sample application requires. This includes the specific recording record and all of its associated track records. The difference between this code and filling an ordinary DataSet is that you do not need to explicitly define the relationship between the recording and track records.
Assembler.cs
Just as in Implementing a Data Transfer Object in .NET with a DataSet, an Assembler class maps the actual database calls into the typed DataSet:
using System; using System.Data; using System.Data.SqlClient; using Recording; public class Assembler { public static RecordingDto CreateRecordingDto(long id) { string selectCmd = String.Format( "select * from recording where id = {0}", id); SqlConnection myConnection = new SqlConnection( "server=(local);database=recordings;Trusted_Connection=yes;"); SqlDataAdapter myCommand = new SqlDataAdapter(selectCmd, myConnection); RecordingDto dto = new RecordingDto(); myCommand.Fill(dto, "recording"); String trackSelect = String.Format( "select * from Track where recordingId = {0} order by Id", id); SqlDataAdapter trackCommand = new SqlDataAdapter(trackSelect, myConnection); trackCommand.Fill(dto, "track"); return dto; } }
Note: The example shown here is not meant to describe the only way to fill the typed DataSet. There are many ways to retrieve this data from the database. For example, you could use a stored procedure.
Using a Typed DataSet in an ASP.NET Page
As mentioned previously, a typed DataSet inherits from System.Data.DataSet. This means that it can be substituted for a DataSet. For example, when using the .NET user interface controls (Web Forms or Windows Forms) a typed DataSet can be used in all places you could use a DataSet. The sample application page shown in the following code example uses two DataGrid controls, RecordingGrid and TrackGrid. You can use the typed DataSet, RecordingDto when setting the DataSource properties on the controls because a typed DataSet inherits from DataSet.
using System; using System.Data; using RecordingApplication.localhost; public class RetrieveForm : System.Web.UI.Page { private RecordingCatalog catalog = new RecordingCatalog(); // protected void Button1_Click(object sender, System.EventArgs e) { string stringId = TextBox1.Text; long id = Convert.ToInt64(stringId); RecordingDTO dto = catalog.Get(id); RecordingGrid.DataSource = dto.recording; RecordingGrid.DataBind(); TrackGrid.DataSource = dto.track; TrackGrid.DataBind(); } }
Tests
Because the typed DataSet is generated by tools in the .NET Framework, you do not need to write tests to verify that it functions correctly. In the following tests, you are testing that the Assembler class loaded the typed DataSet correctly.
AssemblerFixture.cs
using NUnit.Framework; using System.Data; using Recording; [TestFixture] public class AssemblerFixture { private RecordingDto dto; private RecordingDto.Recording recording; private RecordingDto.Track[] tracks; [SetUp] public void Init() { dto = Assembler.CreateRecordingDto(1234); recording = dto.Recordings[0]; tracks = recording.GetTracks(); } [Test] public void RecordingCount() { Assert.Equals(1, dto.Recordings.Rows.Count); } [Test] public void RecordingTitle() { Assert.Equals("Up", recording.Title.Trim()); } [Test] public void RecordingChild() { Assert.Equals(10, tracks.Length); foreach(RecordingDto.Track track in tracks) { Assert.Equals(recording.Id, track.RecordingId); } } [Test] public void TrackParent() { RecordingDto.Track track = tracks[0]; RecordingDto.Recording parent = track.Recording; Assert.Equals("Up", parent.Title.Trim()); } [Test] public void TrackContent() { RecordingDto.Track track = tracks[0]; Assert.Equals("Darkness", track.Title.Trim()); } [Test] public void InvalidRecording() { RecordingDto dto = Assembler.CreateRecordingDto(-1); Assert.Equals(0, dto.Recordings.Rows.Count); Assert.Equals(0, dto.Tracks.Rows.Count); } }
These tests describe how to access the individual elements of the DataSet. Because of the use of a typed DataSet, the test code does not require the actual column names and does not require the return type to be cast. Comparing these tests with the ones described in Implementing Data Transfer Object in .NET with a DataSet reveals the differences between using a strongly-typed interface and a generic interface. The strongly-typed interface is easier to use and understand. It also provides the added benefit of compile-time checking on return types.
Resulting Context
Implementing DTO with a typed DataSet shares a number of the same benefits and liabilities as implementing DTO with a DataSet; however, certain benefits and liabilities are unique to a typed-DataSet implementation.
Benefits
The typed DataSet shares the following benefits with a DataSet when used as a DTO:
Development tool support. Because the DataSet class is implemented in ADO.NET, there is no need to design and implement the DTO. There is also extensive support in Visual Studio for automating the creation and filling of DataSet and typed-DataSet objects.
Integration with controls. A DataSet works directly with the built-in controls in Windows Forms and Web Forms, making it a logical choice as a DTO.
Serialization. The DataSet comes complete with the ability to serialize itself into XML. Not only is the content serialized, but the schema for the content is also present in the serialization.
Disconnected database model. The DataSet represents a snapshot of the current contents of the database. This means that you can alter the contents of the DataSet and subsequently use the DataSet as the means to update the database.
An additional benefit that might persuade you to use a typed DataSet as opposed to an ordinary DataSet is the strongly-typed interface of the typed DataSet. A typed DataSet, as described here, generates classes that can be used to access the contained data. The classes present an interface which defines how the class is to be used in a more explicit manner. This removes the need for casting which was present in the DataSet implementation.
Liabilities
The typed DataSet shares the following liabilities with a DataSet when used in the context of a DTO:
Interoperability. Because the DataSet class is part of ADO.NET, it is not the best choice for a DTO in cases requiring interoperability with clients that are not running the .NET Framework.. You can still use DataSet, however, the client will be forced to parse the XML and build its own representation. If interoperability is a requirement, see Implementing Data Transfer Object in .NET with Serialized Objects.
Stale data. The typed DataSet, like a DataSet, is disconnected from the database. It is filled with a snapshot of the data in the database when it is constructed. This implies that the actual data in the database may be different from what is contained in the typed DataSet. For reading primarily static data, this is not a major issue. If the data is constantly changing, however, using any kind of DataSet is not recommended.
Potential for performance degradation. Instantiating and filling a DataSet can be an expensive operation. Serializing and deserializing a DataSet can also be very time consuming. A good rule of thumb for using a DataSet is that a DataSet is a good choice when you are using more than one table or relying on the capability of the DataSet to update the database. If you are displaying the results from a single table, then using a DataReader with strongly-typed objects may offer better performance. For more information, see Implementing Data Transfer Object in .NET with Serialized Objects.
The following are additional liabilities when using a typed DataSet as opposed to an ordinary DataSet:
A typed DataSet is still a DataSet. A typed DataSet can be substituted at runtime with a DataSet. This means that even though the strongly-typed interface exists, programmers can still access the data without the typed interface. A possible result of doing this is that there could be parts of the code which couple the application tightly to the DataSet table and column names.
The need for an XML schema. When using a typed DataSet you have to create and maintain an XML schema to describe the strongly-typed interface. Visual Studio .NET provides a number of tools to assist in this process, but nevertheless you still have to maintain an additional file.
Related Patterns
For more information, see the following related patterns:
Implementing Data Transfer Object in .NET with Serialized Objects.
Assembler. In Enterprise Application Architecture Patterns, Fowler defines Assembler as a specialized instance of the Mapper pattern [Fowler03].
Acknowledgments
[Beau02] Beauchemin, Bob. Essential ADO.NET. Addison-Wesley, 2002.
[Fowler03] Fowler, Martin. Enterprise Application Architecture Patterns. Addison-Wesley, 2003.
[Wildermuth01] Wildermuth, Shawn. "Typed DataSets in ADO.NET." .NET Developer. May 2001.
蒲永?发表?2005-3-30 8:26:00 |
版权声明Q可以Q意{载,转蝲时请务必以超链接形式标明文章原始出处和作者信息及本声?BR>
http://www.chedong.com/tech/cms.html关键词:"content manage system" cms 内容理pȝ
内容摘要Q?BR>
内容理pȝ是一个很泛的概念Q从商业门户|站的新ȝl到个h的Weblog都可以称作发布系l?BR>
但无论如何,在发布系l选型之前Q首先了解自q实际需求是最重要的:x据现成系l将自己的需求硬往上照搬是非常不可取的。访问量Q权限控制和各种功能需求。每个模块和功能自己都比较清C点以后,再去|上找找cM的实玎ͼ你会发现其实每个环节到目前上都有比较成熟的实CQ而且q在不断完善和发展中Q如果没有:你的需求太ҎQ或者可以尝试分解成更小的系l组合实现?BR>
内容理pȝ被分L以下几个层面Q各个层面优先考虑的需求不?BR>
内容理和表现的分离Q很多成套的CMSpȝ没有把后台各U子pȝ和Portal分离开设计Q以至于在Portal层的模板表现理和新dpȝ的内容管理逻辑混合在一P甚至和BBS{子pȝ的管理都耦合的非帔RQ整个系l会昑־非常庞杂。而且q样的系l各个子pȝ捆绑的比较死Q如果后台的模块很难改变。但是如果把后台各种子系l内容管理逻辑和前台的表现/发布分离后,Portal和后台各个子pȝ之间只是数据传递的关系QPortal只决定后台各个子pȝ数据的取舍和表现Q而后台的各个子系l也都非常容易插拔?BR>
内容理和数据分发的分离Q需要要Portalpȝ设计的时候注?/A>可缓存性(Cache FriendlyQ性设?/A>QCMS后台理和发布机Ӟ本n不要q多考虑“效率”问题,只要最l页面输计的比较CacheableQ效率问题可通过更前端专门的~存服务器解冟?BR>
此外Q就是除了面向最l浏览器用户外,q要注意面向搜烦引擎友好(Search engine Friendly)的URL设计Q通过URL REWRITE转向或基于PATH_INFO的参数解析得动态网在链接QURIQŞ式上更像静态的目录l构Q方便网站内容被搜烦引擎收录Q?BR>
--------------- --------------- ---------------
|新闻理子系l| | BBS论坛子系l| | 商城子系l? |
--------------- --------------- ---------------
| \ / | \ / ?
| ----------|--- \ / ? <== 业务子系l(零g生Q?BR> | / | \ \ / ?
--------------- | --------------- ?
|专题制作子系l| | |全文索子pȝ|
--------------- | ---------------
\ | /
-------------------------|---------------------------------------------
\ | / ?
--------------- ? <== PortalpȝQ品组装)
| Portal pȝ | ?
--------------- ?
|
-------------------------|---------------------------------------------
| ?
| ? <== 发布pȝQ分发代理)
--------------- ?
|前台发布pȝ | ?
---------------
/ \
/ \
--------------- ---------------
| 用户览? | |Search Engine|
--------------- ---------------
q里Q我把在内容发布pȝ选型中找到的一些资料ȝ如下Q?
关键词:CMS Content Manage System
CMS行业研究
CMS讨论邮g列表
商业软g和开源项目列表:
推荐Q基于XML的发布框?BR>
http://cocoon.apache.org/具体实现复杂E度可能会根据需求的不同而不同,但越是大型的pȝ是需要分工:内容(数据Q,表现Q模板)和应用逻辑Q程序)可能分d对这3 者的理?BR>
Portal---门户pȝ
Open source Projects:
关键词:ad server
q告pȝ和内容的分离Q可以大大降低系l之间的兌度?BR>
专业行业研究|站Q?BR>
推荐Q?BR>
http://www.phpadsnew.com/ 功能性比较强关键词: BBS FORUM
论坛软g介绍Q?BR>
在内容的录入理斚wQ所见即所得是比较重要的,q样可以大大化对pȝ中布局排版的需求?BR>Z览器的WYSIWYGQ所见即所得)~辑器是CMS设计中,~辑工具的主要考虑斚w。目前主要是通过JAVASCRIPT调用IE或其他浏览器的内|方法实现。其中基于IE5.5览器的实现最为简z。这里有一个完整的例子Q?BR>
Building a WYSIWYG HTML Editor Part 1/2选型指标Q?BR>
可视化编辑器大全Q?BR>
http://www.bris.ac.uk/is/projects/cms/ttw/ttw.html囄和文件等非结构化数据q是分别使用另外的服务解x较好。这样可以大大简化CMS本n的复杂程度?BR>
推荐Q?BR>文g上传Q?BR>
当前FCKeditor对IE的支持很好,不过在Mozilla Firefox中不能正常工作。从官方|站上得到的消息表明FCKeditor的开发小l正在实现其对Mozilla的支持?BR>http://www.lividecay.org/doc_view.php?doc_id=1123
http://www.redsaga.com/mambo/content/view/12/2/
在jsp下用FCKeditor是非常容易的Q?在FCKeditor的表中带有一个在JSP下用TaglibQ通过调用q个taglibQ?很容易用FCKeditorQ?下面q单说一下它的?BR>FCKeditor在Future使用指南
toolbarSet的定义是非常Ҏ的, 在FCKeditor中用到的所有功能按钮对是有唯一的名U的Q?q个名称你可以在FCKeditor帮助文g或?fckeditor/js/fck_config.js中定义的Default toolbarSet中找?
1.2
http://www.freetextbox.com/ 不过好像不支持jsp?BR>
Javascript实现?BR>http://tinymce.moxiecode.com/
2.
一个在U字?
http://livid.3322.org/lividict/livid.html
3.
开源的Zphp的项目:
http://xoops.org.cn/modules/wfdownloads/singlefile.php?cid=4&lid=222
http://xoops.org.cn/modules/wfdownloads/topten.php?list=hit
4.
http://forum.javaeye.com/viewtopic.php?t=2047&highlight=cvs
http://forum.javaeye.com/viewtopic.php?t=8360&highlight=cvs
http://sourceforge.net/cvs/?group_id=94626
5.
http://www.blog.edu.cn/blog.asp?name=yonghui
http://www.magnolia.info/en/community.html
6.
http://duduwolf.winzheng.com/
http://www.bris.ac.uk/is/projects/cms/ttw/ttw.html
http://www.aine.be/aynhtml/
6.
http://www.jdsl.org/
http://www.cmswiki.com/tiki-index.php?page=JSR170
http://jsr170tools.day.com/crx/index.jsp
7.
目理Q?BR>http://www.codeproject.com/tools/ToDoList2.asp
http://www.moon-soft.com/doc/45036.htm
http://www.moon-soft.com/doc/45036.htm
http://www.neokeen.com/mornlee/2005/02/23/1109168404234.html
http://cosoft.org.cn/forum/forum.php?forum_id=6559
http://blog.csdn.net/kasam/archive/2004/12/30/234644.aspx
http://www.redsoftfactory.com/chinese/opensource/opensource.html
http://forum.javaeye.com/viewtopic.php?t=2300
http://forum.javaeye.com/viewtopic.php?t=8360
8?BR>
http://java-source.net/open-source/content-managment-systems