1.5 SharePoint Foundation的服务器端对象模型
在SharePoint 2010中需要特别强调“服务器端”,这是因为在这个新版本里引入了客户端对象模型。SharePoint 2010客户端模型通俗地理解是服务器端模型的一个子集,在能使用的API以及权限上都有限制,关于客户端模型的开发后续会有详细解释。
1.5.1 概述
SharePoint 2010服务器端对象模型是SharePoint开放给用户的编程接口,对SharePoint的数据访问以及操作都需要通过这些接口来进行访问,从ASP.NET开发转过来的开发者一定要避免直接访问数据库的冲动,所有对SharePoint的定制都需要通过对象模型来操作,尽管有时候直接访问数据库操作数据可以绕开SharePoint产品的限制。
需要使用SharePoint 2010对象模型的第一步是在解决方案里添加对于Microsoft. SharePoint.DLL的引用,这个DLL位于安装目录,通常是C盘下的如下路径:C:\Program Files\Common Files\Microsoft Shared\web server extensions\14(以下简称14)的ISAPI文件夹下面。
更多信息:如有过上一个版本开发经验的开发者可能会产生疑问,为什么上一个版本是在相同路径的12文件夹下面,这个版本就直接跳到14了,其实这是西方人对13这个数字的迷信。
在Visual Studio 2010里开发SharePoint项目有两点尤其需要注意,第一点是项目配置里一定要选择.NET 3.5作为目标框架,对很多项目类型,Visual Studio 2010默认使用.NET 4.0作为目标框架;第二点是目标平台要选X64兼容。
还有需要注意的地方是,通过服务器端对象模型开发的程序必须在Web前端服务器或者应用程序服务器上才能运行,这点和SharePoint2007是一致的。如果需要在客户端通过代码去访问生产环境,在SharePoint 2010里可以使用客户端对象模型或者SharePoint开放出来的一系列Web服务(稍后会做介绍),在SharePoint2007里主要是通过SharePoint开放的Web服务接口。
我们可以用Visual Studio 2010新建一个空白的SharePoint项目(如前文所述,在Visual Studio 2010里可以直接开发SharePoint 2010和2007版本的项目,而无须再安装其他插件),按照以下的介绍进行练习。
1.5.2 创建一个HelloWorld控制台程序
如同任何新的编程语言或者技术平台,我们从创建一个简单的HelloWorld程序开始了解服务器端对象模型。
打开Visual Studio 2010创建一个叫做HelloWorld的控制台程序,务必确认选择.NET 3.5作为目标框架,如图1-24所示。
图1-24 选择.NET 3.5作为运行框架
在创建好的项目中右键单击项目名,单击属性链接,进入属性设置窗口,选中左侧的Build*选项卡,选择“X64”作为目标平台,如图1-25所示。
图1-25 选择目标平台
添加Microsoft.SharePoint类库到项目引用中,此库位于C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\API\Microsoft.SharePoint.dll,并在Program.cs类文件的开头添加对此库的引用using Microsoft.SharePoint,如图1-26所示。
图1-26 添加SharePoint类库
现在,所有的准备工作都已经做好,可以开始在Program.cs类文件里书写代码了。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.SharePoint; namespace HelloWorld{ class Program { static void Main(string[] args){ const string siteURL="http://localhost"; using (SPSite site=new SPSite(siteURL)){//获取对站点集对象的引用 SPWeb web=site.RootWeb;//获取对根站点的引用 foreach (SPList list in web.Lists){//遍历根站点下所有的列表 if (!list.Hidden){//如果该列表不是隐藏列表 Console.WriteLine(list.Title); } } } } } }
上述代码会建立对http://localhost网站集的引用,并打开其根站点(网站),紧接着会遍历其中的所有列表,如果该列表是非隐藏列表,则将其标题输出到控制台窗口。注意,在创建对网站集的引用SPSite对象时,一定要在使用完成后对对象予以释放,SPSite使用了很多非托管资源,如果不释放会造成内存的泄露。详细的注意事项可以参考附录。
更多信息:SharePoint可以通过代码修改列表、文档库(记住文档库也是一种特殊的列表),设置其属性为Hidden。生效后用户将不会在浏览器窗口看到相应列表的链接,但用户仍然可以使用绝对URL查看相应列表。SharePoint工作流历史数据存储列表Workflow History就是很明显的一个例子。
1.5.3 站点集和站点
站点集和站点是SharePoint开发容易搞混的两个概念,站点集对应前面所述的网站集,站点对应的是前面所述的网站集下的网站,但是站点集的对象是Microsoft.SharePoint. SPSite,而站点对应的对象是Microsoft.SharePont.SPWeb,初学者很容易将SPSite理解成站点,将SPWeb理解成站点集,这个在开发的时候需要多加注意。
如HelloWorld里面的例子,我们可以非常容易地调用SPSite和SPWeb来访问SharePoint的数据。
获取站点集对象的引用:
SPSite site=new SPSite(“SiteURL”); //根据提供的URL获得对站点集对象的引用。
获取站点对象的引用方式有很多,但都需要在创建了SPSite对象的基础上才能获取站点对象,这一点可以这样理解,站点集对象相当于父亲,站点对象相当于儿子,没有父亲的对象怎么去创建儿子的对象?当然有人会说直接把儿子的URL传给儿子的构造函数不就可以了吗?但SharePoint有很多狡猾的地方,比如给出两个URL:
http://localhost/au/corporate
http://localhost/enterprise/sales
这两个URL分别对应的应该是站点集还是站点呢?其实两种理解都不会有错,如果au和enterprise是管理路径(SharePoint里用来给站点集分门别类用的设置,可以在管理中心配置),那么这两个URL就都是站点集的URL,如果au和enterprise本身就是站点集的名字,那么这两个URL对应的就应该是站点了。
回到我们的主题,只有在创建了站点集对象的引用后才能创建对站点的引用(稍后会介绍从运行时的上下文环境里直接获得SPWeb对象的引用),有如下常用方式:
SPWeb web=site.OpenWeb(“WebURL”); //返回某个指定站点的SPWeb对象 SPWeb web=site.RootWeb; //返回根站点的SPWeb对象 //遍历站点集下的每一个站点 SPSite site=new SPSite(“SiteURL”); //调用SPSite的AllWebs成员返回所有SPWeb的集合对象SPWebCollection foreach(SPWeb web in site.AllWebs){ Console.WriteLine(“Web site: {0}”,web.Url); web.Dispose();//通过AllWebs取得的SPWeb对象要注意释放 }
如果代码运行在SharePoint Foundation的环境下,比如运行在某个SharePoint站点画面内的Web部件或者跟某个列表绑定的事件处理等情况下,可以直接调用上下文环境获得对站点集或者站点对象的引用,但请注意从上下文中获得的站点集或站点对象不应释放,否则会造成程序异常运行。
SPSite site=SPContext.Current.Site; //获取当前所在站点集对象 SPWeb web=SPContext.Current.Web; //获取当前所在站点对象。
更多关于编程时对象释放的问题请参考附录。
1.5.4 列表和文档库
列表是存储数据的容器,在对象模型里对应的是Microsoft.SharePoint.SPList,列表的集合对象是Microsoft.SharePoint.SPListCollection,假定我们已经获得对SPWeb站点对象的引用Web对象,可以通过以下方法获得对列表对象的引用:
SPListCollection lists=web.lists; //获取站点下的所有列表集合 SPList list=lists[index]; //通过索引获得对列表对象的引用 SPList list=lists[“Announcement”]; //通过列表名获得对对象的引用 //通过列表ID获得对对象的引用 Guid listID=new G uid(“{1893wr25-n900-e230-606b-df14bbi4512}”); SPList list=lists[listID]; //通过列表URL获得对对象的引用 SPList list=web.GetListFromUrl[“http://localhost/lists/announcements/allitems.aspx”]; //通过服务器相对路径获得对对象的引用 SPList list=Web.GetList[“/lists/announcements”];
但以上获取列表对象的方法有一个共同的问题是,如果列表不存在的时候会抛出System.IO.FileNotFoundException的异常,需要在逻辑处理时予以特别设计,而在SharePoint2010里增加了一个新的方法Microsoft.SharePoint.SPListCollection.TryGetList(string listTitle),此方法在获取列表失败后返回NULL,便于之后对返回的列表对象进行判断,是否继续执行代码而不是直接跳到异常处理代码块。
SPListCollection lists=web.lists; SPList list=lists.TryGetList(“Announcements”);
如同前面在介绍功能的时候介绍到的,文档库是一种特殊的列表,事实上从对象模型角度,我们更能这么说,查看SharePoint Foundation API文档,我们会发现文档库对象Microsoft.SharePoint.SPDocumentLibrary正是继承自列表对象Microsoft.SharePoint.SPList。
在大多数的情况下,我们都可以直接通过SPList对象来操作文档库,只有在特殊的情况下,例如下例所示,需要获取某文档库内所有被签出的文件列表的时候,我们才需要用到SPDocumentLibary对象。
SPWeb web=SPContext.Current.Web ; //注意SPWeb没有直接返回文档库对象的方法,需要先获取SPList SPList list=web.Lists[“Shared Documents”]; //将列表对象转换成文档库对象 SPDocumentLibrary doclib=(SPDocumentLibrary)list; //获取所有被签出的文件 IList<SPCheckedOutFile> checkedOutFiles=doclib.CheckedOutFiles;
1.5.5 列表项和文件
获取了列表对象后,我们要对存储在列表的数据进行操作了,列表里的数据由一条一条的记录组成,而记录对应的对象就是Microsoft.SharePoint.SPListItem,相应的记录集合对应的对象是Microsoft.SharePoint.SPListItemCollection。
SPList list=SPContext.Current.Web.Lists.TryGetList("Announcements"); if (list==null){ //做一些处理(比如提示错误消息)后返回; return; } //创建新的记录 SPListItem newItem=list.Items.Add(); newItem["Title"]="标题"; newItem["Body"]="正文"; newItem.Update(); //更新记录 SPListItem updateItem=list.Items[0]; updateItem["Title"]="更新后的标题"; updateItem.Update(); //删除记录 list.Items[0].Delete();
上述代码在获取了列表对象后,针对列表对象做了增加记录、更新记录以及删除记录的三种基本操作。注意到在进行数据的添加或者更新后,要调用SPListItem.Update()方法提交数据更新,否则对于数据不会被持久化的数据库,在下次读取该数据的时候还是会读到更新前的内容。
重要:特别要注意的是对SPList.Items属性的调用,该调用将返回包含所有记录所有字段内容的数据,在大数据列表中这样的操作将会导致性能上的问题。我们将在第4章详细说明对列表数据访问的各种方法。
如同列表一样,获取列表记录的方法也有多种:
SPList list=SPContext.Current.Web.Lists.TryGetList("Announcements"); //通过索引获取列表记录 SPListItem item1=list.Items[index]; //通过列表记录ID获取记录 SPListItem item2=list.Items.GetItemById(1); SPListItem item3=list.GetItemById(1);
上述例子中无疑item3的获取方式要比item2的获取方式要好,因为它避免了对Items方法的调用,减少了对内存的占用。
在获得列表记录后,可以通过File属性获取文件对象。但是需要留意的一点是,也可以通过SPWeb对象来直接获取文件,理解这一点要明白文档库实际上只是SharePoint站点里的一个抽象层,这就意味着如果要访问文档库里的某个文件,实际上是在访问SharePoint站点里某个指定位置的文件,因此我们可以通过两种方式来对文件进行操作。
首先,我们尝试通过SPList对象来对文件进行操作:
using (SPSite site=new SPSite("http://localhost")){ SPWeb web=site.RootWeb; SPList list=web.Lists.TryGetList("Shared Documents"); if (list==null){ Console.WriteLine("Shared Documents not exist!"); return; } //读取文档内容到字节集中 byte[] fileData=System.IO.File.ReadAllBytes(@"c:\test.txt"); //调用SPFileCollection对象的Add方法上传文档 SPFile newFile=list.RootFolder.Files.Add("test.txt",fileData); }
上例中我们首先获得了SPList对象,然后通过RootFolder返回列表下的根文件夹对象,再通过其返回所有的文件集合对象SPFileCollection,之后调用SPFileCollectioin.Add (string fileURL,byte[] file)将文件写入文档库中。
接下来是从文档库读取文件的例子:
using (SPSite site=new SPSite("http://localhost")){ SPWeb web=site.RootWeb; SPList list=web.Lists.TryGetList("Shared Documents"); if (list==null) { Console.WriteLine("Shared Documents not exist!"); return; } //获得对列表记录的引用 SPListItem item=list.GetItemById(1); //获得SPFile对象的引用 SPFile file=item.File; //将文件内容读到字节集合里 byte[] fileData=file.OpenBinary(); //由于该文件是纯文本文件,可以通过逐字的方式输出 foreach(byte byt1e in fileData){ Console.Write(byt1e); } }
下面的例子直接通过SPWeb来获取SPFileCollection对象完成上传文件的操作:
using (SPSite site=new SPSite("http://localhost")){ SPWeb web=site.RootWeb; string fileName="Mytest.txt"; string strURL="http://localhost/shared documents/"; byte[] fileData=System.IO.File.ReadAllBytes(@"c:\test.txt"); //直接通过SPWeb对象获取对SPFileCollection的引用后,调用Add方法上传文件 web.Files.Add(strURL+fileName,fileData); }
1.5.6 开发人员面板
要介绍一下SharePoint 2010的新功能——开发人员面板,这个功能激活后会在画面的下部输出画面加载的细节信息。这个功能对于开发人员还有SharePoint管理人员会有用,它可以帮助分析画面的性能问题等。图1-27显示了加载某站点主页时的加载信息。
图1-27 开发人员面板
这个功能默认是不用激活的,如果需要激活,在SharePoint 2010里,通常建议通过Windows PowerShell来激活,考虑到我们在第2章才会介绍PowerShell,这里提供一个简单的传统STSADM方式来激活。
更多信息:初次接触SharePoint的开发人员可能对STSADM没有什么概念。早前的SharePoint版本就已引入STSADM,用来帮助实现一些管理任务,例如部署解决方案、设置服务器属性等。需要使用此命令,可以在C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\BIN找到。
开发人员面板有四种模式,在命令窗口运行STSADM命令可以设置不同的状态。
- 打开模式:这种模式下每个页面加载时都会在画面下部加载此面板。命令:stsadm -o setproperty -pn developer-dashboard -pv On
- 管理模式:这种模式下不会加载此面板。命令:stsadm -o setproperty -pn developer-dashboard -pv Off
- 按需模式:这种模式下在画面下部会显示一个图标,单击此图标可以显示或隐藏开发人员面板。
- 命令:stsadm -o setproperty -pn developer-dashboard-pv OnDemand
- 混合模式:当某些系统计数器指标超过时显示面板。命令:stsadm-o setproperty-pn developer-dashboard-pv expensiveoperationly
学过第2章后可以使用PowerShell改变属性设置:
$DevDashboardSettings=[Microsoft.SharePoint.Administration.SPWebService]::ContentService. DeveloperDashboardSettings; $DevDashboardSettings.DisplayLevel='OnDemand'; $DevDashboardSettings.RequiredPermissions='EmptyMask'; $DevDashboardSettings.TraceEnabled=$true; $DevDashboardsettings.Update
注意,此设置是在服务器场级别的,也就是更改设置以后对一个服务器场内的所有Web应用程序都会起效。另外,细心的读者会留意到一个小细节,图1-27所示的开发人员面板边框是绿色,当画面加载性能差的时候边框会显示成黄色。