用Silverlight调用SharePoint User Profile Web Service

调用SharePoint Web Service本来就不是一件令人愉悦的事情,如果期间在遇到一些诡异的问题的话……譬如我今天遇到的这件 ……

按照惯例,添加好引用,编写代码调用GetUserProfileByNameAsync方法,稍等一下,一个异常抛出了(liao),大概反序列化某个属性时发生了错误。

而这个错误竟然是因为Visual Studio生成的Reference.cs文件和SharePoint提供的WSDL文件映射错误引起的,解决方法就是手工更改Reference.cs文件,该文件的路径是:项目文件夹\Service References\引用名称\Reference.cs。

打开后定位到PropertyData类,我们需要给以下5个属性重新排序,顺序如下:

  1. IsPrivacyChanged
  2. IsValueChanged
  3. Values
  4. Name
  5. Privacy
重新排序的方法就是找到这几个属性,修改它们各自的System.Runtime.Serialization.DataMemberAttribute,修改或增加参数Order,如:
[System.Runtime.Serialization.DataMemberAttribute(IsRequired = true, Order = 1)]
 public bool IsPrivacyChanged { ... }
这一步完成之后,调用GetUserProfileByNameAsync方法不会再抛出异常了,但是拿到的结果中,所有的PropertyData.Values都为null,但其实SharePoint Server已经返回了结果,但这些结果并没有被正确的反序列化出来,好在我们还有办法直接去解析原始的XML结果。
我们首先需要实现一个IEndpointBehavior和IClientMessageInspector:
public class UserProfilesBehavior : IEndpointBehavior
    {
        public UserProfilesInspector Inspector { get; private set; }

        public UserProfilesBehavior()
        {
            this.Inspector = new UserProfilesInspector();
        }

        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        { }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
        {
            clientRuntime.MessageInspectors.Add(Inspector);
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
        { }

        public void Validate(ServiceEndpoint endpoint)
        { }

    }
    public class UserProfilesInspector : IClientMessageInspector
    {
        public string Account { get; private set; }

        XNamespace xmlns = "http://microsoft.com/webservices/SharePointPortalServer/UserProfileService";

        public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
        {
//像这样取出需要的内容
XDocument document = XDocument.Load(new System.IO.StringReader(reply.ToString()));
            this.Account = this.GetValue(document, "AccountName").ToString();
        }

        private object GetValue(XDocument document, string propertyName)
        {
            var node = document.Descendants().FirstOrDefault((element) => { return element.Name == xmlns + "PropertyData" && element.Element(xmlns + "Name").Value == propertyName; });
            if (node != null)
            {
                var valueNode = node.Descendants(xmlns + "Value").FirstOrDefault();
                if (valueNode != null)
                {
                    return valueNode.Value.Replace("_MThumb","_LThumb");
                }
            }
            return string.Empty;
        }

        public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel)
        {
            return null;
        }

    }

然后更改一下调用Web Service的代码,把刚才写的UserProfilesBehavior以Behavior和UserState的形式进去:

UserProfilesBehavior behavior = new UserProfilesBehavior();
SPUserProfile.UserProfileServiceSoapClient userSoap = new SPUserProfile.UserProfileServiceSoapClient(binding, endpoint);
userSoap.Endpoint.Behaviors.Add(behavior);//添加为Behavior
userSoap.GetUserProfileByNameCompleted += new EventHandler<SPUserProfile.GetUserProfileByNameCompletedEventArgs>(userSoap_GetUserProfileByNameCompleted);
userSoap.GetUserProfileByNameAsync(people.Account, behavior);//添加为UserState

那么在GetUserProfileByNameAsync完成时的事件处理程序中,就可以通过UserState——也就是刚才编写的UserProfilesBehavior来拿到需要的数据了:

void userSoap_GetUserProfileByNameCompleted(object sender, SPUserProfile.GetUserProfileByNameCompletedEventArgs e)
{
            if (e.Error == null)
            {
                UserProfilesBehavior behavior = (UserProfilesBehavior)e.UserState;
string account = behavior.Inspector.Account;
            }
}

参考资源:http://social.msdn.microsoft.com/Forums/en-US/sharepointdevelopment/thread/d1c6a143-dc1b-4788-8431-e16f0269f8b4/

Leave a Reply