如何基于OM模型使用C#在程序中给SharePoint的BCS外部数据类型的字段赋值
概述:
外部内容类型和数据,SharePoint从2010这个版本开始就对BCS提供非常强大的支持,点点鼠标就可以取代以前直接编辑XML的方式来设置SharePoint到SQL数据库的连接。非常方便地在SharePoint中修改删除SQL数据库的数据,并且SharePoint还提供了字段级的支持,您可以在任何List当中插入一个External Data外部数据字段,用来引用SQL表中的某一行,并显示指定字段的值。 我们经常要用到程序去自动滴修改值,这个类型栏的值,是可以直接通过“Iiem[Field]=Value”这个语句简简单单地把它更改了的嘛?答案是否定的,如果要改这个BCS类型的值,您可能需要十几行代码,我们慢慢给您开讲。
此时,您有没有觉得SharePoint就像一个万能插头一样,什么样的“数据”,都能插得进来呢? “公牛牌!” ,Oh no... 是 “母牛牌” ,你懂的!
准备工作
在SharePoint 2010 和 SharePoint 2013 当中我们可以通过SharePoinit Desinger很方便地设置一个基于SQL SERVER的或是其它数据源的,“外部内容类型”,这样我们就可以在SharePoint 查询、修改、添加这个数据表,如下图所示:
本教程不是初级入门教程,如果各位小学生不懂如何进行BCS的基本操作,那么我们的大神门在度娘上已经留下了大量的痕迹,尽情地去Search(色)吧。
假设,现在我们已经有了如下的其于BCS的字段叫“name”,如下图,它是从一个叫“dept stuff” 这个外部类型当中读取数据的,我们还勾选了的相关的 “部门、移动号码”,作为显示字段。
重要概念
要引用如下的3个DLL文件,
,
我的using 是这样的:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.SharePoint; using System.Xml; using Microsoft.SharePoint.BusinessData; using Microsoft.SharePoint.Administration; using Microsoft.SharePoint.BusinessData.SharedService; using System.Data; using Microsoft.BusinessData.Runtime; using Microsoft.BusinessData.MetadataModel; using Microsoft.BusinessData.MetadataModel.Collections; using System.Web; using Microsoft.SharePoint.BusinessData.Infrastructure; using System.Globalization;
1. 存了什么:Identity
其实就是数据库中作为“标识”和“主键”的那一列,在创建SharePoint的Entity(即实体)时,一定要把这个Identity选出来,这是后面进行编程的基础,如果不设定这个的话,只能“呵呵”你了,因为我也不知道会发生什么。
如下图,大家可以偷窥到,我们2个优秀的员工,包XX和蒋XX,所在的部门和移动号码,千万别认为你们所见的,就是在LIST所实际存储的,在External Data类型的字段中,除了Identity什么也不实际存储,那她们的“名字、部门、电话”,这些值是什么呢?
是缓存!Cache! (此处省略1000字原理),记住包括您建的这个叫name的栏,存储的也是Cache,所以当你天真的使用
item["name"]="奥巴马"
来赋值后,你会发现name:部门和name:移动号码,根本没有改变,不是因为我们没有员工叫 “奥巴马” ,而是Identity的问题,所以我们给这种类型字段赋新值,值并不重要,你必须要知道新值在数据库表中的Identity那列的值!
2. 在哪存的:SPField.RelatedField
那你要问了,这个Identity,既然不存储在所建的那个External Field里,那存储在哪里呢,YES! 就是在这个RelatedField里,在微软网站上,关于这个属性的说明,是如此简单和无节操,所以我们取Identity数据的时候要使用这样的语句,同样赋值也是:
string entityId= listItem[dataField.RelatedField].ToString();
3. 怎么加密存的:EntityInstanceIdEncoder
天哪,知道存了什么,存在哪里,读出来却是乱码一片,经过大神Dosboy的研究发现了,这个Identity,竟然是加密码保存的,用得就是这个加密的方法。这是要把人整死的节奏嘛?
测试看看是不是加密:EntityInstanceIdEncoder.IsEncodedIdentifier
用来解密:EntityInstanceIdEncoder.DecodeEntityInstanceId
所以读取Identity的语句应该是这样子的:
1 SPBusinessDataField dataField = listItem.Fields["name"] as SPBusinessDataField; 2 string entityId= listItem[dataField.RelatedField].ToString(); 3 Identity id =null; 4 if (EntityInstanceIdEncoder.IsEncodedIdentifier(entityId)) 5 { 6 object[] oIDList = EntityInstanceIdEncoder.DecodeEntityInstanceId(entityId); 7 id = new Identity(oIDList[0]); 8 9 10 } 11 else 12 id = new Identity(entityId);
取出来的变量id,就是Identity类型的。如果要显示出来,就用这样的:
Console.WriteLine(id.GetIdentifierValues()[0].ToString ());
搞清楚外部数据External Data相关的存储的情况以后,我们就可以赋值的操作了
给External Data栏赋值的过程
1. 搞清楚你要赋的值在源数据库表中那个"主键"的栏的值是多少?
有些人就说了,兄弟我只知道新值,比如本例叫“本.拉灯”,那怎么办,对不起,请在程序中编程查询ID的值,比如“本.拉灯”的员工号(一定很排前)。这是前提,否则不是违背了External Data的原本意义了嘛,就是通过让你选一行数据,然后显示数据库中的值。
2. 给 RelatedField 赋 Identity的值,并根据Identity的值找到External Entity中的新值的那行:
public static IEntityInstance GetEntityInstance(SPBusinessDataField dataField, string entityId, SPSite site, SPListItem item) { SPServiceContext context = SPServiceContext.GetContext(site); IMetadataCatalog catalog = null; BdcService bdcService = SPFarm.Local.Services.GetValue<BdcService>(String.Empty); if (null != bdcService) { catalog = bdcService.GetDatabaseBackedMetadataCatalog(context); } IEntity entity = catalog.GetEntity(dataField.EntityNamespace, dataField.EntityName); ILobSystem lobSystem = entity.GetLobSystem(); ILobSystemInstance LobSysteminstance = lobSystem.GetLobSystemInstances()[dataField.SystemInstanceName]; IEntityInstance entInstance = null; // Get methods collection foreach (KeyValuePair<string, IMethod> method in entity.GetMethods()) { // Get current method’s instance IMethodInstance methodInstance = method.Value.GetMethodInstances()[method.Key]; // Execute specific finder method. if (methodInstance.MethodInstanceType == MethodInstanceType.SpecificFinder) { Identity id = null; if (EntityInstanceIdEncoder.IsEncodedIdentifier(entityId)) { object[] oIDList = EntityInstanceIdEncoder.DecodeEntityInstanceId(entityId); id = new Identity(oIDList[0]); // Execute specific finder method and get the entity instance entInstance = entity.FindSpecific(id, entity.GetLobSystem().GetLobSystemInstances()[0].Value); item[dataField.RelatedField] = entityId.ToString(); } else { object oID = GetTypedIDValue(entityId, entity); id = new Identity(oID); string encodedIdentifier = EntityInstanceIdEncoder.EncodeEntityInstanceId(new object[] { oID }); // Execute specific finder method and get the entity instance // Console.WriteLine(entity.Name + ":" + entity.GetLobSystem().GetLobSystemInstances()[0].Value.Name); entInstance = entity.FindSpecific(id, entity.GetLobSystem().GetLobSystemInstances()[0].Value); item[dataField.RelatedField] = encodedIdentifier; } } } return entInstance; } public static object GetTypedIDValue(string sID, IEntity oEntity) { IIdentifierCollection type = oEntity.GetIdentifiers(); String sIdentifierType = type[0].IdentifierType.FullName.ToLower().Replace("system.", String.Empty); object oID = null; //find the instance value based on the given identifier type switch (sIdentifierType) { case "string": oID = sID; break; case "datetime": oID = DateTime.Parse(sID, CultureInfo.CurrentCulture); break; case "boolean": oID = Boolean.Parse(sID); break; case "int64": oID = Int64.Parse(sID); break; case "int32": oID = Int32.Parse(sID); break; case "int16": oID = Int16.Parse(sID); break; case "double": oID = Double.Parse(sID); break; case "char": oID = Char.Parse(sID); break; case "guid": oID = new Guid(sID); break; default: oID = sID; break; } return oID; }
以上的代码涉及很多关于BCS很多很多的概念,什么实例什么实体,我只想说,你就复制、粘贴吧,你唯一要搞清楚的就是2件事件:
- 列表的Externl Data(就是外部类型)的栏的名字,还有你网站的URL是什么。
- 新值,的Identity是什么。
3. 给其它显示的栏 赋 显示的值
第2步做完后,就要给其它栏,本例是名字(对的也属于其它栏)、手机、部门,赋显示的值哦,用这个代码:
public static void SetSecondaryFields(SPListItem listItem, SPBusinessDataField dataField, IEntityInstance entityInstance) { // Convert the entity to a formatted datatable DataTable dtBDCData = entityInstance.EntityAsFormattedDataTable; // Set the BCS field itself (Display Value) listItem[dataField.Id] = dtBDCData.Rows[0][dataField.BdcFieldName].ToString(); // Get the specific finder method to get the columns that returns IMethodInstance method = entityInstance.Entity.GetMethodInstances(MethodInstanceType.SpecificFinder)[0].Value; ITypeDescriptorCollection oDescriptors = method.GetReturnTypeDescriptor().GetChildTypeDescriptors()[0].GetChildTypeDescriptors(); // Set the column names to the correct values foreach (ITypeDescriptor oType in oDescriptors) { if (oType.ContainsLocalizedDisplayName()) { if (dtBDCData.Columns.Contains(oType.Name)) { dtBDCData.Columns[oType.Name].ColumnName = oType.GetLocalizedDisplayName(); } } } // get the secondary field display names; these should be set string[] sSecondaryFieldsDisplayNames = dataField.GetSecondaryFieldsNames(); // loop through the fields and set each column to its value foreach (string columnNameint in sSecondaryFieldsDisplayNames) { Guid gFieldID = listItem.Fields[String.Format("{0}: {1}", dataField.Title, columnNameint)].Id; listItem[gFieldID] = dtBDCData.Rows[0][columnNameint].ToString(); } listItem.Update(); } }
以上还是涉及涉及很多概念,不用管,直接,复制、粘贴这个函数就行。
4. 示例如何在命令行程序中使用如上的代码:
把上面3个函数,把在一个静态的类中,我们叫myF,然后在Main函数中,写上如下的代码,这个代码是做这样的事情的.
这个网站集的URL是: http://r720/sites/demo
列表的名称是:External Field
外部数据列叫:name (附带显示:部门、移动号码2列)
我现在要把所有的行,的name栏都换成比刚才2位优秀员工更优秀的员工:本拉灯(他的代号200),代码如下,这个你不能复制粘贴,要换成自己的网站的情况:)
using (SPSite spSite = new SPSite("http://r720/sites/demo")) { using (SPWeb spWeb = spSite.RootWeb) { SPList list = spWeb.Lists["External Field"]; foreach (SPListItem listItem in list.Items) { SPBusinessDataField dataField = listItem.Fields["name"] as SPBusinessDataField; IEntityInstance entityinst = myF.GetEntityInstance(dataField, "200",new SPSite("http://r720/sites/demo"), listItem); myF.SetSecondaryFields(listItem, dataField, entityinst); } } } Console.ReadLine();
故事的核心就这么2个语句:
IEntityInstance entityinst = GetEntityInstance(【外部字段】, 【新值ID】,【网站集】,【当前列表项】); SetSecondaryFields(【当前列表项】,【外部字段】,entityinst);
我们再看看效果吧,(千万别打电话哦,有美国人监听呢:))