SharePoint 2013开发入门探索(二)- 列表操作
?? 我們如何用代碼對SharePoint列表做些例如增刪改查的操作呢?如果您的程序可以部署到服務器上,就可以使用?服務器對象模型,因為服務器對象模型提供的功能最多,限制最少;否則可能要選擇客戶對象模型等其他方式,這可能會遇到一些功能限制;另外還有一些其他的訪問方式,例如Web服務等。如何在 SharePoint 2013 中選擇正確的 API 集請參考鏈接?http://msdn.microsoft.com/zh-cn/library/jj164060.aspx。
?????我們首先研究下服務器對象模型。使用服務器對象模型需要到服務器端部署,所以沒有服務器部署權限的話就不能使用了。使用服務對象模型要引用程序集Microsoft.SharePoint —— 如果您創建的是SharePoint項目,這個引用默認就會有的。
?????? 在服務器對象模型中我經常使用的幾個.NET類是SPSite、SPWeb、SPList還有SPListItem,下面簡單介紹下這幾個類。
???????網站集(由一個首要網站以及該網站下的所有網站組成,參考:http://technet.microsoft.com/zh-cn/library/cc262410.aspx)對應的類是SPSite,可以傳參網站集URL給SPSite的構造方法創建對象。一般您可以這樣用。
using (SPSite spSite = new SPSite("http://SharePointServer/")) {//TODO...... }????? 有時候你可能要提升一下權限以場管理員身份執行,則可以用下面的寫法——這是服務器對象模型的優勢,但是如果項目選擇為“部署為沙盒解決方案”則不能提升到場管理員權限。
?
SPSecurity.RunWithElevatedPrivileges(() =>{using (SPSite spSite = new SPSite("http://SharePointServer/")){//TODO......} });?
????? SharePoint里的用戶對應的是SPUser類,也可以使用SPUserToken構造網站集對象實現模擬用戶身份執行代碼。
?
SPSecurity.RunWithElevatedPrivileges(() =>{using (SPSite spSite = new SPSite("http://SharePointServer/")){SPUser imitatedUser = spSite.RootWeb.EnsureUser(@"contoso\sanzhang");SPUserToken token = imitatedUser.UserToken;using (SPSite siteWithUser = new SPSite("SharePointServerUrl", token)){//TODO......}}});?
??????網站對應的類是SPWeb,SPSite有一個RootWeb屬性,是網站集的根網站;SPSite還有個AllWebs屬性,是它的所有網站集合(可以用索引訪問),要得到SPWeb對象還可以調用SPSite對象的OpenWeb方法,傳給一個URL參數(根網站的URL和網站集是相同的,所以下面的代碼傳空字符串,其他子網站要傳標識網站的URL部分)。每個SPWeb對象也有個Webs屬性,是這個網站的子網站(也可以用索引訪問)。
?
using (SPSite spSite = new SPSite("http://SharePointServer/")) {//獲取網站集根網站對象SPWeb spRootWeb = spSite.RootWeb;Console.WriteLine("網站集的根網站是{0}", spRootWeb.Title);//用網站ID獲取子網站對象Guid webId = new Guid("{4a106421-ae78-40fd-ad62-77fecb67cf27}");SPWeb spWeb = spSite.AllWebs[webId];Console.WriteLine("該網站是{0}", spWeb.Title);spWeb = spRootWeb.Webs[webId];Console.WriteLine("該網站是{0}", spWeb.Title);//用網站名稱獲取子網站對象string name = "mysubweb1";spWeb = spSite.AllWebs[name];Console.WriteLine("該網站是{0}", spWeb.Title);spWeb = spRootWeb.Webs[name];Console.WriteLine("該網站是{0}", spWeb.Title);//用網站URL獲取子網站對象string url = @"/mysubweb1";spWeb = spSite.OpenWeb(url);Console.WriteLine("該網站是{0}", spWeb.Title); }?
?????獲取對網站和其他關鍵對象的引用請參考:http://msdn.microsoft.com/zh-cn/library/ms468609(v=office.14).aspx。
???? 列表對應的類是SPList,其實SPList類對應的不僅僅是狹義的自定義列表,文檔庫、圖片庫、日歷、通知、任務都可以用它來操作,還有網站資產、用戶信息列表、解決方案庫、列表模版庫、Web部件庫等等。SPWeb對象有個Lists屬性,是SPListCollection類型,遍歷它或者用ID、Title做索引可得到SPList對象。想了解網站中有哪些List可以用下面的代碼查看:
?
//獲取網站所有列表foreach (SPList list in spWeb.Lists){Console.WriteLine("Title:{0} BaseType:{1} BaseTemplate:{2}", list.Title, list.BaseType, list.BaseTemplate);}//用列表標題獲取列表對象string title = @"我的客戶列表";SPList spList = spWeb.Lists[title];Console.WriteLine("該列表是{0}。", spList.Title);//用列表ID獲取列表對象Guid id = new Guid(@"{3824b091-c7b8-409c-bcc0-7cce487d6b49}");spList = spWeb.Lists[id];Console.WriteLine("該列表是{0}。", spList.Title);//用列表URL獲取列表對象string listUrl = string.Format(@"{0}/{1}", spWeb.Url, "Lists/MyCustomerList/AllItems.aspx");spList = spWeb.GetList(listUrl);Console.WriteLine("該列表是{0}。", spList.Title);?
??????您還是否記得這些列表屬性,我們在開發自定義列表時都設置過?
????? 列表項對應的類是SPListItem,SPList對象的Items屬性是它的項目的集合,是SPListItemCollection類型的,枚舉它的元素就是SPListItem對象了。不過您想訪問列表項的時候一般不要直接通過SPList對象的Items屬性,特別是已經存儲了很多項目的大數據列表,因為可能會有不必要的數據加載影響性能。例如您想知道列表中已存在多少個項目,可以直接訪問SPList對象的ItemCount屬性,而不是SPList對象的Items屬性的Count;如果您想得到列表中的某個項目可以調用SPList對象的GetItemById、GetItemByUniqueId等方法。
?
//用列表標題獲取列表對象SPList spList = spWeb.Lists["我的客戶列表"];//獲取用戶信息列表的項目數量int userCount = spList.ItemCount;Console.WriteLine("我的客戶列表共有{0}條項目。", userCount.ToString());//用項目ID獲取項目對象int itemId = 18;SPListItem spListItem = spList.GetItemById(itemId);Console.WriteLine("該客戶是{0}。", spListItem.Title);//用項目UniqueID獲取項目對象Guid itemUniqueId = new Guid("{83815a27-6291-416d-8db6-a77bcae4bb86}");spListItem = spList.GetItemByUniqueId(itemUniqueId);Console.WriteLine("該客戶是{0}。", spListItem.Title);?
????? 列表對象還有一個Fields的屬性,是SPFieldCollection類型,遍歷這個字段集合可以得到SPField對象。這就是列表的字段信息,包括字段內部名稱、顯示名稱、類型等等。我們要訪問列表項目的某個屬性時就可以用SPField對象的ID,InternalName或Title。
?
string title = @"我的客戶列表";SPList spList = spWeb.Lists[title];SPFieldCollection spFields = spList.Fields;//獲取列表的字段信息foreach (SPField field in spFields){Console.WriteLine("Title:{0} InternalName:{1} TypeDisplayName:{2} TypeAsString:{3}", field.Title, field.InternalName, field.TypeDisplayName, field.TypeAsString);}Guid fieldId = new Guid("{a943ca8c-a2ad-4a90-8a78-2f6a202f6553}");SPField spField = spFields[fieldId];SPListItem spListItem = spList.Items[0];//用字段的ID訪問列表項目屬性Console.WriteLine("該用戶的消費金額是{0}。", spListItem[spField.Id].ToString());//用字段的InternalName訪問列表項目屬性Console.WriteLine("該用戶的消費金額是{0}。", spListItem[spField.InternalName].ToString());Console.WriteLine("該用戶的消費金額是{0}。", spListItem.GetFormattedValue(spField.InternalName));//用字段的Title訪問列表項目屬性Console.WriteLine("該用戶的消費金額是{0}。", spListItem[spField.Title].ToString());Console.WriteLine("該用戶的消費金額是{0}。", spListItem.GetFormattedValue(spField.Title));?
????? 您還記得開發自定義列表欄的時候我們一起設置的那些字段屬性嗎?
????? SPList對象還有個GetItemByIdAllFields方法和GetItemByIdSelectedFields方法。看來我們可以按需索取項目字段了。
?
//用列表標題獲取列表對象string title = @"我的客戶列表";SPList spList = spWeb.Lists[title];int itemId = 1;SPListItem spListItem = spList.GetItemByIdSelectedFields(itemId, new[] { "CustomerName", "Recency", "Frequency", "Monetary" });//用項目的ID和字段的InternalName訪問列表項目Console.WriteLine("客戶{0}的最近消費時間是{1}消費頻率是{2}消費金額是{3}。", spListItem["CustomerName"].ToString(), spListItem["Recency"].ToString(), spListItem["Frequency"].ToString(), spListItem["Monetary"].ToString());?
????? 不過,我把GetItemByIdSelectedFields方法的數組參數減掉了兩個項"Frequency"和"Monetary"再運行,竟然沒有看到我期待的、閃電般美麗的異常!不是說只要兩個字段嗎?而且隨后我把SPListItem對象的Xml屬性也輸出時有看到了好多不是我指定索取的字段信息!
?
string title = @"我的客戶列表";SPList spList = spWeb.Lists[title];int itemId = 1;//刪掉"Frequency"、 "Monetary" SPListItem spListItem = spList.GetItemByIdSelectedFields(itemId, new[] { "CustomerName", "Recency"});//用項目的ID和字段的InternalName訪問列表項目Console.WriteLine("客戶{0}的最近消費時間是{1}消費頻率是{2}消費金額是{3}。", spListItem["CustomerName"].ToString(), spListItem["Recency"].ToString(), spListItem["Frequency"].ToString(), spListItem["Monetary"].ToString());Console.WriteLine(@"XML:{0}",spListItem.Xml);?
?????? 查看一下GetItemByIdSelectedFields方法的源代碼,發現在拼接CAML查詢字段時有個對SPField對象MustFetchByDefault的判斷,如果這個屬性是True,沒有指定也會查詢,而且這個SPField對象MustFetchByDefault屬性是internal的。
?
public SPListItem GetItemByIdSelectedFields(int id, params string[] fields) {if (fields == null){throw new ArgumentNullException("fields");}StringBuilder builder = new StringBuilder();foreach (string str in fields){if (str != null){builder.Append("<FieldRef Name=\"" + str + "\"/>");}}foreach (SPField field in this.Fields){bool flag = false;foreach (string str2 in fields){if (str2 == field.InternalName){flag = true;break;}}if (!flag && field.MustFetchByDefault){builder.Append("<FieldRef Name=\"");builder.Append(field.InternalName);builder.Append("\"/>");}}return this.GetItemById(id, null, false, builder.ToString()); }?
?????? 如何讀取列表項中字段的值請參考:http://msdn.microsoft.com/zh-cn/library/ff521580(v=office.14).aspx。
?????? 在讀取項目屬性時,有些類型是比較特殊的:例如多個用戶或用戶組,它可以轉為SPFieldUserValueCollection類型,這是個SPFieldUserValue對象的集合;?用戶或用戶組?、超鏈接或圖片這樣的字段取出來時String類型,您要解析可以用字符串截取方式,也可以對應構造成SPFieldUserValue、SPFieldUrlValue對象,直接訪問它們的屬性。
?
SPListItem spListItem = spList.GetItemById(itemId);//直接ToString,用戶是[ID]+[;#] + [顯示名];鏈接是[URL] + [,] + [顯示文本]Console.WriteLine("客戶的分享者是{0} 所有者是{1} 業務系統鏈接是{2}。", spListItem["Sharers"], spListItem["Owner"].ToString(), spListItem["BusinessPage"].ToString());//截取字符串取屬性Console.WriteLine("客戶的所有者的顯示名是{0} 業務系統鏈接的URL是{1}。", spListItem["Owner"].ToString().Split(new[] { @";#" }, StringSplitOptions.None)[1], spListItem["BusinessPage"].ToString().Split(',')[0]);//轉為相應對象取屬性SPFieldUserValueCollection shares = (SPFieldUserValueCollection)spListItem["Sharers"];foreach (SPFieldUserValue share in shares){if (share.User != null){SPUser user = share.User;Console.WriteLine(@"找到個用戶{0}", user.Name);}else{SPGroup spGroup = spWeb.Groups.GetByID(share.LookupId);Console.WriteLine(@"找到個用戶組{0}", spGroup.Name); }}SPFieldUserValue owner = new SPFieldUserValue(spWeb, spListItem["Owner"].ToString());SPFieldUrlValue businessPage = new SPFieldUrlValue(spListItem["BusinessPage"].ToString());Console.WriteLine("客戶的業務系統鏈接的URL是{0} 業務系統鏈接的描述是{1}。", businessPage.Url, businessPage.Description);?
???? ? 如果您想按某些條件得到列表中的幾個項目,則可以使用CAML列表查詢了。上文中我們查看SPList的源代碼,看到了一段XML的拼接,這就是CAML。協作應用程序標記語言 (CAML) 簡介請參考:http://msdn.microsoft.com/zh-cn/library/ms426449.aspx。
???????如果要直接使用CAML做查詢可以使用SPList對象的GetItems方法,這個方法的參數是一個SPQuery對象。SPQuery對象的ViewFields是返回字段(個人理解ViewFieldsOnly要設置為True時ViewFields才有效,但是我嘗試中發現不設置“ViewFieldsOnly=true”返回的結果是一樣的。另外,ID、Created、Modified等屬性一直會被返回的),Query是查詢條件,這就相當于SQL語句里的"SELECT ..."和“WHERE...”了。GetItems方法的返回值是一個SPListItemCollection對象,如果喜歡用DataTable的話,可以調用它的GetDataTable()對象。舉個簡單的查詢例子,查詢名稱為張三的客戶:
?
SPList spList = spWeb.Lists[title];SPQuery spQuery = new SPQuery();spQuery.ViewFields = @"<FieldRef Name='CustomerName'/><FieldRef Name='Gender'/><FieldRef Name='Monetary'/><FieldRef Name='BusinessPage'/><FieldRef Name='Owner'/><FieldRef Name='Sharers'/>";//spQuery.ViewFieldsOnly = true;spQuery.Query = @"<Where><Eq><FieldRef Name='CustomerName'/><Value Type='Text'>張三</Value></Eq></Where>";SPListItemCollection spListItemCollection = spList.GetItems(spQuery);//將結果保存到DataTableDataTable dataTable = spListItemCollection.GetDataTable();Console.WriteLine(spQuery.ViewFieldsOnly.ToString());if (spListItemCollection.Count > 0){foreach (SPListItem spListItem in spListItemCollection){Console.WriteLine(spListItem["CustomerName"]);Console.WriteLine(spListItem.Xml);}}????? CAML查詢條件有很多比較符:Eq(=)、Gt(>)、Lt(<)、Geq(>=)、Leq(<=)、Neq(<>)、Contains(Like)、IsNull(Null)、IsNotNull(NotNull)等,多個查詢條件可以使用關系運算符And或Or(http://msdn.microsoft.com/zh-cn/library/ms467521(v=office.14).aspx)。再舉一個例子:自定義列表-我的客戶列表有一個Sharers字段(表示分享用戶列表),這個字段可存儲多個用戶和組,現在有一個用戶甲,要查詢分享給這個用戶或這個用戶所在的組的數據則使用如下代碼:
string title = @"我的客戶列表";SPList spList = spWeb.Lists[title];SPUser user = spWeb.EnsureUser(loginName);SPGroup[] groups = user.Groups.Cast<SPGroup>().ToArray();SPQuery spQuery = new SPQuery();spQuery.ViewFields = @"<FieldRef Name='CustomerName'/><FieldRef Name='Gender'/><FieldRef Name='Monetary'/><FieldRef Name='BusinessPage'/><FieldRef Name='Owner'/><FieldRef Name='Sharers'/>";spQuery.ViewFieldsOnly = true;if (groups.Length > 0){StringBuilder query = new StringBuilder();query.AppendFormat(@"<Where><Or><Includes><FieldRef Name='Sharers' LookupId=""TRUE"" /><Value Type='User'>{0}</Value></Includes>", user.ID.ToString());Array.ForEach(groups, g => query.AppendFormat(@"<Includes><FieldRef Name='Sharers' LookupId='TRUE' /><Value Type='Group'>{0}</Value></Includes>", g.ID.ToString()));query.Append(@"</Or></Where>");spQuery.Query = query.ToString();}else{spQuery.Query = string.Format(@"<Where><Includes><FieldRef Name='Sharers' LookupId='TRUE' /><Value Type='User'>{0}</Value></Includes></Where>", user.ID.ToString());}DataTable dataTable = spList.GetItems(spQuery).GetDataTable();????? SPQuery對象的ViewAttributes屬性可以設置檢索范圍;RowLimit是返回數量ListItemCollectionPosition是查詢位置,用這兩個屬性可以實現分頁查詢;列表中如果包含文件夾并且查詢要在指定文件夾下執行,可以使用Folder屬性。
?????? SPQuery類使用請參考http://technet.microsoft.com/zh-cn/library/microsoft.sharepoint.spquery.aspx?;如果您需要跨網站多列表混合查詢,可以使用SPSiteDataQuery 類,http://msdn.microsoft.com/zh-cn/library/microsoft.sharepoint.spsitedataquery.aspx;查詢列表項請參考http://msdn.microsoft.com/zh-cn/library/ms456030(v=office.14).aspx。如果您覺得拼寫CAML有些麻煩,也可以到網站找一些小工具下載使用,例如:http://sharepointcamlhelper.codeplex.com/。關于查詢性能,有幾篇個人覺得不錯的文章:http://msdn.microsoft.com/zh-cn/subscriptions/ee557257.aspx,http://www.infoq.com/cn/articles/SharePoint-Andreas-Grabner。
???? ?新增列表項目可以先調用SPList對象的AddItem方法,獲取一個新的SPListItem對象,在給SPListItem的所需字段賦值后再調用它的Update方法即可保存新增(SPListItem對象還有個SystemUpdate方法,這個方法在修改時不會影響修改時間和修改者,另外還可以用參數指定是否創建新版本)。
SPUser user1 = spWeb.EnsureUser(loginName1);SPUser user2 = spWeb.EnsureUser(loginName2);SPGroup group1 = spWeb.Groups[loginName3];string title = @"我的客戶列表";SPList spList = spWeb.Lists[title];SPListItem spListItem = spList.AddItem();spListItem["CustomerName"] = "趙六";spListItem["Gender"] = "女";spListItem["EMail"] = "liuzhao@contoso.com";spListItem["CellPhone"] = "13456435678";spListItem["WorkAddress"] = "西直門";spListItem["Recency"] = DateTime.Now;spListItem["Frequency"] = 0.5;spListItem["Monetary"] = 100000;spListItem["BusinessPage"] = new SPFieldUrlValue() { Url = "http://zhaoliu.com", Description = "趙六的個人主頁" };spListItem["CustomerType"] = "鉆石";spListItem["Owner"] = new SPFieldUserValue(spWeb, user1.ID, user1.Name);spListItem["Sharers"] = new SPFieldUserValueCollection { new SPFieldUserValue(spWeb, user1.ID, user1.Name),new SPFieldUserValue(spWeb, user2.ID, user2.Name), new SPFieldUserValue(spWeb, group1.ID, group1.Name) };spListItem.Update();????? 在給項目的字段賦值時,對于用戶或用戶組、超鏈接或圖片這樣的字段,也可以直接以字符串賦值。用戶或用戶組的ID和顯示名要以“;#”連接,超鏈接或圖片則以“,”連接URL和描述。
spListItem["BusinessPage"] = string.Format(@"{0},{1}", "http://sunqi.com", "孫七的個人主頁"); spListItem["Owner"] = new SPFieldUserValue(spWeb, string.Format(@"{0};#{1}", user2.ID.ToString(), user2.Name));spListItem["Sharers"] = string.Join(@";#", user1.ID.ToString(), user1.Name, user2.ID.ToString(), user2.Name, group1.ID.ToString(), group1.Name);?????? 如果項目有個附件文件如何上傳呢?這個還很簡單,只要使用SPListItem對象的Attachments屬性就可以了。這是一個SPAttachmentCollection類型的SPAttachment集合,它有一個Add方法。
spListItem.Attachments.Add(Path.GetFileName(@"C:\1.txt"), File.ReadAllBytes(@"C:\1.txt"));????? 上文我們提到,對文檔庫的操作也要使用SPList類,可是我看了一下SPListItem對象的File屬性——它是只讀的。 那么如何給文檔庫創建文件夾和上傳文件呢?這次創建文件夾或文件的項目要用到SPList對象的AddItem重載方法,相關的SPFolder和SPFile也可以通過列表的網站SPWeb對象獲取。
string title = @"我的文檔庫";SPList spList = spWeb.Lists[title];//創建一個文件夾的SPListItem對象SPListItem folderListItem = spList.AddItem(spList.RootFolder.ServerRelativeUrl, SPFileSystemObjectType.Folder, "文件夾2");folderListItem.Update();//找到網站對應文件夾SPFolder對象SPFolder spFolder = spList.ParentWeb.GetFolder(folderListItem.UniqueId);bool allowUnsafeUpdates = spWeb.AllowUnsafeUpdates;//如果AllowUnsafeUpdates是False可能下面操作會有異常spWeb.AllowUnsafeUpdates = true;//在指定文件夾下添加文件spFolder.Files.Add(Path.GetFileName(@"C:\1.txt"), File.ReadAllBytes(@"C:\1.txt"));spWeb.AllowUnsafeUpdates = allowUnsafeUpdates;????? 再看看修改和刪除列表項目就非常簡單了。(參考資料:http://msdn.microsoft.com/zh-cn/library/ms467435(v=office.14).aspx)
string title = @"我的客戶列表";SPList spList = spWeb.Lists[title];//修改:int itemId = 5;SPListItem spListItem = spList.GetItemById(itemId);spListItem["CustomerName"] = "孫七七";spListItem.Update();//刪除://spListItem.Delete();?????? 不過要高效的批量執行增刪改操作還是需要SPWeb對象的ProcessBatchData方法。命令語法請參考:http://msdn.microsoft.com/zh-CN/library/ms455433(v=office.12).aspx、http://msdn.microsoft.com/zh-cn/library/microsoft.sharepoint.spweb.processbatchdata.aspx、http://msdn.microsoft.com/zh-cn/library/cc404818.aspx。下面代碼是插入、修改和刪除的例子,對于多個用戶和組、超鏈接或圖片還是使用的字符串拼接(用戶和組用“;#”連接ID和顯示名稱,超鏈接或圖片用空格+逗號+空格連接URL和描述),還有時間類型字段,要轉換為ISO8601格式,可以調用SPUtility類的CreateISO8601DateTimeFromSystemDateTime方法創建(http://msdn.microsoft.com/zh-cn/library/ms197282(v=office.14).aspx)。
SPUser user1 = spWeb.EnsureUser(loginName1);SPUser user2 = spWeb.EnsureUser(loginName2);SPGroup group1 = spWeb.Groups[loginName3];string listId = @"3824b091-c7b8-409c-bcc0-7cce487d6b49";StringBuilder strBatchData = new StringBuilder(@"<?xml version=""1.0"" encoding=""UTF-8""?><Batch>");//命令頭:string setListText = string.Format(@"<SetList Scope=""Request"">{0}</SetList>", listId);//插入數據:strBatchData.AppendFormat(@"<Method ID=""Insert,1"">{0}<SetVar Name=""ID"">New</SetVar><SetVar Name=""Cmd"">Save</SetVar>", setListText);strBatchData.AppendFormat(@"<SetVar Name=""urn:schemas-microsoft-com:office:office#{0}"">{1}</SetVar>", "CustomerName", "蔣九");strBatchData.AppendFormat(@"<SetVar Name=""urn:schemas-microsoft-com:office:office#{0}"">{1}</SetVar>", "Gender", "男");strBatchData.AppendFormat(@"<SetVar Name=""urn:schemas-microsoft-com:office:office#{0}"">{1}</SetVar>", "EMail", "jiujiang@contoso.com");strBatchData.AppendFormat(@"<SetVar Name=""urn:schemas-microsoft-com:office:office#{0}"">{1}</SetVar>", "CellPhone", "13656435678");strBatchData.AppendFormat(@"<SetVar Name=""urn:schemas-microsoft-com:office:office#{0}"">{1}</SetVar>", "WorkAddress", "菜市口");strBatchData.AppendFormat(@"<SetVar Name=""urn:schemas-microsoft-com:office:office#{0}"">{1}</SetVar>", "Recency", SPUtility.CreateISO8601DateTimeFromSystemDateTime(DateTime.Now.AddDays(-1)));strBatchData.AppendFormat(@"<SetVar Name=""urn:schemas-microsoft-com:office:office#{0}"">{1}</SetVar>", "Frequency", "0.3");strBatchData.AppendFormat(@"<SetVar Name=""urn:schemas-microsoft-com:office:office#{0}"">{1}</SetVar>", "Monetary", "30000");strBatchData.AppendFormat(@"<SetVar Name=""urn:schemas-microsoft-com:office:office#{0}"">{1}</SetVar>", "BusinessPage", string.Format(@"{0} , {1}", "http://jiangjiu.com", "蔣九的個人主頁"));strBatchData.AppendFormat(@"<SetVar Name=""urn:schemas-microsoft-com:office:office#{0}"">{1}</SetVar>", "CustomerType", "青銅");strBatchData.AppendFormat(@"<SetVar Name=""urn:schemas-microsoft-com:office:office#{0}"">{1}</SetVar>", "Owner", string.Format(@"{0};#{1}", user2.ID.ToString(), user2.Name));strBatchData.AppendFormat(@"<SetVar Name=""urn:schemas-microsoft-com:office:office#{0}"">{1}</SetVar>", "Sharers", string.Join(@";#", user1.ID.ToString(), user1.Name, user2.ID.ToString(), user2.Name, group1.ID.ToString(), group1.Name));strBatchData.Append("</Method>");//修改數據:string updateItemId = "2";strBatchData.AppendFormat(@"<Method ID=""Update,1"">{0}<SetVar Name=""ID"">{1}</SetVar><SetVar Name=""Cmd"">Save</SetVar><SetVar Name=""urn:schemas-microsoft-com:office:office#{2}"">{3}</SetVar></Method>", setListText, updateItemId, "CustomerName", "李四四");//刪除數據:string deleteItemId = "3";strBatchData.AppendFormat(@"<Method ID=""Delete,1"">{0}<SetVar Name=""ID"">{1}</SetVar><SetVar Name=""Cmd"">Delete</SetVar></Method>", setListText, deleteItemId);//命令尾:strBatchData.Append(@"</Batch>");//執行:Console.WriteLine(spWeb.ProcessBatchData(strBatchData.ToString()));????? 服務端對象模型還有一個很重要的上下文對象--SPContext,在開發Web部件時經常會用到。我們可以通過SPContext.Current獲取到當前上下文SPContext對象,用該對象可以直接獲取當前網站、當前列表、當前項目......(http://msdn.microsoft.com/zh-cn/library/microsoft.sharepoint.spcontext.aspx)
SPContext spContext = SPContext.Current;//當前網站集SPSite spSite = spContext.Site;//當前網站SPWeb spWeb = spContext.Web;//當前用戶SPUser spUser = spWeb.CurrentUser;//當前列表SPList spList = spContext.List;//當前列表IDGuid listId = spContext.ListId;//當前項目SPItem spItem = spContext.Item;//當前項目IDint itemId = spContext.ItemId;//當前項目ID(字符串)string itemIdAsString = spContext.ItemIdAsString;//當前頁面上下文SPContextPageInfo contextPageInfo = spContext.ContextPageInfo;//......?
????? 上文花了較大篇幅研究服務端對象模型,接下來我們研究托管客戶端對象模型(托管客戶端對象模型是用.NET開發的,另有ECMAScript客戶端對象模型以后會介紹,http://msdn.microsoft.com/zh-cn/library/ee539429(v=office.14).aspx)。客戶端對象模型不需要部署到服務器端,當然功能也會有一定限制。使用客戶端對象模型代碼需要引用Microsoft.SharePoint.Client程序集和Microsoft.SharePoint.Client命名空間。
????? 首先,我們要構建一個客戶端上下文對象——ClientContext(相當于服務端對象模型的Microsoft.SharePoint.SPContext)。如果需要顯式以某個用戶的 Windows 憑據運行,可以給ClientContext對象的Credentials賦值。
ClientContext clientContext = new ClientContext(webFullUrl);//NetworkCredential networkCredential = CredentialCache.DefaultNetworkCredentials;//clientContext.Credentials = networkCredential;NetworkCredential networkCredential = new NetworkCredential(@"contoso\administrator", "password");clientContext.Credentials = networkCredential;???????使用過了服務器端對象模型再理解客戶端對象模型比較容易,與Microsoft.SharePoint.SPList對應的是Microsoft.SharePoint.Client.List、與Microsoft.SharePoint.SPListItem對應的是Microsoft.SharePoint.Client.ListItem,只是客戶端對象模型要寫更多代碼,通過查詢取得的對象不能直接使用,需要調用ClientContext對象的Load方法,并顯式調用ExecuteQuery方法。(http://msdn.microsoft.com/zh-cn/library/ee534956(v=office.14).aspx、http://msdn.microsoft.com/zh-cn/library/gg277498.aspx)
string webFullUrl = "http://SharePointServer/";using (ClientContext clientContext = new ClientContext(webFullUrl)){ List list = clientContext.Web.Lists.GetByTitle("我的客戶列表");CamlQuery camlQuery = new CamlQuery();//指定過濾條件camlQuery.ViewXml = @"<View><Query><Where><Eq><FieldRef Name='CustomerName'/><Value Type='Text'>蔣九</Value></Eq></Where></Query></View>";ListItemCollection listItems = list.GetItems(camlQuery);//加載查詢,并指定加載字段:clientContext.Load(listItems,items => items.Include(item => item.Id,item => item["CustomerName"],item => item["Gender"],item => item["EMail"],item => item["CellPhone"],item => item["WorkAddress"],item => item["Recency"],item => item["Frequency"],item => item["Monetary"],item => item["BusinessPage"],item => item["CustomerType"],item => item["Owner"],item => item["Sharers"]));//執行查詢clientContext.ExecuteQuery();int count = listItems.Count;foreach (ListItem item in listItems){Console.WriteLine("ID: {0} CustomerName: {1} Gender: {2} EMail: {3} CellPhone: {4} WorkAddress: {5} Recency: {6} Frequency: {7} Monetary: {8} BusinessPage: {9} CustomerType: {10} Owner: {11} Sharers: {12} ",item.Id, item["CustomerName"], item["Gender"], item["EMail"], item["CellPhone"],item["WorkAddress"], item["Recency"], item["Frequency"], item["Monetary"],((FieldUrlValue)item["BusinessPage"]).Url, item["CustomerType"], ((FieldUserValue)item["Owner"]).LookupValue, string.Join(",", ((FieldUserValue[])item["Sharers"]).Select(v => v.LookupValue).ToArray()));}}????? 新增項目時也多了一個步驟,要先創建一個ListItemCreationInformation對象(添加附件還要創建AttachmentCreationInformation對象),再調用List的AddItem方法;為ListItem對象的屬性賦值后,要調用Update和ExecuteQuery方法。
Guid listId = new Guid(@"{3824b091-c7b8-409c-bcc0-7cce487d6b49}");List list = clientContext.Web.Lists.GetById(listId);ListItemCreationInformation listItemCreationInformation = new ListItemCreationInformation();ListItem listItem = list.AddItem(listItemCreationInformation);listItem["CustomerName"] = "李四";listItem["Gender"] = "女";listItem["EMail"] = "sili@contoso.com";listItem["CellPhone"] = "13456435678";listItem["WorkAddress"] = "西直門";listItem["Recency"] = DateTime.Now;listItem["Frequency"] = 0.5;listItem["Monetary"] = 100000;listItem["BusinessPage"] = new FieldUrlValue() { Url = "http://lisi.com", Description = "李四的個人主頁" };listItem["CustomerType"] = "鉆石";listItem["Owner"] = FieldUserValue.FromUser(loginName1);listItem["Sharers"] = new [] { FieldUserValue.FromUser(user1.LoginName),FieldUserValue.FromUser(user2.LoginName), FieldUserValue.FromUser(group1.LoginName)};listItem.Update();AttachmentCreationInformation attachmentCreationInformation = new AttachmentCreationInformation();using (FileStream fileStream = new FileStream(fileFullName, FileMode.Open)){attachmentCreationInformation.ContentStream = fileStream;attachmentCreationInformation.FileName = Path.GetFileName(fileFullName);listItem.AttachmentFiles.Add(attachmentCreationInformation);clientContext.ExecuteQuery();}????? 如果要上傳一個文檔到文檔庫則需要創建FileCreationInformation對象。
string title = @"我的文檔庫";List list = clientContext.Web.Lists.GetByTitle(title);Folder folder = list.RootFolder.Folders.Add("文件夾1");folder.Update();FileCreationInformation fileCreationInformation = new FileCreationInformation();fileCreationInformation.Content = System.IO.File.ReadAllBytes(fileFullName);fileCreationInformation.Overwrite = true;fileCreationInformation.Url = Path.GetFileName(fileFullName);folder.Files.Add(fileCreationInformation);clientContext.ExecuteQuery();????? 將項目修改或刪除的代碼很簡單,也是操作后要調用ListItem對象的Update和ExecuteQuery方法。(http://msdn.microsoft.com/zh-cn/library/ee539976(v=office.14).aspx)
Guid listId = new Guid(@"{3824b091-c7b8-409c-bcc0-7cce487d6b49}");int itemId = 14;List list = clientContext.Web.Lists.GetById(listId);ListItem item = list.GetItemById(itemId);clientContext.Load(item);clientContext.ExecuteQuery();//修改item["CustomerName"] = "蔣九九";item.Update();clientContext.ExecuteQuery();//刪除item.DeleteObject();clientContext.ExecuteQuery();?
????? 關于Web Services(http://msdn.microsoft.com/zh-cn/library/dd878586(v=office.12).aspx)的方式操作列表,首先要添加Web引用(解決方案資源管理器-〉引用-〉添加服務引用-〉高級(左下角)-〉添加Web引用(左下角)-〉URL輸入框輸入?http://<site>/_vti_bin/Lists.asmx.)。
???? ?這里的操作命令語法和服務器端對象模型、客戶端對象模型里的CAML非常相似,只是Web服務方法的參數和返回值多是XmlElement對象,查詢列表項目shiyongLists.GetListItems 方法(請參考:http://msdn.microsoft.com/zh-cn/library/websvclists.lists.updatelistitems(v=office.12).aspx),下面是一個簡單的查詢例子。
string listName = "我的客戶列表";using (Lists listService = new Lists()){listService.Credentials = CredentialCache.DefaultCredentials;//或者://listService.Credentials = new NetworkCredential(@"contoso\administrator", "password");XmlDocument xmlDocument = new XmlDocument();XmlNode queryNode = xmlDocument.CreateNode(XmlNodeType.Element, "Query", string.Empty);XmlNode viewFieldsNode =xmlDocument.CreateNode(XmlNodeType.Element, "ViewFields", string.Empty);viewFieldsNode.InnerXml = @"<FieldRef Name='ID' /><FieldRef Name='CustomerName' /><FieldRef Name='Gender' /><FieldRef Name='EMail' /><FieldRef Name='CellPhone' />";queryNode.InnerXml = string.Format(@"<Where><Eq><FieldRef Name='CustomerName'/><Value Type='Text'>{0}</Value></Eq></Where>", "蔣九");XmlNode resultNode = listService.GetListItems(listName, null, queryNode, viewFieldsNode, null, null, null);Console.WriteLine(resultNode.InnerXml);Console.ReadLine();}???????? 新增、修改、刪除列表項目使用Lists.UpdateListItems 方法執行批量命令(請參考:http://msdn.microsoft.com/zh-cn/library/websvclists.lists.updatelistitems(v=office.12).aspx)。對于多個用戶和組、超鏈接或圖片等特殊的字段類型還是使用的和服務器端對象模型相同的字符串拼接。
string listName = "我的客戶列表";using (Lists listService = new Lists()){listService.Credentials = CredentialCache.DefaultCredentials;//或者://listService.Credentials = new NetworkCredential(@"contoso\administrator", "password");XmlDocument xmlDocument = new XmlDocument();XmlElement updatesBatch = xmlDocument.CreateElement("Batch");updatesBatch.SetAttribute("OnError", "Continue");StringBuilder methodsText = new StringBuilder();//刪除數據:methodsText.AppendFormat(@"<Method ID='Delete,1' Cmd='Delete'><Field Name='ID'>{0}</Field></Method>", deleteItemId.ToString());//插入數據:methodsText.Append(@"<Method ID='Insert,1' Cmd='New'><Field Name='ID'>New</Field>");methodsText.AppendFormat(@"<Field Name='{0}'>{1}</Field>", "CustomerName", "蔣九");methodsText.AppendFormat(@"<Field Name='{0}'>{1}</Field>", "Gender", "男");methodsText.AppendFormat(@"<Field Name='{0}'>{1}</Field>", "EMail", "jiujiang@contoso.com");methodsText.AppendFormat(@"<Field Name='{0}'>{1}</Field>", "CellPhone", "13656435678");methodsText.AppendFormat(@"<Field Name='{0}'>{1}</Field>", "WorkAddress", "菜市口");DateTime recency = DateTime.Now.AddDays(-1);methodsText.AppendFormat(@"<Field Name='{0}'>{1}</Field>", "Recency", string.Format("{0}-{1}-{2}T{3}:{4}:{5}Z", recency.Year.ToString("0000"), recency.Month.ToString("00"), recency.Day.ToString("00"), recency.Hour.ToString("00"), recency.Minute.ToString("00"), recency.Second.ToString("00")));methodsText.AppendFormat(@"<Field Name='{0}'>{1}</Field>", "Frequency", "0.4");methodsText.AppendFormat(@"<Field Name='{0}'>{1}</Field>", "Monetary", "10000");methodsText.AppendFormat(@"<Field Name='{0}'>{1}</Field>", "BusinessPage", string.Format(@"{0} , {1}", "http://jiangjiu.com", "蔣九的個人主頁"));methodsText.AppendFormat(@"<Field Name='{0}'>{1}</Field>", "CustomerType", "青銅");methodsText.AppendFormat(@"<Field Name='{0}'>{1}</Field>", "Owner", string.Format(@"{0};#{1}", user2.ID.ToString(), user2.Name));methodsText.AppendFormat(@"<Field Name='{0}'>{1}</Field>", "Sharers", string.Join(@";#", user1.ID.ToString(), user1.Name, user2.ID.ToString(), user2.Name, group1.ID.ToString(), group1.Name));methodsText.Append("</Method>");methodsText.Append(@"<Method ID='Insert,2' Cmd='New'><Field Name='ID'>New</Field>");methodsText.AppendFormat(@"<Field Name='{0}'>{1}</Field>", "CustomerName", "孫七");methodsText.AppendFormat(@"<Field Name='{0}'>{1}</Field>", "Gender", "女");methodsText.AppendFormat(@"<Field Name='{0}'>{1}</Field>", "EMail", "qisun@contoso.com");methodsText.AppendFormat(@"<Field Name='{0}'>{1}</Field>", "CellPhone", "13656437456");methodsText.AppendFormat(@"<Field Name='{0}'>{1}</Field>", "WorkAddress", "恭王府");DateTime recency2 = DateTime.Now.AddDays(-3);methodsText.AppendFormat(@"<Field Name='{0}'>{1}</Field>", "Recency", string.Format("{0}-{1}-{2}T{3}:{4}:{5}Z", recency2.Year.ToString("0000"), recency2.Month.ToString("00"), recency2.Day.ToString("00"), recency2.Hour.ToString("00"), recency2.Minute.ToString("00"), recency2.Second.ToString("00")));methodsText.AppendFormat(@"<Field Name='{0}'>{1}</Field>", "Frequency", "0.6");methodsText.AppendFormat(@"<Field Name='{0}'>{1}</Field>", "Monetary", "10000");methodsText.AppendFormat(@"<Field Name='{0}'>{1}</Field>", "BusinessPage", string.Format(@"{0} , {1}", "http://sunqi.com", "孫七的個人主頁"));methodsText.AppendFormat(@"<Field Name='{0}'>{1}</Field>", "CustomerType", "黃金");methodsText.AppendFormat(@"<Field Name='{0}'>{1}</Field>", "Owner", string.Format(@"{0};#{1}", user2.ID.ToString(), user2.Name));methodsText.AppendFormat(@"<Field Name='{0}'>{1}</Field>", "Sharers", string.Join(@";#", user1.ID.ToString(), user1.Name, user2.ID.ToString(), user2.Name, group1.ID.ToString(), group1.Name));methodsText.Append("</Method>");//修改數據:methodsText.AppendFormat(@"<Method ID='Update,1' Cmd='Update'><Field Name='ID'>{0}</Field>", updateItemId.ToString());methodsText.AppendFormat(@"<Field Name='{0}'>{1}</Field>", "CustomerName", "趙六六");methodsText.Append("</Method>");updatesBatch.InnerXml = methodsText.ToString();XmlNode returnNode = listService.UpdateListItems(listName, updatesBatch);Console.WriteLine(returnNode.InnerXml);}?
???? 如果您覺得Web Services+XmlElement不夠先進,還可以使用WCF+LINQ TO XML的方式,使用WCF直接添加服務引用就可以了。
????? 使用WCF訪問SharePoint列表和使用Web Services訪問的SharePoint的方法應該是同出一轍的,只是這里把參數XmlElement對象換成了XElement對象。可是開始的時候我還是遇到了一點小異常。
?????? 對于這個MessageSecurityException類型的異常——“HTTP 請求未經客戶端身份驗證方案'Anonymous'授權。從服務器收到的身份驗證標頭為‘NTLM’。”,我們可以用添加兩句代碼,設置BasicHttpBinding.Security.Mode和BasicHttpBinding.Security.Transport.ClientCredentialType解決。
BasicHttpBinding basicHttpBinding = (BasicHttpBinding)listsSoapClient.Endpoint.Binding;basicHttpBinding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;basicHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;listsSoapClient.ClientCredentials.Windows.ClientCredential = new NetworkCredential(@"username", "password", "domain");XElement queryXElement = XElement.Parse(string.Format(@"<Query><Where><Eq><FieldRef Name='CustomerName'/><Value Type='Text'>{0}</Value></Eq></Where></Query>", "趙六六"));XElement viewFieldsXElement = XElement.Parse(@"<ViewFields><FieldRef Name='CustomerName'/><FieldRef Name='Gender'/><FieldRef Name='Monetary'/><FieldRef Name='BusinessPage'/><FieldRef Name='Owner'/><FieldRef Name='Sharers'/></ViewFields>");XElement resultXElement = listsSoapClient.GetListItems(listName, null, queryXElement, viewFieldsXElement, null, null, null);Console.WriteLine(resultXElement.ToString());?
????? 如果您不喜歡加代碼,還可以修改應用程序配置文件(.config)中的binding節點。?
?
<system.serviceModel><bindings><basicHttpBinding><binding name="ListsSoap" ><security mode="TransportCredentialOnly"><transport clientCredentialType="Ntlm" /></security></binding></basicHttpBinding></bindings><client><endpoint address="http://SharePointServer/_vti_bin/Lists.asmx" binding="basicHttpBinding"bindingConfiguration="ListsSoap" contract="WebsvcLists.ListsSoap"name="ListsSoap" /></client></system.serviceModel>?
?
?
?
總結
以上是生活随笔為你收集整理的SharePoint 2013开发入门探索(二)- 列表操作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中国气象台发布高温黄色预警:未来一周多地
- 下一篇: C#多线程之旅(七)——终止线程