用SharePoint对象模型更新用户个人设置(Guru Pratap Ketepalli)

[原文发表地址] Update User Personal Settings with the SharePoint Object Model (Guru Pratap Ketepalli)

[原文发表时间] 2011-06-23  14:43

每个用户对其网站集都有自己的个人设置。你可以点击登录用户链接,选择My Settings查看详情。

clip_image002[8]

选中My Settings之后,你会被引导到显示你的个人信息。SharePoint重定位userdisp.aspx以下列形式显示用户详情:http://sitecollection/_layouts/userdisp.aspx?Force=True&ID=XXX

clip_image004[4]

SharePoint在站点Content DB中的“UserInfo”表中储存用户数据。当你导航至:http://sitecollection/_layouts/people.aspx,它就会以SharePoint列表形式显现。这些字段可以添加或删除,以适应团队的需要。如果你初次访问网站时,用户文件服务应用不可用,你的数据则会在Active目录中被检索,并储存在目录数据库“用户信息列表”中。于是下次登录时,目录数据库会搜索你的信息,如果找到,就会显示出来。

通过以下的步骤,用SharePoint对象模型可以实现信息的更新。

1. 为拥有所有易扩展属性的用户设置创建实体。

   1: public struct UserSettingsEntity

   2:  

   3: {

   4:  

   5: public string Url;

   6:  

   7: public string LoginName;

   8:  

   9: public string Title;

  10:  

  11: public string Department;

  12:  

  13: public string SipAddress;

  14:  

  15: public string MobilePhone;

  16:  

  17: public string Name;

  18:  

  19: public string Notes;

  20:  

  21: public string Picture;

  22:  

  23: public string FirstName;

  24:  

  25: public string LastName;

  26:  

  27: public string WorkPhone;

  28:  

  29: public string UserName;

  30:  

  31: public string WebSite;

  32:  

  33: public string SPSResponsibility;

  34:  

  35: public string Office;

  36:  

  37: }

2. 为网站集Url创建SPSite对象,检索SPUser以更新用户数据。

   1: private UserSettingsEntity userSettings = new UserSettingsEntity();

   2:  

   3: userSettings.Title = "Title"

   4:  

   5: userSettings.Department = "Dept";

   6:  

   7: userSettings.SipAddress = "sipAddr";

   8:  

   9: userSettings.MobilePhone = "Mobile";

  10:  

  11: userSettings.Name = "Name";

  12:  

  13: userSettings.Notes = "AboutMe";

  14:  

  15: userSettings.Picture = "http://xyz/abc.jpg";

  16:  

  17: userSettings.FirstName = "FirstName";

  18:  

  19: userSettings.LastName = "LastName";

  20:  

  21: userSettings.WorkPhone = "WorkPhone";

  22:  

  23: userSettings.UserName = "UserName";

  24:  

  25: userSettings.WebSite = "http://xyz.com/";

  26:  

  27: userSettings.SPSResponsibility = "AskMeAbout";

  28:  

  29: userSettings.Office = "Office";

  30:  

  31: SPSecurity.RunWithElevatedPrivileges(delegate

  32:  

  33: {

  34:  

  35: using (SPSite site = new SPSite(commandLineArguments["Url"]))

  36:  

  37: {

  38:  

  39: SPWeb web = site.RootWeb;

  40:  

  41: Console.WriteLine("Updating User for {0}", site.Url);

  42:  

  43: // Ensure to get a valid SPUser object for a given login name.

  44:  

  45: SPUser user = web.EnsureUser(userSettings.LoginName);

  46:  

  47: if (null == user)

  48:  

  49: {

  50:  

  51: Console.WriteLine("Failed : Not a valid user");

  52:  

  53: }

  54:  

  55: else

  56:  

  57: {

  58:  

  59: Console.WriteLine("Got SPUser #{0}", user.ID);

  60:  

  61: try

  62:  

  63: {

  64:  

  65: Console.WriteLine("Attempting to update User Information for SPUser #{0}:", user.ID);

  66:  

  67: user.UpdateUserInformation(userSettings);

  68:  

  69: Console.WriteLine("Success : Updated User Information Successfully!.");

  70:  

  71: }

  72:  

  73: catch (Exception ex)

  74:  

  75: {

  76:  

  77: Console.WriteLine("Failed.\r\nError: {0} : {1}",

  78:  

  79: user.ID, ex.Message);

  80:  

  81: }

3. 用SPUserExtension类扩展SPUser来更新用户数据

   1: /// <summary>

   2:  

   3: /// SPUserExtension class which contains 2 extension methods.

   4:  

   5: /// </summary>

   6:  

   7: public static class SPUserExtension

   8:  

   9: {

  10:  

  11: /// <summary>

  12:  

  13: /// Get the User Information for a particular user.

  14:  

  15: /// </summary>

  16:  

  17: /// <param name="user"></param>

  18:  

  19: /// <returns></returns>

  20:  

  21: public static SPListItem GetUserInformation(this SPUser user)

  22:  

  23: {

  24:  

  25: return user.ParentWeb.SiteUserInfoList.GetItemById(user.ID);

  26:  

  27: }

  28:  

  29: /// <summary>

  30:  

  31: /// Update the user information with given title, department, 

  32:  

  33: /// sipAddress and Mobile Phone.

  34:  

  35: /// </summary>

  36:  

  37: public static void UpdateUserInfo (this SPUser user, 

  38:  

  39: UserSettingsEntity userSettings)

  40:  

  41: {

  42:  

  43: SPListItem userInfo = user.GetUserInformation();

  44:  

  45: userInfo["JobTitle"] = userSettings.Title;

  46:  

  47: userInfo["Department"] = userSettings.Department;

  48:  

  49: userInfo["SipAddress"] = userSettings.SipAddress;

  50:  

  51: userInfo["MobilePhone"] = userSettings.MobilePhone;

  52:  

  53: userInfo["Title"] = userSettings.Name;

  54:  

  55: userInfo["Notes"] = userSettings.Notes; // "Its about Me...";

  56:  

  57: userInfo["Picture"] = userSettings.Picture; // "http://xyz/abc.jpg/";

  58:  

  59: userInfo["FirstName"] = userSettings.FirstName;// "FirstName";

  60:  

  61: userInfo["LastName"] = userSettings.LastName; // "LastName";

  62:  

  63: userInfo["WorkPhone"] = userSettings.WorkPhone; // "WorkPhone";

  64:  

  65: userInfo["UserName"] = userSettings.UserName; // "UserName";

  66:  

  67: userInfo["WebSite"] = userSettings.WebSite; // "http://xyz.com/";

  68:  

  69: userInfo["SPSResponsibility"] = userSettings.SPSResponsibility; // "SPSResponsibility";

  70:  

  71: userInfo["Office"] = userSettings.Office; // "Office";

  72:  

  73: userInfo.Update(); //write changes to SharePoint

  74:  

  75: } 

  76:  

  77: }

此代码如UI中显示的每个域一样创建实体类,userinfo对象(用户信息列表)显示了所有含有SharePoint内部域名的域。

服务器构架对象模型概述

[原文发表地址] Server Architecture Object Model Overview

[原文发表时间] 2011-06-06 07:00

下图展示了SharePoint Foundation服务器关于Microsoft.SharePoint.Administration命名空间的集合和对象的构架。

clip_image002[14]

1. SPFarm对象是SharePoint foundation对象模型层次结构中的根节点对象。Servers属性获取一个集合,来代表整个部署中所有的服务器,而Services属性则获取一个集合,来代表所有的服务。

2. 每个SPServer对象代表了一个物理服务器。ServiceInstances属性提供对运行在服务器上的一组服务实例的访问。

3. 每个SPService对象代表一个安装在服务器中的逻辑服务。派生的SPService类类型包括,例如,Windows服务对象,如定时服务,搜索,database服务等,以及Web服务对象,如支持Web应用的发布基本Web内容的服务。

4. SPWebService对象可以实现为特定逻辑服务或应用进行配置设置。WebApplications属性获取运行服务的Web应用集合。

5. 如果服务实现SharePoint Foundation的服务应用程序框架,它就可以被拆分成多个场范围内的配置实例(CFSIs)。每个配置实例都提供服务功能,但每个又有各自的权限和配置设定。

6. 每个在特定服务器上运行的服务实例,或者CFSI,都由一个SPServiceInstance对象代表。

7. SPDatabaseServiceInstance对象代表了运行于数据库服务器计算机上的独立的 数据库服务实例。SPDatabaseServiceInstance类派生自SPServiceInstance类,继承了服务属性,提供访问服务或应用程序的实例实施。Databases属性用于获取服务中使用的内容数据库集合。

8. 每个SPWebApplication对象代表了一个网络信息服务(IIS)网站上的Web应用。SPWebApplication对象支持对证书和其他场范围内的应用设置的访问。Sites属性在Web应用范围内获取网站集合,ContentDatabases属性则获取在Web应用中使用的内容数据库集合。

9. SPContentDatabase继承了SPDatabase类,且代表了包含Web应用用户数据的数据库。Sites属性获取网站集合的集成,为该内容数据库存储数据,而WebApplication属性则获取本源Web应用程序。

10. SPSiteCollection对象代表了在Web应用范围内的网站集合的集合

更多信息,请见服务器及站点构架:对象模型概述

FILESTREAM 和 SharePoint 2010

[原文发表地址] FILESTREAM with SharePoint 2010

[原文发表时间] 3 May 2011 7:00 AM

FILESTREAM是SQL Server 2008中的一个新功能,它使得非结构化的数据能在NTFS文件系统中进行存储。在 sharepoint2010中支持FILESTREAM(本地或者远程)通过在内容可寻址或商业存储中存储大型的二进制非结构化数据,从而减少资金花费.

非结构化数据

非结构化数据指的是没有绑定到已定义的模型或不能很好适用于SharePoint关系表的信息,它可以是Microsoft Office文档文件格式,视频,音频,和相关的文件类型。

结构化数据

SharePoint中的结构化数据指的是和它对应的非结构化数据或BLOB相关联的元数据。关系数据库经常被作为结构化数据的例子。

SQL Server和非结构化数据存储

在SQL Server有三种方法用来存储非结构化数据,它们是 RBS,SQL BLOB和FILESTREAM:

· 远程BLOB存储器(RBS),在这个方法中SharePoint依赖SQL Server中的一个新层来读写存储于数据库之外的独立BLOB存储器(文件系统或专用的BLOB存储器)上的BLOB数据。

· SQL BLOB指的是SharePoint中传统的BLOB存储器, BLOB 数据与结构化的元数据一起被保存在内容数据库

  • FILESTREAM

FILESTREAM概述

FILESTREAM在varbinary(max)数据类型上实现,它告知数据库引擎在文件系统中存储非结构化的数据要通过包含有文件系统目录的FILESTREAM文件组,而不是文件本身(也称为数据容器)。数据容器是数据引擎存储器和文件系统存储器之间的接口。二进制数据类型varbinary是针对存储在SharePoint 2010内容数据库中的大型二进制对象而设计的,它指向 是可变长度二进制数据。(MAX)指的是最大值,即允许存储的最大值2^31-1字节或2GB。远程BLOB存储器并没有提供一种机制超出SharePoint限制的2GB文件大小。在SharePoint中,远程BLOB数据被RBS中的内容数据库中的唯一标识所引用(详见说明)。

clip_image001

FILESTREAM在性能方面有如下优点1)FILESTREAM使用NT系统缓存来缓存文件数据,从而减少对数据库引擎性能的影响2)没有使用SQL Server缓冲池;因此内存可被用于查询操作。

在用SharePoint存储大的BLOB数据时FILESTREAM的价值会得到体现,如视频文件或超过1MB的BLOB数据会从FILESTREAM中获益。

特殊考虑

FILESTREAM及业务可持续型管理

数据库镜像不支持FILESTREAM,这是由于不能在主服务器上创建FILESTREAM文件组,而且数据库镜像不能为包含FILESTREAM文件组的数据库配置。如果FILESTREAM提供程序被用来本地存储BLOB数据(在相同的内容数据库中),那么此数据库就不能配置为数据库镜像。

如果FILESTREAM提供程序用以在独立的SQL数据库或使用第三方BLOB存储器中存储BLOB数据,则内容数据库可以被镜像;然而,数据库镜像只应用在内容数据库中,而不用在BLOB数据中. BLOB数据需要被单独处理,并和对应的相关元数据(内容数据库中)保持同步。对于FILESTREAM BLOB数据库,可以通过日志传送来实现。

想了解FILESTREAM和SQL Server远程BLOB存储器之间的不同之处请查阅

http://blogs.msdn.com/b/sqlrbs/archive/2009/11/18/sql-server-remote-blob-store-and-filestream-feature-comparison.aspx.

FILESTREAM Office Web 应用程序缓存

Word和PowerPoint Web使用Office Web应用程序缓存来创建一个基于浏览器的文档版本,通过文档的缓存版本增加性能和减少服务器机器上的资源开销,或者使用Office Web应用程序缓存使得对相同文档进行多个请求得以实现。

Office Web应用程序缓存出现在两个不同层次,在服务器文件系统和基于每个网站应用程序的“特定的”站点集合上。正如图片呈现给用户的过程一样,文档和演示文稿请求要通过Office Web应用程序的两个缓存来实现。 当Office Web应用程序功能被启用时,Web应用程序中的所有网站集合都使用这两个缓存存储单元。

配置了FILESTREAM的内容数据库将会在配置文件指定的位置存储文档和演示文稿的副本。推荐在没有配置FILESTREAM的数据库上配置Office Web应用程序缓存。

SharePoint 2010配置FILESTREAM

下面的指导说明是用于以演示为目的的单服务器部署和实现本地FILESTREAM提供程序的。

在目标SQL Server实例上启用FILESTREAM

1.在开始菜单上,指向所有程序,指向Microsoft SQL Server 2008 (R2),指向配置工具,然后点击SQL Server配置管理器。

2.在服务清单中右击SQL Server服务,然后点打开

3.在SQL Server配置管理器嵌入式管理单元中,指出你想启用FILESTREAM的SQL Server实例的位置。

4.右击那个实例,选择属性

5.在SQL Server属性对话框中,点选FILESTREAM选项卡。

6.选择Enable FILESTREAM for Transact-SQL access复选框。

7.如果想要从Windows中读写FILESTREAM数据,点击Enable FILESTREAM for file I/O streaming access。在Windows Share Name框中输入Windows共享名字。

8.如果远程客户需要访问保存在这个共享上的FILESTREAM数据,选择允许远程客户访问FILESTREAM数据的权限。

9.点击应用。

10.点击开始,所有程序,Microsoft SQL Server 2008 (R2),然后点SQL Server Management Studio.

11.在SQL Server Management Studio中,点新查询打开查询编辑器。

12. 在查询编辑器中输入下面的Transact-SQL代码:

EXEC sp_configure filestream_access_level, 2

在查询编辑器中输入下面的Transact-SQL代码:RECONFIGURE

设置RBS数据存储器

1.点击开始,所有程序,Microsoft SQL Server 2008 (R2),然后点SQL Server Management Studio.

2.展开数据库。

3.选择你想创建BLOB存储器的内容数据库,然后点击新查询。

4.在SQL Server Management Studio中点击新查询打开查询编辑器。

5.在查询编辑器中,输入Transact-SQL代码:

use [Database Name]

if not exists (select * from sys.symmetric_keys where name = N’##MS_DatabaseMasterKey##’) create master key encryption by password password = N’Admin Key Password !2#4′

在查询编辑器中,输入Transact-SQL代码:

use [Database Name]

if not exists(select groupname from sysfilegroups where groupname=N’RBSFilestreamProvider’)alter database [Database Name] add filegroup RBSFilestreamProvider contains filestream

在查询编辑器中,输入Transact-SQL代码:

use [Database Name]

alter database [Database Name] add file (name = RBSFilestreamFile, filename = ‘c:\BlobStore’) to filegroup RBSFilestreamProvider

安装Microsoft SQL Server 2008 R2 Remote Blob Store

在Microsoft SQL Server 2008 R2功能包处http://www.microsoft.com/downloads/en/details.aspx?FamilyID=ceb4346f-657f-4d28-83f5-aae0c5c83d52&displaylang=en下载Microsoft SQL Server 2008 R2 Remote Blob Store x64包,

以管理员权限打开命令行提示符,执行下面的命令来安装之前下载的RBS.MSI:

msiexec /qn /lvx* rbs_install_log.txt /i RBS.msi TRUSTSERVERCERTIFICATE=true FILEGROUP=PRIMARY DBNAME="<Database Name>" DBINSTANCE="<Instance Name>" FILESTREAMFILEGROUP=RBSFilestreamProvider FILESTREAMSTORENAME=FilestreamProvider_1

备注

在上面的声明中给RBS.MSI指定完整路径,即C:\Users\Administrator\Desktop\RBS.MSI。根据你需要的环境修改DBNAME 和DBINSTANCE的值。

启用远程BLOB存储器

1.在开始菜单上点击所有程序。

2.点击Microsoft SharePoint 2010 Products

3.点击SharePoint 2010 Management Shell

4.在SharePoint 2010 Management Shell中输入下面的Windows PowerShell语句来设置将要配置的内容数据库: $database=Get-SPContentDatabase –Identity “Database Name”

在SharePoint 2010 Management Shell中输入下面的Windows PowerShell语句来获取决定内容数据库如何使用Microsoft SQL Server Remote Blob存储器的执行环境的对象:

$rbs=$database.RemoteBlobStorageSettings

在SharePoint 2010 Management Shell中输入下面的Windows PowerShell语句来决定是否已经为选中的内容数据库安装了RBS:$rbs.Installed()

备注

$rbs.Installed()的结果应该是True,如果结果是False的话,可以通过审核rbs_install_log.txt来验证RBS.MSI是否已经成功地安装了。确保安装语句运行在SharePoint 2010 Management Shell下,输入下面的语句来为选中的内容数据库启用RBS:$rbs.Enable()

在SharePoint 2010 Management Shell中输入Windows PowerShell语句来为选中的内容数据库设置RBSprovider:$rbs.SetActiveProviderName($rbs.GetProviderNames()[0])

$rbs

备注

$rbs的结果是:

clip_image002

表1

表1的附件

Enabled指定对于选中的内容数据库RBS是否已启用。

ActiveProviderName是将要存入SQL Remote Blob存储器的新文件的名字。如果新文件没有用SQL Remote Blob存储器保存的话该项将会为空。

MinimumBlobStorageSize指的是考虑是否值得使用RBS存储的BLOB的最小尺寸,超出了指定的MinimumBlobStorageSize的BLOB数据将被保存到RBS。

FILESTREAM性能数据显示了超过1MB的BLOB数据具有最高效的流性能。请查看http://msdn.microsoft.com/en-us/library/cc949109(SQL.100).aspx

clip_image003

配置MinimumBlobStorageSize:

1.在开始菜单点击所有工程。

2.点击Microsoft SharePoint 2010 Products

3.点击SharePoint 2010 Management Shell

4.在SharePoint 2010 Management Shell中输入下面的Windows PowerShell语句来配置MinimumBlobStorageSize为1MB:

$database = Get-SPContentDatabase “Database Name”

$database.RemoteBlobStorageSettings.MinimumBlobStorageSize=1048576

$database.Update()

UpgradePersistedProperties指被删除或更改了的字段名或值的集合。

验证安装

验证FILESTREAM配置和RBS安装:

1.点击开始,点所有程序,接着点Microsoft SQL Server 2008,然后点SQL Server Management Studio

2.展开数据库。

3. 选择想要创建BLOB存储器的内容数据库,然后点击新查询。

4.在SQL Server Management Studio中,点击新查询打开查询编辑器。

5.在查询编辑器中输入下面的Transact-SQL代码:

USE [Database Name]

SELECT * FROM dbo.DatabaseInformation

保证RBSCollectionId和RBSProvider两行都可用。

测试RBS数据存储器

1.在一个网站的已配置好的内容数据库中选择一个想要的文档库。

2.上载一个大于1 MB的文件。

3.在有数据存储器的计算机上点击打开,然后点计算机。

4.浏览RBS数据存储器目录。

5.浏览文件并打开最近修改(而不是$FSLOG)的文件夹。在那个文件夹下打开最近修改的文件。验证这个文件和你上载的文件有相同的大小和内容。如果不同,请确认RBS已经正确安装并启用。

数据存储器 目录结构与下面的图例相似。

clip_image004

备注

当在上传一个大于MinimumBlobStorageSize的文档

出现事件错误:“ The URL ‘<Document Library>/File’ is invalid. It may refer to a nonexistent file or folder, or refer to a valid file or folder that is not in the current Web.”时,请打开SQL Server Configuration Manager,启用Enable FILESTREAM for file I/O streaming access并重新启动SQL Server (MSSQLSERVER)服务。

附加资源

SharePoint Online -用SharePoint Online进行基于云的协作

[原文发表地址] SharePoint Online – Cloud-Based Collaboration with SharePoint Online

随着Office 365的发行,微软将提供下一代微软在线服务一项基于SharePoint 2010, Exchange 2010 Lync Server 2010,并基于云的协作和通信服务。Office 365目前正处于测试阶段。在微软管理的云数据中心中,它会提供基于订阅的SharePoint, ExchangeLync的软件软件即服务(Saas)。

SharePoint Online作为SharePoint 2010的云版本,将为用户提供许多与SharePoint 2010相同的功能,而用户无需管理可扩展性和安全协作解决方案所需的硬件和软件。在本文中,我会通过构建在SharePoint Online中运行的解决方案,来概括SharePoint Online开发与SharePoint 2010开发的异同点。

随着SharePoint Online下一版本的发布,SharePoint开发人员将能够使用他们在SharePoint 2010里使用的相同技能和工具来开发协作方案,这些工具和技能包括 Visual Studio 2010, SharePoint Designer 2010, C#Visual Basic以及SharePoint API SDK。在开发内部部署的SharePoint和开发云端的SharePoint有很多共同点,但是同样也有很多显著的差异会影响你如何构建解决方案。

了解这些差异将有助于您了解哪些解决方案可以在SharePoint Online里创建并运行,以及如何开发这些解决方案。

自定义SharePoint Online的相似点

SharePoint 2010的开发中,您可以通过使用浏览器和SharePoint Designer 2010来自定义SharePoint,并通过使用Visual Studio 2010生成解决方案。而对于SharePoint Online来说,通过浏览器和SharePoint Designer 2010来进行自定义与SharePoint 2010版本大致相同(鉴于下一节所述的功能差异)。使用Visual Studio 2010开发SharePoint Online解决方案也大体相同。通过其中迭代开发的集成调试体验,在Visual Studio 2010中的开发基于SharePoint 2010的本地实例,(无论运行在Windows 7Windows Server 2008 R2的本地机或虚拟机(VM)中)。开发完成后,该解决方案通过SharePoint 2010同样提供的解决方案库,被上传到SharePoint Online中。

自定义SharePoint Online的关键差异

尽管SharePoint Online建立在SharePoint 2010的基础上,但当您在开发SharePoint Online的解决方案时,仍有一些关键差异需要您牢记。首先,SharePoint Online只支持网站和Web范围的解决方案。它运行在多租户云中,多租约运行在一个共享的数据库基础架构中,所以Farm范围(功能在这整个Farm范围内被激活)的解决方案不被支持也并不奇怪。同样,在SharePoint Online中,您的SharePoint租赁最高访问级别为网站集级别,所以Web应用范围的功能(功能在一个Web应用中的每个Web站点中运行)也不被支持

其次,SharePoint Online只支持部分信任的解决方案。完全信任的解决方案,也就是那些访问权限超出网站级别,或者可以授权以管理员级别运行Farm上的解决方案也不受支持

最后,尽管SharePoint Online是建立在SharePoint 2010的基础上,但它和内部服务器部署的2010版在功能上并没有百分百的相同。如欲了解SharePoint 2010SharePoint Online的完整功能比较,请参阅微软SharePoint Online测试版服务说明,您可从bit.ly/bBckolOffice 365测试版服务说明中获取有关信息

功能列表显示大多数的SharePoint自定义功能都受支持。不支持业务连接服务(BCS,外部列表和从SharePoint
Online
外部调用Web服务(不在部分信任的解决方案中被支持)将会对构建运行在SharePoint Online的解决方案造成巨大影响。不过,我们有计划在将来发布的版本中支持BCS服务。

了解了这些异同点后,让我们看看一些解决方案的示例。您可以在SharePoint
Online
中生成这些类型的解决方案并运行,这包括了沙盒解决方案和SharePoint客户端对象模型(OM)。其他类型的解决方案,例如通过声明式工作流解决方案将业务流程自动化等,将在以后的文章中介绍。

用沙盒解决方案开发SharePoint Online

在上节中,您了解到SharePoint
Online解决方案必须限于站点或Web范围的功能,数据访问限定于站点范围的集合,且必须在部分信任环境中运行。开发以沙盒模式运行的解决方案符合所有这些条件,同时让SharePoint
Online管理员可以直接将解决方案上传到解决方案库,从而实现轻松部署。

Visual Studio 2010为沙盒解决方案提供了极大支持,其中包括项目模板和项目项模板支持,创建新项目为沙盒解决方案的SharePoint自定义向导,站点集范围的SharePointAPIIntelliSense支持,以及调试和打包支持。若要开始构建SharePoin Online解决方案,你需要开发并调试作用在本地SharePoint 2010的解决方案。你需要在安装SharePoint
2010Visual Studio 2010的同时,安装64位的Windows 7Windows Server 2008 R2 。另一个着手构建解决方案的好办法是使用Information Worker Demonstration and Evaluation Virtual Machine
RTM)。它提供了虚拟化的SharePoint
2010开发环境(从
bit.ly/ezfe2Y 下载)。我也推荐Visual Studio SharePoint 2010 Power Toolsbit.ly/azq882) ,它增加了沙盒和沙盒模式的可视Web部件项目项模板的编译时支持。

在本文的示例中,我将运用下面的这个简单场景来构建解决方案:即为虚拟的Contoso公司员工提供他们采购系统所不支持的采购申请功能。首先,我会在内部部署的SharePoint 2010开发环境中创建一个站点集和站点。我使用的是前面提到过的虚拟机,因此我创建了http://o365dpe.contoso.com/sites/spomsdnmag/purchasing。我的第一个解决方案会部署一个用来追踪这些非标准化购买的列表。我会打开Visual Studio 2010,选择文件|新项目,并在新建项目对话框中选择“空白的SharePoint项目”,然后将该项目命名为 PurchasingMgr

SharePoint自定义向导对话框中,我将在“what local site… (您想使用哪个地方站点进行调试) 一项中键入我站点的URLhttp://o365dpe.contoso.com/sites/spomsdnmag/Purchasing/,选择“部署为沙盒解决方案”,然后点击“完成”,如图一所示。

1 制定PurchasingMgr的站点和信任级别

接下来,我会在解决方案资源管理器中选择PurchasingMgr项目,右击并选择“添加新项目”。在“添加新项目”
对话框中,我会在已安装的模板节点中选择
SharePoint 2010支持的SharePoint项模板。不是所有这些模板都支持沙盒解决方案,所以那些模板在SharePoint Online中也并非都被支持。图2显示了支持沙盒解决方案的项目模板。

2 在沙盒解决方案中支持的项目模板

项模板

是否兼容沙盒?

备注

可视Web 部件

需要在SharePoint 服务器上安装ASCX 文件

可视Web 部件 (沙盒模式)

通过安装 Visual Studio 2010 SharePoint Power Tools提供此项功能

Web 部件

顺序工作流

要求将工作流解决方案部署为Farm解决方案

状态机工作流

要求将工作流解决方案部署为Farm解决方案

业务数据连接模型

需要将BCS解决方案部署为完全信任的解决方案 ;这是在 SharePoint Online 中不支持的功能

应用程序页

需要将 ASPX 页面部署到
SharePoint 服务器

事件接收器

模块

内容类型

通过内容类型添加的列表定义

列表定义

列表实例

空元素

用户控件

需要在 SharePoint 服务器上安装的 ASCX 文件

为了生成我的列表,我将选择内容类型项模板来定义站点列和内容类型,并键入NonStandBusPurchaseRequestsCT作为名字。

SharePoint
自定义向导中,我会选择“项”作为基础内容类型,然后单击“完成”。内容类型将包含一个标题列,一个描述列和一个价格列。我会通过替换用XML创建的Elements.xml中的内容(图3种)来声明定义这些内容类型。

3
通过Elements.xml
定义NonStandBusPurchaseRequestsCT

<?xml
version=”1.0″ encoding=”utf-8″?>

<Elements xmlns=”http://schemas.microsoft.com/sharepoint/”>

<Field SourceID=”http://schemas.microsoft.com/sharepoint/v3″

ID=”{A74E67E5-8905-4280-90C9-DEBFFC30D43D}”

Name=”RequestDescription”

DisplayName=”Description”

Group=”Purchasing Manager Custom Columns”

Type=”Note”

DisplaceOnUpgrade=”TRUE” />

<Field SourceID=”http://schemas.microsoft.com/sharepoint/v3″

ID=”{CB5054F5-0C60-4DBE-94D2-CEFBFB793C7F}”

Name=”Price”

DisplayName=”Price”

Group=”Purchasing Manager Custom Columns”

Type=”Currency”

DisplaceOnUpgrade=”TRUE” />

<!– Parent
ContentType: Item (0x01) –>

<ContentType ID=”0x010078a81c8413f54917856495e56e7c09ed”

Name=”Purchasing Manager – Non-Standard Business Purchase
Requests Content Type”

Group=”Purchasing Manager Content Types”

Description=

“Non-Standard
Business Purchase Requests Content Type

for the
Purchasing Manager Solution”

Inherits=”TRUE”

Version=”0″>

<FieldRefs>

<FieldRef ID=”{fa564e0f-0c70-4ab9-b863-0177e6ddd247}” Name=”Title”

DisplayName=”Title” />

<FieldRef ID=”{A74E67E5-8905-4280-90C9-DEBFFC30D43D}”

Name=”RequestDescription”

Required=”TRUE” />

<FieldRef ID=”{CB5054F5-0C60-4DBE-94D2-CEFBFB793C7F}” Name=”Price”

Required=”TRUE” />

</FieldRefs>

</ContentType>

</Elements>

接下来,我将在解决方案资源器中右击PurchasingMgr并选择“添加新项目”,来定义基于该内容类型的列表。我将选择项模板“通过内容类型添加的列表定义”,并命名列表定义为NonStandBusPurchaseRequestsListDefn,然后单击“添加”。

SharePoint 自定义向导中,我会选择之前 创建的内容类型,并选中“添加列表实例”。为NonStandBusPurchaseRequestsListDefn创建的Elements.xml如图4所示。

4 通过Elements.xml 定义NonStandBusPurchaseRequestsListDefn

<?xml
version=”1.0″ encoding=”utf-8″?>

<Elements xmlns=”http://schemas.microsoft.com/sharepoint/”>

<!– Do not change
the value of the Name attribute below.

If it does not
match the folder name of the List Definition project item,

an error will
occur when the project is run. –>

<ListTemplate

Name=”NonStandBusPurchaseRequestsListDefn”

Type=”10051″

BaseType=”0″

OnQuickLaunch=”TRUE”

SecurityBits=”11″

Sequence=”410″

DisplayName=”Purchasing Manager –

Non-Standard
Business Purchase Requests List Definition”

Description=

“Non-Standard
Business Purchase Requests List Definition

for the
Purchasing Manager Solution”

Image=”/_layouts/images/itgen.png”/>

</Elements>

请注意,在我创建的功能里的每个列表定义都需要通过一个大于10000的唯一类型值来辨别(以避免与定义在SharePoint中的列表冲突),而且我使用这个值来定义基于那个列表定义的任何列表实例。

要将自定义列表添加到列表视图中,我将打开创建的Schema.xml,并将FiledRef元素添加到默认视图中,如5所示

5 添加自定义列表到NonStandBusPurchaseRequestsListDefn默认视图中

<View BaseViewID=”1″ Type=”HTML” WebPartZoneID=”Main”

DisplayName=”$Resources:core,objectiv_schema_mwsidcamlidC24;”

DefaultView=”TRUE” MobileView=”TRUE”
MobileDefaultView
=”TRUE”

SetupPath=”pages\viewpage.aspx” ImageUrl=”/_layouts/images/generic.png”

Url=”AllItems.aspx”>

<Toolbar Type=”Standard”
/>

<XslLink Default=”TRUE”>main.xsl</XslLink>

<RowLimit Paged=”TRUE”>30</RowLimit>

<ViewFields>

<FieldRef Name=”Attachments”>

</FieldRef>

<FieldRef Name=”LinkTitle”>

</FieldRef>

<FieldRef ID=”{A74E67E5-8905-4280-90C9-DEBFFC30D43D}”

Name=”RequestDescription” />

<FieldRef ID=”{CB5054F5-0C60-4DBE-94D2-CEFBFB793C7F}” Name=”Price”
/>

</ViewFields>

<Query>

<OrderBy>

<FieldRef Name=”ID”>

</FieldRef>

</OrderBy>

</Query>

<ParameterBindings>

<ParameterBinding Name=”NoAnnouncements”

Location=”Resource(wss,noXinviewofY_LIST)” />

<ParameterBinding Name=”NoAnnouncementsHowTo”

Location=”Resource(wss,noXinviewofY_DEFAULT)” />

</ParameterBindings>

</View>

最后,我将定义列表中的一个实例。我选择位于NonStandBusPurchaseRequestsListDefn下的ListInstance1并命名其为NonStandBusPurchaseRequestsListInstance。我会打开Elements.xml,通过添加以下XML来使列表基于内容类型,并为用户提供帮助性说明:

<?xml
version=”1.0″ encoding=”utf-8″?>

<Elements xmlns=”http://schemas.microsoft.com/sharepoint/”>

<ListInstance Title=”Non-Standard
Business Purchase Requests”

OnQuickLaunch=”TRUE”

TemplateType=”10051″

Url=”Lists/NonStandBusPurchaseRequestsListInstance”

Description=

“Non-Standard
Business Purchase Requests List

for the
Purchasing Manager Solution”>

</ListInstance>

</Elements>

Visual Studio 2010中,我会选择“调试”,然后开始调试来测试解决方案。此解决方案被打包并部署到内部部署的网站,如图6所示。

6 调试PurchasingMgr 解决方案

我已经测试了PurchasingMgr方案,我现在可以将其部署到SharePoint Online上了。我将使用工作组站点模板在SharePoint Online上创建一个新的站点集,将其命名为Purchasing。回到Visual Studio 2010中,我会在项目资源管理器中右击PurchasingMgr项目并选择“打包”来打包解决方案。要将解决方案部署到SharePoint
Online,我只需要将其上传到解决方案库并激活站点功能(我需要站点集管理员权限来实现此操作)。要做到这一点,我要登陆SharePoint Online,定位到我的站点集,选择“站点操作
|站点设置”,再选择“解决方案”来访问解决方案库。在解决方案库中,我将单击“解决方案”选项卡,在功能区选择“上传解决方案”,然后在bin/Debug中浏览PurchasingMgr.wsp文件并单击OK,然后激活。你会看到您的解决方案在解决方案库中,如7所示。

7 PurchasingMgr部署到SharePoint Online

接下来,为了激活包含我的网站列,内容类型和列表的功能,我会定位到Purchasing站点并选择站点操作|站点设置|管理站点功能。我会选择Purchasing
Manager(采购经理)内容类型和列表功能,并选择激活。此时您应该会在SharePoint
Online站点看见非标准业务采购请求列表。

Purchasing Manager(采购经理)只是您在SharePoint
Online中利用沙盒解决方案可以完成的其中一个例子。牢记沙盒解决方案的限制,以及SharePoint
Online所支持的功能的限制,你就可以创建能在SharePoint
2010SharePoint Online运行的解决方案。

Silverlight创建客户端解决方案

SharePoint 2010引入了客户端OM。。它为使用运行在远程机器(包括Silverlight
ECMAScript的浏览器)的Microsoft
.NET Framework Silverlight
ECMAScript(包括JavaScript JScript)来构建的SharePoint客户端提供了面向对象和面向客户端的API。该APIMicrosoft.SharePoint服务器端的命名空间是保持一致的,因此学习起来很容易。该API在受支持的客户端类型中也是保持一致的,因此将知识应用于不同的客户端解决方案中也很容易。客户端OM
APISharePoint Online支持。对于云开发,它也是一个很有价值的工具。

比如,我可以使用客户端OM创建一个Silverlight 4应用程序,将项目添加至我的列表中,并托管此应用程序在沙盒Web部件中。为此,我将打开Visual
Studio 2010,选择文件
|新项目,并在新项目对话框中选择“清空SharePoint项目”。我将项目命名为PurchasingMgrWP并单击OK。我会再次创建此解决方案为沙盒解决方案并使其指向我内部部署的Purchasing站点中。为了创建Silverlight
4应用程序,我会右击PurchasingMgrWP解决方案,在已安装模板下选择Silverlight,选择Silverlight应用程序并将解决方案命名为NonStandBusPurchaseReqsSLOM。在新的Silverlight应用程序对话框中,我会取消“在新Web站点中托管Silverlight应用程序”对话框(我们将通过在SharePoint中托管应用程序来进行测试)并选择Silverlight版本为Silverlight 4

若要引用Silverlight客户端OM API ,我会将Microsoft.SharePoint.Client.Silverlight.dllMicrosoft.SharePoint.Client.Silverlight.Runtime.dll的引用添加到项目中。这两个程序集位于C:\Program
Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\ClientBin中。接下来,我将打开MainPage.xaml来创建Silverlight UI,并用8的代码替换XAML

8 NonStandBusPurchaseReqsSLOM MainPage.xaml

<UserControl xmlns:sdk=”http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk”

x:Class=”NonStandBusPurchaseReqsSLOM.MainPage”

xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”

xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”

xmlns:d=”http://schemas.microsoft.com/expression/blend/2008″

xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006″

mc:Ignorable=”d”

d:DesignHeight=”300″ d:DesignWidth=”400″>

<Grid x:Name=”LayoutRoot” Background=”White”>

<Grid.ColumnDefinitions>

<ColumnDefinition />

<ColumnDefinition />

</Grid.ColumnDefinitions>

<Grid.RowDefinitions>

<RowDefinition Height=”Auto”/>

<RowDefinition Height=”Auto”/>

<RowDefinition Height=”Auto”/>

<RowDefinition Height=”Auto”/>

</Grid.RowDefinitions>

<sdk:Label Content=”Title:” Grid.Column=”0″ Grid.Row=”0″ Margin=”3″/>

<sdk:Label Content=”Description:” Grid.Column=”0″ Grid.Row=”1″ Margin=”3″/>

<sdk:Label Content=”Price:” Grid.Column=”0″ Grid.Row=”2″ Margin=”3″/>

<TextBox Name=”Title” Grid.Column=”1″ Grid.Row=”0″ Margin=”3″/>

<TextBox Name=”Description” Grid.Column=”1″ Grid.Row=”1″ Margin=”3″/>

<TextBox Name=”Price” Grid.Column=”1″ Grid.Row=”2″ Margin=”3″/>

<Button Content=”Add” Grid.Column=”1″ Grid.Row=”3″ Margin=”3″

Name=”addNonStanPurchaseReq” HorizontalAlignment=”Right”

Height=”25″ Width=”100″ Click=”addNonStanPurchaseReq_Click” />

</Grid>

</UserControl>

8中的XAML定义文本框和一个按钮来收集信息以添加到我的列表,如9所示

9 设计器中的 MainPage.xaml

双击设计器中的按钮,将类替换成10中的代码。

10 addNonStanPurchaseReq_Click

using System;

using System.Collections.Generic;

using System.Linq;

using System.Net;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Animation;

using System.Windows.Shapes;

using Microsoft.SharePoint.Client;

namespace NonStandBusPurchaseReqsSLOM

{

public
partial class MainPage : UserControl

{

private
string webUrl;

public
MainPage(string url)

{

webUrl = url;

InitializeComponent();

}

private
void addNonStanPurchaseReq_Click(object sender, RoutedEventArgs
e)

{

ClientContext
clientContext = new ClientContext(webUrl);

Web webSite =
clientContext.Web;

ListCollection
webLists = webSite.Lists;

List nonStandBusPurList =


clientContext.Web.Lists.GetByTitle(

“Non-Standard Business Purchase Requests”);

ListItem newListItem =


nonStandBusPurList.AddItem(new
ListItemCreationInformation());

newListItem[“Title”] = Title.Text;

newListItem[“RequestDescription”] =
Description.Text;

newListItem[“Price”] = Price.Text;

newListItem.Update();


clientContext.Load(nonStandBusPurList, list => list.Title);


clientContext.ExecuteQueryAsync(onQuerySucceeded, onQueryFailed);

}

private
void onQuerySucceeded(

object
sender, ClientRequestSucceededEventArgs args)

{


Dispatcher.BeginInvoke(() =>

{

MessageBox.Show(“New
item added.”
);

});

}

private
void onQueryFailed(object
sender,


ClientRequestFailedEventArgs args)

{


Dispatcher.BeginInvoke(() =>

{

MessageBox.Show(“Request
failed. “
+ args.Message + “\n”
+

args.StackTrace);

});

}

}

}

10中的代码遵循了客户端OM代码的通用模式。首先,我将通过ClientContext类(相当于SPCContext类)访问客户端上下文。接下来,我将分别通过WebListCollectionList类分别访问站点和列表。请注意SPWeb,
SPListCollection SPList类之间的相似点。最后,我将通过调用List.AddItem方法创建一个ListItem,将用户界面中的数据填充进去并调用ListItem.Update方法。只有当调用ClientContext.Load
ClientContext.ExecuteQueryAsync方法来执行查询时,ListItem才真正被创建。请注意您可以通过ClientContext.Load并调用ClientContext.ExecuteQueryAsync方法加载多个查询,从而节省了到服务器之间的往返。

为了部署Silverlight 4应用程序,我会添加一个板块来和我的Web Part

项目一起部署应用程序。我会在解决方案资源管理器中选择PurchasingMgrWP,右击并选择添加新项目|模块,将模块命名为ClientBin。我将使用以下XML来替换新创建的Elements.xml中的内容:

<?xml version=“1.0” encoding=“utf-8”?>

<Elements xmlns=“http://schemas.microsoft.com/sharepoint/”>

<Module Name=“ClientBin”>

<File Path=“ClientBin\NonStandBusPurchaseReqsSLOM.xap”

Url=“ClientBin/NonStandBusPurchaseReqsSLOM.xap”
/>

</Module>

</Elements>

XMLNonStandBusPurchaseReqsSLOM.xap 文件部署到SharePoint 站点的ClientBin文件夹中。

为使用ClientBin模块部署NonStandBusPurchaseReqsSLOM项目的输出内容,我将在解决方案资源管理器中选择ClientBin模块,并打开“项目输出引用”属性对话框。我将单击“增加”,选择NonStandBusPurchaseReqsSLOM作为项目名称,并选择ElementFile作为部署类型。

接下来,我会将自定义Web部件添加到我的SharePoint解决方案中,来承载我的Silverlight
4应用程序。我会在解决方案资源管理器中选择PurchasingMgrWP,右击并选择添加|新项目,选择Web部件并将其命名为NonStandBusPurchaseReqsWP。为了将参数传递给我的Silverlight
4应用程序(例如一个用来创建ClientContext的站点的URL),我将使用一个自定义Web部件。为此,我会添加一个名为SilverlightObjectTagControl.cs的帮助类,并用11的代码替换类的代码体。

11 添加SilverlightObjectTagControl.cs帮助类

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

namespace PurchasingMgrWP

{

class
SilverlightObjectTagControl : WebControl

{

public
string Source { get;
set; }

public
string InitParameters { get; set; }

protected
override void
CreateChildControls()

{

base.CreateChildControls();

if
(Source != null && Source != “”)

{

string
width = (this.Width == Unit.Empty) ? “400” :

this.Width.ToString();

string
height = (this.Height == Unit.Empty) ? “300” :

this.Height.ToString();

this.Controls.Add(new LiteralControl(


<div>”
+


<object data=\”data:application/x-silverlight-2,\” +


type=\”application/x-silverlight-2\” width=\””
+
width +


\” height=\”” +
height + “\”>” +


<param name=\”source\” value=\””
+ Source + “\”/>” +


<param name=\”onerror\” value=\”onSilverlightError\”
/>”
+


<param name=\”background\” value=\”white\” />”

+


<param name=\”minRuntimeVersion\” value=\”4.0.50826.0\”
/>”
+


<param name=\”autoUpgrade\” value=\”true\” />”

+


<param name=\”initparams\” value=\””
+
InitParameters + “\” />” +


<a href=\”http://go.microsoft.com/fwlink/?LinkID=”
+


149156&v=4.0.50826.0\” +


style=\”text-decoration: none;\”>”
+


<img src=\”http://go.microsoft.com/fwlink/?LinkId=161376\” +


alt=\”Get Microsoft Silverlight\” style=\”border-style:
none\”/>”
+


</a>”
+


</object>”
+


<iframe id=\”_sl_historyFrame\” +


style=.visibility:hidden;height:0;width:0;border:0px.></iframe>”

+


</div>”

));

}

}

}

}

11中的SilverlightObjectTagControl有两个属性:Source用来向Web部件传递Silverlight应用程序的URL,从而可以在Web部件中加载该Silverlight应用程序;InitParameter用来将初始参数传递到Silverlight
4应用程序中。在CreateChildControls方法中,这些属性被用于为Silverlight应用程序生成<object
/>标签。若要使用这个类,打开NonStandBusPurchaseReqsWP.cs,并将该类中的代码替换成12的代码。

12 NonStandBusPurchaseReqsWP.cs

using System;

using System.ComponentModel;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

using
System.Web.UI.WebControls.WebParts;

using Microsoft.SharePoint;

using Microsoft.SharePoint.WebControls;

namespace
PurchasingMgrWP.NonStandBusPurchaseReqsWP

{

[ToolboxItemAttribute(false)]

public
class NonStandBusPurchaseReqsWP
: WebPart

{

protected
override void
CreateChildControls()

{

base.CreateChildControls();


SilverlightObjectTagControl slhc =

new SilverlightObjectTagControl();

slhc.Source =
SPContext.Current.Site.Url +

“/ClientBin/NonStandBusPurchaseReqsSLOM.xap”;

slhc.InitParameters = “url=” + SPContext.Current.Web.Url;

this.Controls.Add(slhc);

}

}

}

12中的代码创建了一个SilverlightObjectTagControl实例,将Source属性设为在ClientBin
中的Silverlight应用程序URL,并设置InitParameter来保存现有站点的URL(非标准业务购买请求列表可在此找到)。要将URL传递到NonStandBusPurchaseReqsSLOM中的MainPage类构造器中,打开App.xaml.cs,并为Application_Startup事件添加以下代码:

private void
Application_Startup(object sender,

StartupEventArgs e)

{

string
url = e.InitParams[“url”];

this.RootVisual
= new MainPage(url);

}

若要测试Web Part ,将PurchasingMgr.wsp包部署到内部部署的Purchasing站点来部署非标准业务购买请求列表(该列表在之前提到的调试阶段结束的时候被移除),然后从Visual
Studio 2010中调试PurchasingMgrWP解决方案。当Web 部件被添加到\Purchasing\Home.aspx中时,它允许我将项目从Silverlight直接添加到列表中,如1314所示

13 运行中的NonStandBusPurchaseReqsWP

14 更新后的非标准业务购买请求列表

开发和调试内部部署站点使我能使用Visual
Studio 2010来同时调试SharePointSilverlight 4,直到我将解决方案全部测试完毕为止。这时,我会上传PurchasingMgrWP.wspSharePoint Online的解决方案库中。

SharePoint客户端OM提供了我们所熟悉而统一的面向对象的API来访问SharePoint Online的列表和库。此APIMicrosoft.SharePoint API的一个子集,并限于站点集及更小的范围,这点与SharePoint
Online的开发要求很完美达统一起来。

云中的SharePoint解决方案

总的来说,SharePoint Online为开发者提供了一个独特的机会来运用他们已有的技术和工具去构建面向云的SharePoint解决方案。通过了解SharePoint Online自定义功能(包括支持和不支持哪些内容),沙盒解决方案,SharePoint客户端OMSharePoint Designer 2010构建的声明性工作流,您可以使用SharePoint Online生成可在云运行的SharePoint解决方案。在测试版本过程中,如您欲了解更多的SharePoint Online的开发信息,请查看SharePoint
Online Developer资源中心
msdn.com/sharepointonline)

使用带有选项域的LINQ-to-SharePoint (Ricky Kirkham)

[原文发表地址]Using LINQ-to-SharePoint with Choice Fields (Ricky Kirkham)

[原文发表时间]3 May 2011 7:00 AM

如果你们喜欢使用LINQ to SharePoint提供程序来执行服务器端代码数据查询的话,迟早会遇到在LINQ查询强制类型的世界和SharePoint的分散本质之间产生的一种冲突。要使用LINQ查询,源代码必须包括一个对象关系映射,就是将列表和内容类型转换为类,把字段转换成内容类型类的强类型属性。这个代码是由SPMetal工具生成的。但是当解决方案部署之后,网站拥有者仍然可以添加新列表。在对象关系映射中这些新添加的列表并没有被包括进去。类似地,新的字段也可以被添加到网站和列表中。在对象关系映射中这些也并没有被包括。

当然,如果你们的LINQ查询及数据更改仅涉及到那些在设计阶段生成对象关系映射时出现的列表,内容类型和字段,新实体的存在并不会造成问题。但是有时候设计时对象关系映射和运行阶段被查询的网站内容的不匹配会给LINQ工作带来问题。许多这类问题可以通过用Extending the
Object-Relational Mapping
中介绍的扩展对象关系映射技术来解决。本篇博客中,我想描述一个问题并提出一个在后面的文章中不会提到的解决方案。

 

SPMetal为一个选项字段生成代码时,它将会创造一个枚举类型来表示该字段可能的值。例如,下面的代码是为标准的任务列表上的标准优先级域生成的:

public enum Priority : int

{

None = 0,

Invalid = 1,

[Microsoft.SharePoint.Linq.ChoiceAttribute(Value = “(1) High”)]

_1High = 2,

[Microsoft.SharePoint.Linq.ChoiceAttribute(Value = “(2) Normal”)]

_2Normal = 4,

[Microsoft.SharePoint.Linq.ChoiceAttribute(Value = “(3) Low”)]

_3Low = 8,

}

但是如果列表拥有者把“(4)
Trivial”
作为一个额外的可能选项添加到优先级列的定义中,会发生什么? 对于在优先级域具有新值的任务项目,一个 LINQ查询会返回“Invalid”而不是“(4)
Trivial”
作为字段的值

 

问题的本质是,可以被最终用户修改的数据架构是固有的弱类型,但是对象关系映射将一个强类型束缚施加到架构上。为了解决这个问题,我们需要放松那个束缚;也就是,我们需要削弱,从enum转换到string的选择字段类型;但是不要削弱得太多。

 

一个简单的解决方案,但并不总是适用

 

如果你们想要做的都是查询数据,SPMetal工具的可配置性提供了一个简单解决方案。通过使用命令行调用SPMetal中关联的parameters.xml文件,可以修改工具的默认行为。如何这么操作的详细情况请参考SPMetalOverriding SPMetal Defaults by Using a Parameters XML File。在这种情况下,让SPMetal把优先级域看作是一个字符串而不是具有parameters.xml文件的枚举,如下所示:

<?xml
version=”1.0″ encoding=”utf-8″?>

<Web xmlns=”http://schemas.microsoft.com/SharePoint/2009/spmetal“>

<ContentType Name=”Task”
Class=”Task”>

<Column Name=”Priority” Member=”Priority” Type=”String” />

</ContentType>

<ExcludeContentType Name=”Summary
Task”/>

</Web>

补充:你也会注意到我把任务摘要内容类型从对象关系映射中删除了,这是为了避免再次做我的方案中一些不得不做的琐事,因为任务摘要内容类型也有优先级域。

通过这个正在使用的parameters.xml文件,SPMetal将会为Task类的优先级属性创建下面的签名:

public String
Priority

以下签名相反,它会默认创建的。public System.Nullable<Priority> Priority

补充:优先级域也在标准团队网站作为站点列,因此SPMetal将仍然会生成枚举来保存为站点列的类型,但是它也不会使用枚举作为任务内容类型中的优先级列类型。

现在你的LINQ查询将会返回“(4)
Trivial”
或其他添加到优先级列定义的选项,而不是“Invalid”

什么时候你也想要写到选项域

当你要用LINQ-to-SharePoint提供者写数据并查询该数据时怎么办? (你可以参考How to: Write to
Content Databases Using LINQ to SharePoint
来操作)既然已经弱化了优先级域的类型,那么代码就可以将任意字符串写到域中了,即使是一个不在选项列表定义列中的字符串。你可以保证你也可以写在写到选项域之前检查可能有的选项列表的代码,但是你不想每次用LINQ
to SharePoint
提供商写到选项域时去重写那些代码。确保必要的确认每次都执行的一个方法就是把确认代码放在代表选项域属性的设置函数里。下面介绍一种方式,你可以做到这一点

首先你需要做的是删除SPMetal生成的文件中的声明且实现属性的代码。如果不这样做的话,当SPMetal生成代码时那个属性中的设置函数的自定义会被覆盖,你也不会知道你将再也不能重新生成代码了。

幸好,SPMetal生成的类是用partial关键字标记的。那意味着在另一个代码文件中你可以重新声明类,并也可以向新文件中的类添加额外成员。

继续Tasks列表及优先级列中的例子,进行这些步骤:

1. 在有Tasks列表的站点上指出SPMetal,生成你的代码

2. Visual Studio项目中添加一个新类代码文件。删除自动生成的子类声明,也删掉命名空间声明。

补充:由SPMetal生成的代码文件没有声明一个命名空间,这意味着Visual
Studio
项目的默认命名空间是设想的。如果你在两个代码文件中定义了一个分部类,一个并没有把类放到明确的命名空间声明中,那么另一个代码文件也就不能把类放到明确的命名空间声明中,甚至明确声明的默认的命名空间也不行。这似乎是编译器处理分部类的一个怪癖。因此,你必须删除Visual
Studio
自动包括在新类代码文件中的那个明确的命名空间声明。

3. Microsoft.SharePointMicrosoft.SharePoint.Linq添加using语句。

4. 将生成的代码中Task类声明的签名复制到你的新文件中,添加打开及关闭大括号。(不要复制上面类的声明属性)

public partial class
Task : Item {

}

5. 在生成的代码中复制优先级属性支持的字段,然后把它粘贴到新文件的Task类中。如果之前你已经将简单的解决方法(上面提到过)应用到这个项目中了,那么支持子段的声明将会是:

private String
_priority;

否则,它看起来应是:private System.Nullable<Priority> _priority;

6. 用同样的方法从生成的文件中把Priority类声明移到新文件中。这种情况下,你一定要复制声明上的属性。

[Microsoft.SharePoint.Linq.ColumnAttribute(Name = “Priority”, Storage =
“_priority”, FieldType =
“Choice”)]

public System.Nullable<Priority> Priority

{

get {

return this._priority;

}

set {

if ((value != this._priority)) {

this.OnPropertyChanging(“Priority”,
this._priority);

this._priority = value;

this.OnPropertyChanged(“Priority”);

}

}

}

同样,如果你已经把简单的解决方案应用到了这个项目中,那么属性的类型将是字符串类型,而不是System.Nullable<Priority>

7.
如果之前你没有把简单的解决方案应用到该项目,那么就需要在新文件中出现它的地方将System.Nullable<Priority>改为“String”。

8.
在两个文件中你不能声明相同的属性和支持字段。不像类那样,属性不能被标记为“partial”。因此,你要确保它们从生成代码文件中删除了,也不会在SPMetal以后的运行中再次生成。完成那个操作需要创建一个有下面内容的parameters.xml文件。

<?xml
version=”1.0″ encoding=”utf-8″?>

<Web AccessModifier=”Internal” xmlns=”http://schemas.microsoft.com/SharePoint/2009/spmetal“>

<ContentType Name=”Task”
Class=”Task”>

<ExcludeColumn Name=”Priority” />

</ContentType>

<ExcludeContentType Name=”Summary
Task”/>

</Web>

9.
用引用新的parameters.xml文件的命令行重新运行SPMetal。新生成的代码文件将不再包含优先级属性或它的支持字段的声明。

现在需要向选项字段类的设置函数添加你的验证逻辑。请采用下面的步骤继续操作。

1.
向上面的“if’声明的优先级属性添加下面的代码:

using (SPSite siteCollection = new SPSite(—– ????? —–))

{

using (SPWeb website = siteCollection.OpenWeb(—-
????? —–))

{

SPList taskList = website.GetList(this.Path);

SPFieldChoice priorityField
= taskList.Fields[“Priority”] as SPFieldChoice;

if (!priorityField.Choices.Contains(value))

{

throw new ArgumentOutOfRangeException(“value”,
String.Format(“‘{0}’ is not a possible value for
the Choice field ‘Priority’ in the ‘Tasks’ list.”, value));

}

}

}

从底部开始,注意这个代码的后面。如果通过调用代码的值没有在SPFieldChoice对象的选项集合中,那么就会抛出一个异常。从SPList对象的字段集合中获取字段对象的引用和对后者对象的引用,反过来,后者对象的引用通过SPWeb对象的GetList方法和Task对象的Path属性被获取。

前此任何一个都可以做,但是,代码需要引用SPWebSPSite对象。如果代码运行那里有一个HTTP文本环境中和一个非空的SPContext对象,代码也许会从SPContext对象中获得当前的SPSiteSPWeb对象。先让我们试着写些能用在操作台应用程序或其他没有HTTP环境的场景中的代码。

2.
要为SPWeb对象获取一个引用,需要一个针对网站的site-collection-relativeURL你可以通过启动任务对象的Path属性来构建URL以及从它的列表的网站相对URL的末尾做修剪。因此添加下面几行:

String webRelativeListURL
= “/Lists/Tasks”;

String siteRelativeWebURL = this.Path.Remove(this.Path.LengthwebRelativeListURL.Length);

3.
siteRelativeWebURL作为参数插入到OpenWeb方法中。

你需要从父网站集合的绝对URL来获取一个对SPSite对象的引用。这比较困难。Task类的Path属性不包括网站集合的URL的协议或域的一部分。在SPMetal生成的Task类中任何其他成员也没有这条信息。你需要做的是创建Task类的一个新成员,它可以保存这些信息,并当创建Task对象后可以初始化。参考下面的步骤继续:

1.
在你的代码文件(不是生成的代码文件)中,向Task类添加一个内部域的声明。

internal String parentDataContextWebURL;

你可以思考一会儿我们为什么要给它取那个名字。现在需要注意的是将要保存的是构造SPSite所需URL

2.用新域名代替SPSite架构中的参数—–
?????
—–parentDataContextWebURL.

内容类型类有一个由SPMetal生成的构造函数,这是SPMetal行为的一部分,不能被关掉或更改。因此不能自定义这个构造函数来初始化新的内部字段(因为你的自定义在代码重新生成时会被覆盖)。况且在你自己的类的部分定义中创建另一个构造函数不起任何作用。这是因为你的LINQ代码不能直接调用构造列表对象和它里头的项目。而是这些实体被DataContext对象的GetList方法创建。反过来那个方法调用生成的构造函数那个你不能修改的构造函数。

在生成的代码中你会发现内容类型类构造函数没有调用部分方法OnCreated,因此在你自己的分部类定义中可以实现这个方法。不幸的是这个方法不带参数(也没有调用它的构造函数),因此没办法传递你可能在初始化新域所需的数据。(同样,如果有一个HTTP环境,也许我们还可以从那里获得所需的信息,但是我们现在正在试着写能在没有这样的环境也可以运行的选项域验证代码。)

你需要做的是覆写DataContext对象的GetList方法,以使在返回到列表之前它就初始化每个列表对象的新域。可以这样做的原因是SPMetal生成的DataContext派生类也是被标记为Partial,按照下面的步骤继续:

1.
靠近生成的代码文件上部的是DataContext派生类的声明。把那个声明复制到你之前创建的新代码文件,然后添加打开和关闭大括号。这个类的名字是由你作为对SPMetal命令行调用/ code参数值来决定的。

public partial class LinqChoiceFieldExperimentsDataContext : Microsoft.SharePoint.Linq.DataContext
{

}

2.
向类中添加下面DataContext.GetList方法的重载。

public override EntityList<T> GetList<T>(string
listName)

{

EntityList<T> list = base.GetList<T>(listName);

if (typeof(T).Name == “Task”)

{

foreach (T t in list)

{

Task task = t as Task;

task.parentDataContextWebURL = this.Web;

}

}

return list;

}

注意将DataContext对象的Web属性(它保存了对象的绝对URL)传递到列表中每个Task对象的内部子段的方法,反过来,是用你的自定义设置函数来创建一个SPSite对象。顺便说下,这个URL也许是网站集合的一个子站的URL那样不会产生任何问题。SPSite构造函数很聪慧的,它知道你真正想要的是在传递到构造函数中的参数中实体的父SPSite

确实是那样。比如下面的,调用函数将会阻止无效的值写到选项域中。

using (LinqChoiceFieldExperimentsDataContext lCFDC
= new LinqChoiceFieldDataContext(http://Contoso/Marketing/))

{

EntityList<Task> tasks = lCFDC.GetList<Task>(“Tasks”);

tasks.First().Priority = “(4) Trivial”;

lCFDC.SubmitChanges();

}

当然在它的现有版本中,这个代码仅适用于Tasks列表中的优先级列。设想推广它来解决所有列表的选项域。正如我大学数学教科书上说的一样:“这是留给读者的练习”。