探索 SharePoint 2010 客户端对象模型功能
本文讨论了强大的 SharePoint 2010 客户端对象模型功能及其各种使用方式。
下载 理解 SharePoint 2010 客户端对象模型功能 - 5.29 MB
引言
SharePoint 2010 确实令人兴奋,如果您已经使用过 SharePoint 的早期版本,那么毋庸置疑,在探索其新功能时,您会感到更加兴奋。每个平台都有一些缺点,但其功能至关重要,我们开发人员总是喜欢探索软件或平台提供的新事物和新功能。SharePoint 2010 拥有一些出色而酷炫的新功能,但很遗憾我没有机会使用所有这些功能,但我设法学习了一些关于 SharePoint 2010 客户端对象模型的东西。在本文中,我将分享我迄今为止(仍在学习中)关于 SharePoint 2010 客户端对象模型的所学。完整的源代码在两个项目中提供,您可以下载并尝试在您这边编译和执行。
欢迎使用 SharePoint 2010 客户端对象模型
如果您查看过 SharePoint 2010 的用户界面,那么您可能已经注意到,Microsoft 通过大幅减少页面刷新和使用 Silverlight 来真正增强了用户体验。SharePoint 需要一些新功能来满足自身改进用户界面的需求,并提供一个更好、更强大的平台来创建可以在 SharePoint 服务器之外运行的解决方案。客户端对象模型作为实现此目的的关键功能之一被引入。它帮助开发人员更轻松地构建与 SharePoint 交互的客户端应用程序,而无需使用 SharePoint Web 服务和 RPC 调用。
SharePoint Web 服务有什么问题?
下图描述了 MOSS 2007 中访问数据的流程
以前,您必须使用服务器端对象模型、Web 服务或 RPC 调用来与 MOSS 2007 交互。特别是,如果您希望 Silverlight 应用程序从 SharePoint 获取数据,那么您必须仔细思考并花费大量时间来完成。此外,您不能总是将 SharePoint Web 服务用作解决方案,原因如下:
· 基于 XML 的 SOAP 通信存在封送和解封送的开销。
-
会话未经身份验证,所有请求都必须经过身份验证(ExcelServices Web 服务的 OpenWorkbook 方法除外)。
-
无法获取所有网站集或 Web 应用程序的列表。
-
您无法获取 SharePoint 服务器的版本。
-
从 SharePoint 服务器下载文件时内存开销很大。
-
检索项目级别权限时存在限制。
SharePoint 2010 客户端对象模型有什么优点?
下图描述了使用客户端对象模型访问 SharePoint 2010 数据更简便的方式
有许多优点,如下所示:
· 用于在 SharePoint 2010 中执行 CRUD 操作的简单 API。
· 支持三个主要的客户端开发平台
-
.NET 管理对象模型
-
ECMAScript 对象模型 (JavaScript)
-
Silverlight 客户端对象模型
· 三个平台之间实现一致且惊人地相似。
· 更好、更丰富的用户体验,而不是传统的 ASP.NET 回发驱动的用户界面。
· 大部分处理发生在客户端机器上,从而减少了对服务器的影响。
· 调用批量执行以最小化往返次数。
· 异步调用以确保稳定和响应迅速的用户体验。
· 类似于服务器对象模型的结构和命名约定。
SharePoint 2010 客户端对象模型背后的机制
下图显示了 SharePoint 客户端对象模型背后的基本机制
如果您使用 API(应用程序编程接口)访问 SharePoint 2010 数据,则 SharePoint 客户端对象模型会将请求打包成 XML 格式,然后将 XML 请求发送到服务器。服务器接收请求,解析并调用 SharePoint 服务器对象模型。然后它收集响应,解析并打包成 JSON (JavaScript Object Notation) 对象,然后将其发送回 SharePoint 托管客户端对象模型。SharePoint 托管客户端对象模型解析 JSON 结果,然后将其发送回 .NET 托管对象模型或 ECMA 脚本模型。请查看上面显示的图表。
SharePoint 2010 客户端对象模型的种类
SharePoint 客户端对象模型有三种形式。对于 .NET 客户端应用程序,可以使用 .NET 托管对象模型。Silverlight 应用程序可以使用 Silverlight 客户端对象模型,对于 JavaScript 实现,可以使用 ECMAScript 对象模型。由于这三种模型,应用程序的丰富性更高,开发变得更容易,并且客户端应用程序不需要依赖 SharePoint Web 服务和 RPC 调用。
客户端类及其等效的服务器端类
服务器端类 |
客户端类 |
||
SPContext |
ClientContext |
||
SPSite |
Site |
||
SPWeb |
Web |
||
SPList |
列表 |
||
SPListItem |
ListItem |
||
SPField |
字段 |
.NET 托管客户端对象模型
打开 Visual Studio 2010 并创建一个新的空 SharePoint 项目。给一个有意义的名称并指定将创建项目的路径
指定将调试应用程序的站点。请记住选择“部署为场解决方案”选项,因为可视 Web 部件不能与沙盒解决方案一起使用。 单击“完成”创建新项目
下一步是将可视 Web 部件添加到项目中。 将可视 Web 部件的名称指定为“ClientOMWebPart”。
在可视 Web 部件的 ascx 文件中,添加以下代码片段
完整的源代码在可下载项目中提供
<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %> <%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %> <%@ Import Namespace="Microsoft.SharePoint" %> <%@ Register Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="UIDemoUserControl.ascx.cs" Inherits="ClientOMUIActions.UIDemo.UIDemoUserControl" %> <style type="text/css"> .style1 { height: 23px; } .style2 { height: 25px; } </style> <script type="text/javascript"> //Dialog opening function OpenListDialog() { var options = SP.UI.$create_DialogOptions(); options.url = "/_layouts/ClientOMUIActions/ListCreationPage.aspx"; options.width = 600; options.height = 160; options.dialogReturnValueCallback = Function.createDelegate(null, CloseListCallback); SP.UI.ModalDialog.showModalDialog(options); } function OpenAllListsDialog() { var options = SP.UI.$create_DialogOptions(); options.url = "/_layouts/ClientOMUIActions/ListViewerPage.aspx"; options.width = 600; options.height = 500; options.dialogReturnValueCallback = Function.createDelegate(null, CloseListCallback); SP.UI.ModalDialog.showModalDialog(options); } function OpenDocLibDialog() { var options = SP.UI.$create_DialogOptions(); options.url = "/_layouts/ClientOMUIActions/DocumentLibraryCreationPage.aspx"; options.width = 600; options.height = 160; options.dialogReturnValueCallback = Function.createDelegate(null, CloseDocLibCallback); SP.UI.ModalDialog.showModalDialog(options); } function OpenAsyncDialog() { var options = SP.UI.$create_DialogOptions(); options.url = "/_layouts/ClientOMUIActions/AsynchronusProcessing.aspx"; options.width = 800; options.height = 700; options.dialogReturnValueCallback = Function.createDelegate(null, CloseDocLibCallback); SP.UI.ModalDialog.showModalDialog(options); } function OpenCAMLDialog() { var options = SP.UI.$create_DialogOptions(); options.url = "/_layouts/ClientOMUIActions/UsingCAMLQuery.aspx"; options.width = 800; options.height = 700; options.dialogReturnValueCallback = Function.createDelegate(null, CloseDocLibCallback); SP.UI.ModalDialog.showModalDialog(options); } function OpenLINQDialog() { var options = SP.UI.$create_DialogOptions(); options.url = "/_layouts/ClientOMUIActions/UsingLINQQuery.aspx"; options.width = 800; options.height = 700; options.dialogReturnValueCallback = Function.createDelegate(null, CloseDocLibCallback); SP.UI.ModalDialog.showModalDialog(options); } function OpenSilverlightDialog() { var options = SP.UI.$create_DialogOptions(); options.url = "/SitePages/SilverlightWebPartOM.aspx"; options.width = 600; options.height = 400; options.dialogReturnValueCallback = Function.createDelegate(null, CloseDocLibCallback); SP.UI.ModalDialog.showModalDialog(options); } var messageId; // Dialog callback function CloseListCallback(result, target) { if (result === SP.UI.DialogResult.OK) { javascript: setTimeout('javascript:return true;', 8000); messageId = SP.UI.Notify.addNotification("<img src='_layouts/images/loading.gif'> Creating list <b>" + target + "</b>..."); //alert("OK was clicked!"); } if (result === SP.UI.DialogResult.cancel) { //alert("Cancel was clicked!"); SP.UI.Notify.addNotification("Operation was cancelled...", false, "", null); } } function CloseDocLibCallback(result, target) { if (result === SP.UI.DialogResult.OK) { javascript: setTimeout('javascript:return true;', 8000); messageId = SP.UI.Notify.addNotification("<img src='_layouts/images/loading.gif'> Creating document library <b>" + target + "</b>..."); //alert("OK was clicked!"); } if (result === SP.UI.DialogResult.cancel) { //alert("Cancel was clicked!"); SP.UI.Notify.addNotification("Operation was cancelled...", false, "", null); } } </script> <table bgcolor="#83BB00" border="0" cellpadding="3" cellspacing="3"> <tr> <td colspan="4"> <b>Using Client Object Model in SharePoint 2010</b> </td> </tr> <tr> <td class="style1"> </td> <td class="style1"> </td> <td class="style1"> </td> <td class="style1"> </td> </tr> <tr> <td> <strong>.NET MANAGED</strong></td> <td> <strong>ECMASCRIPT</strong></td> <td> <strong>SILVERLIGHT CLIENT</strong></td> <td> </td> </tr> <tr> <td> <a href="Javascript:OpenAsyncDialog();">Asynchronus Processing</a> </td> <td> <a href="Javascript:OpenListDialog();">Create a List </a> </td> <td> <a href="Javascript:OpenSilverlightDialog();">Using Asynchronus Client Object Model</a> </td> <td> </td> </tr> <tr> <td> <a href="Javascript:OpenCAMLDialog();">Using CAML Query</a> </td> <td> <a href="Javascript:OpenDocLibDialog();">Create a Document Library </a> </td> <td> </td> <td> </td> </tr> <tr> <td> <a href="Javascript:OpenLINQDialog();">Using LINQ with Queries</a></td> <td> </td> <td> </td> <td> </td> </tr> </table>
构建项目,部署并按 F5 键在 Visual Studio 中运行可视 Web 部件。从 Web 部件库中将自定义可视 Web 部件添加到页面。可视 Web 部件将如下所示打开
我使用了 ASP.NET Update Panel 来避免页面闪烁和完全回发,并使用 Update Progress 控件来显示正在进行的处理。进入 14 hives 下的 ISAPI 文件夹,然后将以下程序集的引用添加到您的项目中
Microsoft.SharePoint.Client.dll Microsoft.SharePoint.Client.Runtime.dll
此外,该类必须继承自 Microsoft.SharePoint.Client 命名空间。然后处理 btnCAMLQuery_Click 事件,在该事件中我们将使用客户端对象模型访问 SharePoint 服务器。
客户端上下文
它是使用 SharePoint 客户端对象模型进行编程的入口点。它与 SPContext 的工作方式相同,它确保有一个单一对象作为使用客户端对象模型的起点。ClientContext 类继承自 IDisposable 接口,因此您需要使用 using 语句或需要显式调用 IDisposable 来处置 ClientContext 对象。如果您不这样做,您可能会遇到内存泄漏问题。让我们看一下 ClientContext 类的属性和方法
名称 |
描述 |
||
处置 |
调用此方法后,它会处置 ClientContext 对象。 |
||
执行查询 |
当站点的所有查询都加载完毕后,可以调用此方法将命令发送到服务器。 |
||
executeQueryAsync |
这在 ECMAScript 对象模型中可用,它允许您执行查询并传递两个委托用于回调。一个用于查询成功返回时,另一个用于查询返回错误时。 |
||
Load (加载) |
使用 LINQ 的方法语法,您可以使用此方法加载查询。对象也可以在没有查询的情况下传递,它本身将返回对象。 |
||
加载查询 |
此方法可用于将对象集合作为 IQueryable 集合返回。它还支持 LINQ 的方法和查询语法。 |
||
身份验证模式 |
有三种身份验证模式可用 默认 – 这是默认设置,然后托管客户端对象模型使用 Windows 凭据(NTLM 和 Kerberos)对用户进行身份验证。 匿名 – 如果站点允许匿名用户,则可以使用此身份验证模式。 表单身份验证 – 如果站点使用基于表单的身份验证,则可以使用此模式。在 ClientContext 实例中,必须提供用户凭据。身份验证 Web 服务在请求对象之前使用身份验证 Cookie。 |
||
FormsAuthenticationLoginInfo |
在表单身份验证的情况下,此属性可用于设置用户 ID 和密码。 |
||
请求超时 |
获取或设置客户端请求的超时时间。 |
||
Site |
获取与 ClientContext 类关联的网站集。 |
||
URL |
获取与 ClientContext 类关联的网站 URL。 |
||
Web |
获取 ClientContext 关联的网站。 |
以下代码为指定的站点创建 ClientContext 类的实例。ClientContext 对象将其 URL 参数作为构造函数,以连接到任何网站集或 Web。然后将身份验证模式设置为默认值,即 Windows 身份验证
using (SP.ClientContext ctx = new SP.ClientContext("<a href="http://spdev/">http://SPDEV</a>"))
{
ctx.AuthenticationMode = SP.ClientAuthenticationMode.Default;
}
如果您需要表单身份验证,可以按如下所示设置 FormsAuthenticationLoginInfo 属性。
ctx.AuthenticationMode = SP.ClientAuthenticationMode.FormsAuthentication;
SP.FormsAuthenticationLoginInfo formsAuthInfo = new SP.FormsAuthenticationLoginInfo("sanjay.santra", "<a href="mailto:p@s#54?&67");ctx.FormsAuthenticationLoginInfo">p@s#54?&67");
ctx.FormsAuthenticationLoginInfo</a> = formsAuthInfo;
以下代码片段展示了如何使用匿名身份验证
ctx.AuthenticationMode = SP.ClientAuthenticationMode.Anonymous;
然后我们将创建一个 ClientContext.Web 类的对象。接下来是获取“公告”列表。通过查看实现,可以很容易地看出它与服务器端编码非常相似。
var web = ctx.Web;
SP.List list = web.Lists.GetByTitle("Announcements");
下一步是构建 CAML 查询。我们已经构建了一个 CAML 查询,如上所示,它从“公告列表”中获取列表项,并且这些项不应已过期。Microsoft.SharePoint.Client.CamlQuery 类指定了对列表的查询。ViewXml 方法获取或设置定义列表视图的 XML 架构的值。Microsoft.SharePoint.Client.List.GetItems 方法根据设置的 CAML 查询获取所有项目。
SP.CamlQuery camlQuery = new SP.CamlQuery();
camlQuery.ViewXml = @"
<View>
<Query>
<Where>
<And>
<IsNotNull>
<FieldRef Name='Title' />
</IsNotNull>
<Leq>
<FieldRef Name='Expires' />
<Value Type='DateTime'>2010-12-05T00:00:00Z</Value>
</Leq>
</And>
</Where>
</Query>
</View>";
SP.ListItemCollection listCollection = list.GetItems(camlQuery);
然后是时候通过调用 ClientContext.Load 方法并将 ListItemCollection 作为参数传递来加载查询了。然后调用 ClientContext.ExecuteQuery 方法,该方法将命令发送到服务器并执行我们设置的 CAML 查询。
ctx.Load(listCollection);
ctx.ExecuteQuery();
下一步是填充数据表并将其绑定到 SPGridView
DataTable dTable = new DataTable();
dTable.Columns.Add("ID");
dTable.Columns.Add("Title");
dTable.Columns.Add("Created");
dTable.Columns.Add("Modified");
dTable.Columns.Add("CreatedBy");
dTable.Columns.Add("ModifiedBy");
dTable.Columns.Add("Expires");
for (int iCntr = 0; iCntr < listCollection.Count; iCntr++)
{
dTable.Rows.Add(dTable.NewRow());
dTable.Rows[dTable.Rows.Count - 1]["ID"] = listCollection[iCntr]["ID"].ToString();
dTable.Rows[dTable.Rows.Count - 1]["Title"] = listCollection[iCntr]["Title"].ToString();
dTable.Rows[dTable.Rows.Count - 1]["Created"] = listCollection[iCntr]["Created"].ToString();
dTable.Rows[dTable.Rows.Count - 1]["Modified"] = listCollection[iCntr]["Modified"].ToString();
dTable.Rows[dTable.Rows.Count - 1]["CreatedBy"] = ((Microsoft.SharePoint.Client.FieldUserValue)(listCollection[iCntr]["Author"])).LookupValue;
dTable.Rows[dTable.Rows.Count - 1]["ModifiedBy"] = ((Microsoft.SharePoint.Client.FieldUserValue)(listCollection[iCntr]["Editor"])).LookupValue;
dTable.Rows[dTable.Rows.Count - 1]["Expires"] = listCollection[iCntr]["Expires"].ToString();
}
if (dTable.Rows.Count > 0)
{
spGrdMain.DataSource = dTable;
spGrdMain.DataBind();
}
else
{
dTable.Rows.Add(dTable.NewRow());
dTable.Rows[dTable.Rows.Count - 1]["ID"] = "Query returned zero results.";
}
最后,是时候运行页面了。按 F5 启动调试,并在 Web 部件页面上单击“使用 CAML 查询”链接。
点击“使用 CAML 查询获取数据”按钮来触发 CAML 查询。
返回的结果将如下所示在 SPGridView 中显示。
将 LINQ 与查询结合使用
添加另一个应用程序页并将其命名为 UsingLINQQuery.aspx。添加一个按钮和一个标签控件,它们可以封装在一个更新面板中(非强制)。此外,添加一个更新进度控件。您可以在 aspx 文件中使用以下代码
<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %> <%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %> <%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %> <%@ Import Namespace="Microsoft.SharePoint" %> <%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="UsingLINQQuery.aspx.cs" Inherits="ClientOMUIActions.Layouts.ClientOMUIActions.UsingLINQQuery" DynamicMasterPageFile="~masterurl/default.master" %> <asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server"> </asp:Content> <asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server"> <asp:UpdateProgress ID="updProgress" AssociatedUpdatePanelID="updMain" runat="server"> <ProgressTemplate> <table> <tr> <td> Loading data using LINQ query with SharePoint Managed Client Object Model... </td> <td> <img src="https://codeproject.org.cn/_layouts/ClientOMUIActions/Images/Loader1.gif" /> </td> </tr> </table> </div> </ProgressTemplate> </asp:UpdateProgress> <asp:UpdatePanel ID="updMain" runat="server"> <ContentTemplate> <asp:Button class="ms-ButtonHeightWidth" ID="btnLINQQuery" runat="server" Text="Get Data Using CAML Query" OnClick="btnLINQQuery_Click"/> <asp:Label ID="lblLists" runat="server" /> </ContentTemplate> </asp:UpdatePanel> </asp:Content> <asp:Content ID="PageTitle" ContentPlaceHolderID="PlaceHolderPageTitle" runat="server"> :::Using LINQ Query with .NET Managed Client Object Model::: </asp:Content> <asp:Content ID="PageTitleInTitleArea" ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server" > :::Using LINQ Query with .NET Managed Client Object Model::: </asp:Content>
类必须继承自 Microsoft.SharePoint.Client 命名空间。然后处理 btnLINQQuery_Click 事件,在该事件中我们将使用客户端对象模型访问 SharePoint 服务器。您可以从可下载的示例项目中获取完整代码。 大部分内容与我之前描述的(针对 CAML 查询)相同,因此我直接进入您需要定义 LINQ 查询的部分:
SP.List list = web.Lists.GetByTitle("Announcements");
var query = from listNew
in web.Lists
where listNew.Title != null
select listNew;
var listCollection = ctx.LoadQuery(query);
ctx.ExecuteQuery();
在这里,我创建了一个 LINQ 查询,它从“公告”列表中获取数据并将其放入变量中。然后可以使用 LoadQuery 方法传递 LINQ 查询。最后,调用 ExecuteQuery 方法执行查询并迭代结果。然后我们可以将集合绑定到我们为此目的使用的标签控件。
foreach (SP.List resList in listCollection)
{
lblLists.Text += "</br>" + resList.Title;
}
最终输出将如下所示:
异步处理
我们开发的两个程序都是同步的。同步代码的问题在于,当操作正在进行时,应用程序会变得无响应。在操作完成之前,用户需要等待才能使用应用程序。此问题尤其发生在 .NET 托管客户端上,而不是 Silverlight 和 ECMAScript 上,因为它们本质上是异步的。 在项目中添加一个新应用程序页。将其命名为 AsynchronousProcessing.aspx。在 aspx 页面中添加以下代码:
<asp:UpdateProgress ID="updProgress" AssociatedUpdatePanelID="updMain" runat="server"> <ProgressTemplate> <table> <tr> <td> Loading data asynchronusly using SharePoint Managed Client Object Model... </td> <td> <img src="https://codeproject.org.cn/_layouts/ClientOMUIActions/Images/Loader1.gif" /> </td> </tr> </table> </div> </ProgressTemplate> </asp:UpdateProgress> <asp:UpdatePanel ID="updMain" runat="server"> <ContentTemplate> <table class="style1"> <tr> <td colspan="5"> <asp:Button class="ms-ButtonHeightWidth" ID="btnAsync" runat="server" Text="Get All Data Asynchronusly" OnClick="btnAsync_Click"/> </td> </tr> <tr> <td style="background-color:Silver;"> <b>Available Lists</b></td> <td style="background-color:Silver;"> <b>Available List Templates</b> </td> <td style="background-color:Silver;"> <b>Available Fields</b></td> <td style="background-color:Silver;"> <b>Available Folders</b></td> <td style="background-color:Silver;"> <b>Available Role Definitions</b></td> <td style="background-color:Silver;"> <b>Available Feature Definitions</b></td> </tr> <tr> <td valign="top"> <asp:Label ID="lblLists" runat="server"></asp:Label> </td> <td valign="top"> <asp:Label ID="lblListTemplates" runat="server"></asp:Label> </td> <td valign="top"> <asp:Label ID="lblFields" runat="server"></asp:Label> </td> <td valign="top"> <asp:Label ID="lblFolders" runat="server"></asp:Label> </td> <td valign="top"> <asp:Label ID="lblRoles" runat="server"></asp:Label> </td> <td valign="top"> <asp:Label ID="lblFeatures" runat="server"></asp:Label> </td> </tr> </table> </ContentTemplate> </asp:UpdatePanel>
实现方式几乎与其他两个演示相似,但主要区别在于您需要使用 IAsyncResult 接口的 BeginInvoke 方法。定义一个字符串类型的类级别委托。
类必须继承自 Microsoft.SharePoint.Client 命名空间。然后处理 btnAsync_Click 事件,在该事件中我们将使用客户端对象模型访问 SharePoint 服务器。您可以从可下载的示例项目中获取完整代码。
delegate string AsynchronousDelegate();
基本上,在此演示中,我将获取站点中所有可用的列表、列表模板、字段、文件夹、角色定义和功能定义。填充所有这些项目需要时间,这就是为什么我们需要异步处理来帮助保持页面的响应性。代码如下所示:
AsynchronousDelegate executeQueryAsynchronouslyLists = null;
IAsyncResult asyncResultLists = null;
AsynchronousDelegate executeQueryAsynchronouslyListTemplates = null;
IAsyncResult asyncResultListTemplates = null;
AsynchronousDelegate executeQueryAsynchronouslyFields = null;
IAsyncResult asyncResultFields = null;
AsynchronousDelegate executeQueryAsynchronouslyFolders = null;
IAsyncResult asyncResultFolders = null;
AsynchronousDelegate executeQueryAsynchronouslyRoles = null;
IAsyncResult asyncResultRoles = null;
AsynchronousDelegate executeQueryAsynchronouslyFeatures = null;
IAsyncResult asyncResultFeatures = null;
try
{
executeQueryAsynchronouslyLists = new AsynchronousDelegate(GetLists);
asyncResultLists = executeQueryAsynchronouslyLists.BeginInvoke(null, null);
executeQueryAsynchronouslyListTemplates = new AsynchronousDelegate(GetListTemplates);
asyncResultListTemplates = executeQueryAsynchronouslyListTemplates.BeginInvoke(null, null);
executeQueryAsynchronouslyFields = new AsynchronousDelegate(GetFields);
asyncResultFields = executeQueryAsynchronouslyFields.BeginInvoke(null, null);
executeQueryAsynchronouslyFolders = new AsynchronousDelegate(GetFolders);
asyncResultFolders = executeQueryAsynchronouslyFolders.BeginInvoke(null, null);
executeQueryAsynchronouslyRoles = new AsynchronousDelegate(GetRoles);
asyncResultRoles = executeQueryAsynchronouslyRoles.BeginInvoke(null, null);
executeQueryAsynchronouslyFeatures = new AsynchronousDelegate(GetFeatures);
asyncResultFeatures = executeQueryAsynchronouslyFeatures.BeginInvoke(null, null);
lblLists.Text = executeQueryAsynchronouslyLists.EndInvoke(asyncResultLists);
lblListTemplates.Text = executeQueryAsynchronouslyListTemplates.EndInvoke(asyncResultListTemplates);
lblFields.Text = executeQueryAsynchronouslyFields.EndInvoke(asyncResultFields);
lblFolders.Text = executeQueryAsynchronouslyFolders.EndInvoke(asyncResultFolders);
lblRoles.Text = executeQueryAsynchronouslyRoles.EndInvoke(asyncResultRoles);
lblFeatures.Text = executeQueryAsynchronouslyFeatures.EndInvoke(asyncResultFeatures);
}
catch(Exception pEx)
{
lblLists.Text = pEx.ToString();
}
finally
{
executeQueryAsynchronouslyLists = null;
asyncResultLists = null;
executeQueryAsynchronouslyListTemplates = null;
asyncResultListTemplates = null;
executeQueryAsynchronouslyFields = null;
asyncResultFields = null;
executeQueryAsynchronouslyFolders = null;
asyncResultFolders = null;
executeQueryAsynchronouslyRoles = null;
asyncResultRoles = null;
executeQueryAsynchronouslyFeatures = null;
asyncResultFeatures = null;
}
我们将方法名称传递给委托对象,它将异步执行。然后创建 IAsyncResult 接口的对象,该对象用委托的 BeginInvoke 方法初始化。它是启动异步操作的方法的返回类型。BeginInvoke 将操作添加到队列中,然后由公共语言运行时 (CLR) 托管线程池中的单独线程进行处理。 EndInvoke 方法获取异步调用的结果。如果您正在使用 BeginInvoke,则应使用 EndInvoke 来确认由 BeginInvoke 触发的操作结果。没有 EndInvoke 的 BeginInvoke 可能会导致内存泄漏。最后,标签控件可以绑定到 EndInvoke 方法返回的结果。调用方法相对简单,因为我们已经展示了如何对 SharePoint 进行 .NET 托管客户端对象模型调用。但是,我只是为了参考而展示一个方法:
protected string GetListTemplates()
{
using (SP.ClientContext ctx = new SP.ClientContext("<a href="http://spdev/">http://SPDEV</a>"))
{
ctx.AuthenticationMode = SP.ClientAuthenticationMode.Default;
string returnValue = string.Empty;
var web = ctx.Web;
ctx.Load(web);
ctx.Load(web.ListTemplates);
ctx.ExecuteQuery();
SP.ListTemplateCollection lists = web.ListTemplates;
ctx.ExecuteQuery();
foreach (SP.ListTemplate list in lists)
returnValue += "</br>" + list.Name;
return returnValue;
}
}
请参阅提供的代码,当页面运行时,它将如下所示
ECMAScript 客户端对象模型
如果您已经使用过 .NET 托管客户端对象模型,那么使用 ECMAScript 和客户端对象模型相对简单且相似。但有一些区别,如下所示:
• 您不能在 ClientContext 构造函数中使用服务器端 URL。
• 它不支持 LINQ 语法。
• 它本质上是异步的。
• 必须包含 FormDigest 控件才能在对 SharePoint 数据进行某些修改时为安全验证创建摘要。它根据用户、站点和时间在您的页面中添加一个安全令牌。一旦页面回发,安全令牌就会被验证。一旦生成安全令牌,它在可配置的时间内有效。要了解更多信息,请访问此链接
http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.webcontrols.formdigest.aspx
让我们使用 ECMAScript 进行一些实践。向项目中添加一个应用程序页,名称任意,但在可下载代码中我将其命名为“ListCreationPage.aspx”。在此演示中,我们将使用 ECMAScript 客户端对象模型创建 SharePoint 2010 列表。为此,请在 aspx 页面中添加以下代码片段:
<asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server"> <script type="text/javascript"> var messageId; function BtnCreateListCancel_Click() { SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.cancel, 'Cancelled clicked'); } function BtnCreateListOk_Click() { var form = document.forms.<%SPHttpUtility.NoEncode(Form.ClientID,Response.Output);%>; var ListNameUrl = form.<%SPHttpUtility.NoEncode(TxtListName.ClientID,Response.Output);%>.value; //Create list using client OM createList(ListNameUrl); SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.OK, ListNameUrl); } //Actual JS client side object model calls function createList(listName) { //Create client context. var clientContext = new SP.ClientContext(); var oWebsite = clientContext.get_web(); //Let's create list creation information object var listCreationInfo = new SP.ListCreationInformation(); listCreationInfo.set_title(listName); listCreationInfo.set_templateType(SP.ListTemplateType.announcements); listCreationInfo.set_quickLaunchOption(SP.QuickLaunchOptions.on); this.oList = oWebsite.get_lists().add(listCreationInfo); //Let's create also new item to the list to be created var itemCreateInfo = new SP.ListItemCreationInformation(); this.oListItem = oList.addItem(itemCreateInfo); oListItem.set_item('Title', 'Example item for ' + listName); oListItem.set_item('Body', 'Hello seminar audience. From list ' + listName); oListItem.update(); clientContext.load(oListItem); clientContext.load(oList); //Execute the actual script clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed)); } //Called if client side OM is successful function onQuerySucceeded() { //Remove the 'creating' event notification if(messageId != null) { SP.UI.Notify.removeNotification(messageId); } //Add 'created' notification as non sticky messageId = SP.UI.Notify.addNotification("List <b>" + oList.get_title() + "</b> created...", false, "", null); } function onQueryFailed(sender, args) { //Remove the 'creating' event notification if(messageId != null) { SP.UI.Notify.removeNotification(messageId); } //Shown in case of error on the JS OM call messageId = SP.UI.Notify.addNotification("Operation was cancelled...", false, "", null); } </script> </asp:Content> <asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server"> <table id="maintable" border="0" cellspacing="0" cellpadding="0" class="ms-propertysheet" width="100%"> <tr> <td> <wssuc:InputFormSection Title="List name" runat="server"> <Template_Description> <SharePoint:EncodedLiteral ID="EncodedLiteral1" runat="server" text="Define list name to be created." EncodeMethod='HtmlEncode'/> </Template_Description> <Template_InputFormControls> <wssuc:InputFormControl runat="server"> <Template_Control> <table border="0" cellspacing="1"> <tr> <td class="ms-authoringcontrols" colspan="2" nowrap="nowrap"> <SharePoint:EncodedLiteral ID="EncodedLiteral7" runat="server" text="List name will be used as url and as the title" EncodeMethod='HtmlEncode'/>: <font size="3"> </font><br /> </td> </tr> <tr> <td dir="ltr"> <table> <tr> <td> <tdnowrap="nowrap"colspan="2"class="ms-authoringcontrols"> <wssawc:InputFormTextBox title="Enter list name" class="ms-input" ID="TxtListName" Columns="35" Runat="server" maxlength="255" size="60" width="100%" /> </td> </tr> </table> </td> </tr> </table> </Template_Control> </wssuc:InputFormControl> </Template_InputFormControls> </wssuc:InputFormSection> <wssuc:ButtonSection runat="server" ShowStandardCancelButton="False"> <Template_Buttons> <asp:placeholder ID="Placeholder1" runat="server"> <input class="ms-ButtonHeightWidth" type="button" name="BtnOk" id="Button1" value="OK" onclick="BtnCreateListOk_Click()" /> <SeparatorHtml> <span id="idSpace" class="ms-SpaceBetButtons" /> </SeparatorHtml> <input class="ms-ButtonHeightWidth" type="button" name="BtnCancel" id="Button2" value="Cancel" onclick="BtnCreateListCancel_Click()" /> </asp:PlaceHolder> </Template_Buttons> </wssuc:ButtonSection> </td> </tr> </table> </asp:Content>
包括对 SP.js, SP.Debug.js 的引用。请注意,如果将在生产环境中使用,请不要包含 SP.Debug.js。我们有一个 SharePoint InputFormTextBox 控件和按钮控件。要创建列表,用户必须在 InputFormTextBox 控件中指定列表名称并单击“确定”按钮。因此,我们需要附加一个名为 BtnCreateListOk_Click() 的 JavaScript 函数。此函数的定义如下所示:
function BtnCreateListOk_Click()
{
var ListNameUrl = form.<%SPHttpUtility.NoEncode(TxtListName.ClientID,Response.Output);%>.value;
//Create list using client OM
createList(ListNameUrl);
SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.OK, ListNameUrl);
}
这里要做的第一件事是获取需要创建的列表的名称。使用 SPHttpUtility 类的 NoEncode 方法获取 InputFormTextBox 控件的值。然后我们将列表名称传递给 createList 函数。所以,让我们进入 createList 函数
function createList(listName) {
//Create client context.
var clientContext = new SP.ClientContext();
var oWebsite = clientContext.get_web();
//Let's create list creation information object
var listCreationInfo = new SP.ListCreationInformation();
listCreationInfo.set_title(listName);
listCreationInfo.set_templateType(SP.ListTemplateType.announcements);
listCreationInfo.set_quickLaunchOption(SP.QuickLaunchOptions.on);
this.oList = oWebsite.get_lists().add(listCreationInfo);
//Let's create also new item to the list to be created
var itemCreateInfo = new SP.ListItemCreationInformation();
this.oListItem = oList.addItem(itemCreateInfo);
oListItem.set_item('Title', 'Sample item for ' + listName);
oListItem.set_item('Body', 'Hi friends. From list ' + listName);
oListItem.update();
clientContext.load(oListItem);
clientContext.load(oList);
//Execute the actual script
clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));
}
//Called if client side OM is successful
function onQuerySucceeded() {
//Remove the 'creating' event notification
if(messageId != null)
{
SP.UI.Notify.removeNotification(messageId);
}
messageId = SP.UI.Notify.addNotification("List <b>" + oList.get_title() + "</b> created...", false, "", null);
}
function onQueryFailed(sender, args) {
//Remove the 'creating' event notification
if(messageId != null)
{
SP.UI.Notify.removeNotification(messageId);
}
//Shown in case of error on the JS OM call
messageId = SP.UI.Notify.addNotification("Operation was cancelled...", false, "", null);
}
首先,我们需要获取 ClientContext 对象,然后我们可以使用 SP.ClientContext 对象的 get_web()(就像 SPContext.Current.Web)方法获取关联的网站。在此演示中,我们将创建一个列表,因此我们必须创建 SP.ListCreationInformation 类的对象。我们需要设置标题、列表模板类型,并且还必须指定列表是否会出现在快速启动菜单上。最后,我们可以通过调用 get_lists().add 方法添加列表,该方法将 ListCreationInformation 对象作为参数。 创建列表后,我们来创建一个列表项。创建 ListItemCreation 类的对象并相应地设置标题和正文。调用 update 方法更新列表项。无论如何,您必须在调用 executeQueryAsync 方法之前调用 load 方法加载列表和列表项的属性。executeQueryAsync 方法异步执行,并接受两个函数:onQuerySucceeded 和 onQueryFailed。如果您查看这些函数,您会注意到这两个函数主要用于处理成功和失败,并使用 SharePoint 2010 增强的 UI 通知呈现用户定义的错误消息。
讨论 SharePoint 2010 增强的 UI 通知和对话框不在我们的讨论范围之内,因此我不会详细讨论。因此,单击我们之前添加的可视 Web 部件中 ECMAScript 部分下的“创建列表”链接。SharePoint 2010 UI 对话框将打开,并提示您键入列表名称。输入后,单击“确定”创建列表
SharePoint 2010 状态栏已更新,显示正在进行的过程:
最后,您可以转到新创建的列表并找到我们创建的默认列表项。
Silverlight 客户端对象模型
Silverlight 客户端对象模型可以在两种情况下使用。它可以在 Silverlight Web 部件中使用,也可以在 Silverlight 跨域数据访问系统中使用。与 .NET 托管客户端对象模型相比,大部分内容都非常相似,但 Silverlight 不支持同步操作。因此,您必须创建委托以及成功和失败方法。此外,代码在后台线程上运行,因此用户界面线程需要先启动才能向其写入任何内容。因此,代码必须用 Dispatcher 对象的 BeginInvoke 方法包装。 所以,让我们创建一个 Silverlight 应用程序,它将检索 SharePoint 2010 站点下所有可用的列表。以管理员模式打开 Visual Studio 2010。创建一个新的 Silverlight 应用程序。将其命名为“SilverlightOM”。
系统将提示您选择 Silverlight 版本。请选择 Silverlight 4。另外,请确保您已选中“在新的 Web 站点中托管 Silverlight 应用程序”选项,以便创建一个新的 Web 项目,该项目将托管 Silverlight 应用程序。单击“确定”继续。
项目创建后,您将看到两个项目,如下所示:
现在在 SilverlightOM 项目中,添加对 Microsoft.SharePoint.Client.Silverlight.dll 和 Microsoft.SharePoint.Client.Silverlight.Runtime.dll 的引用。
那么这些程序集在哪里呢?实际上,它们位于以下位置:C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\ClientBin
打开 ManPage.xaml 并放置以下代码
<UserControl x:Class="SilverlightOM.MainPage" xmlns="<a href="http://schemas.microsoft.com/winfx/2006/xaml/presentation">http://schemas.microsoft.com/winfx/2006/xaml/presentation</a>" xmlns:x="<a href="http://schemas.microsoft.com/winfx/2006/xaml">http://schemas.microsoft.com/winfx/2006/xaml</a>" xmlns:d="<a href="http://schemas.microsoft.com/expression/blend/2008">http://schemas.microsoft.com/expression/blend/2008</a>" xmlns:mc="<a href="http://schemas.openxmlformats.org/markup-compatibility/2006">http://schemas.openxmlformats.org/markup-compatibility/2006</a>" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="500" xmlns:toolkit="<a href="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit">http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit</a>" xmlns:sdk="<a href="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk</a>"> <Grid x:Name="LayoutRoot" Background="White" Height="300" Width="448"> <Button Content="Get All Lists" Height="23" HorizontalAlignment="Left" Margin="12,54,0,0" Name="btnSilverlight" VerticalAlignment="Top" Width="97" Click="btnSilverlight_Click" /> <ListBox Height="175" HorizontalAlignment="Left" Margin="12,91,0,0" Name="lbLists" VerticalAlignment="Top" Width="395" /> </Grid> </UserControl>
在此演示中,我们使用了一个按钮和一个列表框控件。单击按钮后,列表框将填充站点中所有可用的列表。现在,我们已经定义了上面代码中显示的控件,现在是时候处理按钮单击事件了,我们将在其中使用 Silverlight 客户端对象模型编写代码以从 SharePoint 2010 站点检索数据。此外,添加以下类级别变量: 活动几乎相同,只是处理请求的方式是异步的。我们定义了一个 ClientContext 对象。然后,我们正在查询 Web 以获取所有列表。现在,我们需要创建两个委托对象;一个用于处理请求成功事件 (ClientRequestSucceededEventHandler),另一个用于请求失败事件 (ClientRequestFailedEventHandler)。相应的成功和失败事件处理函数必须作为委托对象的参数传递。现在这些委托对象需要传递给 ExecuteQueryAsync 方法。接下来要做的是定义我们为成功和失败活动定义的两个函数。
IEnumerable<SP.List> listItems = null;
private void btnSilverlight_Click(object sender, RoutedEventArgs e)
{
SP.ClientContext ctx = new SP.ClientContext("<a href="http://spdev/">http://SPDEV</a>");
var query = from listCollection in ctx.Web.Lists
select listCollection;
listItems = ctx.LoadQuery(query);
SP.ClientRequestSucceededEventHandler success = new SP.ClientRequestSucceededEventHandler(getItemsSucceeded);
SP.ClientRequestFailedEventHandler failure = new SP.ClientRequestFailedEventHandler(getItemsFailed);
ctx.ExecuteQueryAsync(success, failure);
}
private void getRequestSucceeded(object sender, SP.ClientRequestSucceededEventArgs e)
{
Dispatcher.BeginInvoke(() =>
{
lbLists.ItemsSource = listItems;
lbLists.DisplayMemberPath = "Title";
});
}
private void getRequestFailed(object sender, SP.ClientRequestFailedEventArgs e)
{
Dispatcher.BeginInvoke(() =>
{
MessageBox.Show("Request failed with the following details: " + e.ErrorCode + " " + e.ErrorDetails + " " + e.Message + " " + e.StackTrace);
});
}
因此,对于这两个方法,通过 Silverlight 对象模型更改用户界面 (UI) 的代码,您必须通过调用 BeginInvoke 方法将此工作委托给创建 UI 的线程的 Dispatcher 对象。在上面显示的代码中,您可以看到在成功函数中,我们将列表框与列表项绑定,如果失败,我们显示一个包含错误详细信息的消息框。请注意,如果您直接尝试更新(不使用 BeginInvoke 方法)用户界面 (UI),则可能会收到以下错误: “无效的跨线程访问” 运行应用程序,如果一切正常,那么是时候在 SharePoint 2010 环境中运行应用程序了。为此,我们需要在 Silverlight 2010 Web 部件中使用此应用程序。
ClientAccessPolicy.xml
如果您需要使用跨域调用(Silverlight 应用程序在您的域中运行,但调用其他域上的服务),那么您必须使用 clientaccesspolicy.xml 文件。因为,跨域通信需要针对多种安全威胁进行保护。更详细地说,假设您的 Silverlight 应用程序正在 http://abcintranet.com/som 上运行,并且此应用程序正在尝试调用 http://xyzintranet.com/pom/service.svc 上的服务。此请求将被阻止,因为可能存在恶意 Silverlight 控件试图通过调用服务执行未经授权的操作。 Silverlight 4 支持两种跨域访问策略文件: 在服务托管域的根位置放置 ClientAccessPolicy.xml 文件,以配置服务允许跨域访问。 在服务托管域的根位置放置 CrossDomain.xml 文件。该文件必须将整个域标记为公共。 在我们的例子中,我们必须在域的根位置添加一个 ClientAccessPolicy.xml 文件。使用以下 XML 创建文件:
<?xml version="1.0" encoding="utf-8" ?> <access-policy> <cross-domain-access> <policy> <allow-from http-request-headers="*"> <domain uri="*" /> </allow-from> <grant-to> <resource path="/" include-subpaths="true" /> </grant-to> </policy> </cross-domain-access> </access-policy>
构建您的 Silverlight 项目并取出 .xap 文件。您可以在 SilverlightOM.Web/ClientBin 文件夹中找到它。然后将 .xap 文件上传到 SharePoint 2010 站点中的文档库。然后编辑 SharePoint 页面并添加一个 Silverlight Web 部件。指定您存储在文档库中的 .xap 文件的位置。单击“确定”并指定此文件的其他可选属性。
因此,Silverlight Web 部件被添加到页面中,单击“获取所有列表”按钮后,站点中所有可用的列表将显示在列表框中。
各种 SharePoint 对象模型之间的对象等效性
|
.NET 托管客户端对象模型 |
ECMAScript 对象模型 |
Microsoft.SharePoint.SPContext |
Microsoft.SharePoint.Client.ClientContext |
SP.ClientContext |
Microsoft.SharePoint.SPSite |
Microsoft.SharePoint.Client.Site |
SP.Site |
Microsoft.SharePoint.SPWeb |
Microsoft.SharePoint.Client.Web |
SP.Web |
Microsoft.SharePoint.SPList |
Microsoft.SharePoint.Client.List |
SP.List |
Microsoft.SharePoint.SPListItem |
Microsoft.SharePoint.Client.ListItem |
SP.ListItem |
Microsoft.SharePoint.SPField |
Microsoft.SharePoint.Client.Field |
SP.Field |
- |
Microsoft.SharePoint.Client.ApplicationContext |
- |
- |
- |
SP.Application.UI |
- |
- |
SP.Ribbon |
- |
- |
SP.Ribbon.PageState |
- |
- |
SP.Ribbon.TenantAdmin |
- |
- |
SP.UI |
- |
- |
SP.UI.ApplicationPages |
- |
- |
SP.UI.ApplicationPages.Calendar |
- |
Microsoft.SharePoint.Client.Utilities |
SP.Utilities |
- |
Microsoft.SharePoint.Client.WebParts |
SP.WebParts |
- |
Microsoft.SharePoint.Client.Workflow |
SP.Workflow |
结束语
我写这篇文章的目的是提供对 SharePoint 客户端对象模型工作原理的非常基本的理解。此外,我还尝试尽可能多地结合简单的方法和论证来实施相同的功能。SharePoint 客户端对象模型确实非常强大和方便,每个人都应该了解它。完整的源代码以两个项目的形式提供,可以下载。
学习是一个永无止境的过程,我最近几周学到的东西,我认为我应该分享出来。初学者会有一个开始,专家们非常欢迎分享他们的想法或意见,如果有什么遗漏或需要更改的地方。 您可能会同意我的观点,写作对开发人员来说是一项艰巨的工作,对我来说也确实如此。除了我的日常工作之外,我已经连续两周在办公室熬夜完成这篇文章了。
这篇文章是为我的所有团队成员和你们所有人写的。如果这篇文章对您有用,我将很高兴知道。我将非常乐意收到您的问题、反馈、建议和忠告。请写信给我:SanjaySantra@hotmail.com。祝大家阅读愉快!
参考文献
http://msdn.microsoft.com/zh-cn/library/ee857094(office.14).aspx
http://msdn.microsoft.com/zh-cn/library/cc197955(v=vs.95).aspx
http://blogs.technet.com/b/vedant/archive/2010/06/14/sharepoint-2010-resource-guide.aspx