MOSS 2010:Visual Studio 2010开发体验(18)——在独立的Silverlight应用程序中通过WCF访问SharePoint数据

来源:互联网 发布:西安java讲师 编辑:程序博客网 时间:2024/04/30 13:25

上一篇我讲到了如何在Silverlight中使用客户端对象模型访问SharePoint数据,诸如列表,列表条目,文档之类都是可以的,而且这个对象模型是很完整的,它既可以做数据查询,还可以做操作。简单类比一下就是,原先服务器对象模型能做的,客户端对象模型也大致能做。

有关服务器对象模型,请参考http://www.cnblogs.com/chenxizhang/archive/2010/04/05/1704550.html

有关客户端对象模型,请参考http://www.cnblogs.com/chenxizhang/archive/2010/04/26/1721653.html

 

在上一篇中,我们是将Silverlight作为WebPart使用在SharePoint站点内部,那种做法相对简单一些,有两个好处:

  • 不需要考虑身份验证的问题(事实上,在Microsoft.SharePoint.Client.Silverlight.dll中的ClientContext,根本就不存在什么属性可以让你设置用户凭据Credentials)
  • 不需要考虑网站地址的问题。因为ClientContext有一个Current的属性,就是访问了当前的SharePoint站点的上下文引用。当然这个地址也可以明确指定。

 

看起来很不错吧,但是,如果我们的Silverlight应用程序是独立运行在一个网站,那么即便我们确实修改了网站地址,例如我们的代码做了如下修改

ClientContext ctx = new ClientContext(http://localhost:45223/sites/dev);

但这样却仍然是无法运行的,请看下面的错误

image

这是什么错误呢?其实一点都不奇怪,如果你以前做过Silverlight开发,调用过外部网站的Web Service的话。这叫跨站访问控制,默认情况下,Silverlight是不可以访问外部服务的。

关于这一点,我之前有一个文章专门介绍,请参考http://www.cnblogs.com/chenxizhang/archive/2010/03/12/1683939.html

 

那好吧,我们该如何解决这个问题呢?其实很简单,我们可以在当前网站中添加一个服务,让这个服务去访问SharePoint,然后Silverlight访问该服务。这不就可以了吗?

按照这样的思路,这一篇我准备用WCF来实现该服务,并且演示如何在Silverlight中使用它。我们的目的仍然是筛选得到年龄小于60的员工清单。

 

1. 创建一个独立的Silverlight应用程序

image

image

2. 对页面进行一些必要的设计

image

3. 创建WCF服务

我们暂时离开Silverlight项目,切换到刚才创建项目时一起创建的一个Web项目中

image

在这个项目中,我们添加一个WCF服务

image

image

【注意】它会增加两个文件,一个是IEmployeeService.cs(如上图所示),一个是EmployeeService.svc(如下图所示)

image

同时,还会修改web.config文件如下

image

【注意】这些属于是WCF最基本的知识,如果你对此不清楚,可以参考我有关其他的文章,例如你可以在我的博客中搜索WCF关键字

http://www.google.com.hk/custom?domains=cnblogs.com&q=site:www.cnblogs.com/chenxizhang/+wcf&sitesearch=cnblogs.com&client=pub-4210569241504288&forid=1&channel=5875741252&ie=UTF-8&oe=UTF-8&safe=active&cof=GALT:%23008000;GL:1;DIV:%23336699;VLC:663399;AH:center;BGC:FFFFFF;LBGC:FFFFFF;ALC:0000FF;LC:0000FF;T:000000;GFNT:0000FF;GIMP:0000FF;LH:50;LW:129;L:http://www.cnblogs.com/images/logo_small.gif;S:http://www.cnblogs.com/;FORID:1&hl=zh-CN

 

4. 修改该服务,使其具有一个方法,可以获取员工列表

首先修改服务契约

using System;using System.Collections.Generic;using System.Linq;using System.Runtime.Serialization;using System.ServiceModel;using System.Text;namespace SilverlightApplication6.Web{    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IEmployeeService" in both code and config file together.    [ServiceContract]    public interface IEmployeeService    {        [OperationContract]        void DoWork();        [OperationContract]        Employee[] GetEmployees();    }    [DataContract]    public class Employee    {        [DataMember]        public string FirstName { get; set; }        [DataMember]        public string LastName { get; set; }    }}
.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }

 

然后修改服务定义

using System;using System.Collections.Generic;using System.Linq;using System.Runtime.Serialization;using System.ServiceModel;using System.Text;namespace SilverlightApplication6.Web{    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "EmployeeService" in code, svc and config file together.    public class EmployeeService : IEmployeeService    {        public void DoWork()        {        }        public Employee[] GetEmployees()        {            return new Employee[]{                new Employee(){FirstName="ares",LastName="chen"}            };                   }    }}

.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }【注意】目前我们只是做演示,还没有读取SharePoint

 

5. 测试该服务,选中svc文件,然后右键,在浏览器中查看

image

如果能看到下面这样的界面,则表示服务是成功的

image

然后,我们可以找到一个专门的测试工具,来确认该服务真的是可以运行的。可以在下面找到一个工具

image

image

image

image

双击左侧的GetEmployees

image

点击”Invoke”按钮

image

点击“OK”

image

到这里为止,我们就确认了该服务确实是可以正常工作的。

 

6. 在Silverlight应用程序中使用该服务

首先,我们要在Silverlight应用程序中添加服务引用

image

image

点击“OK”之后就可以在程序中生成所谓的客户代理

image

那么到底如何使用这个服务呢?

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;namespace SilverlightApplication6{    public partial class MainPage : UserControl    {        public MainPage()        {            InitializeComponent();            Loaded += new RoutedEventHandler(MainPage_Loaded);        }        void MainPage_Loaded(object sender, RoutedEventArgs e)        {            localhost.EmployeeServiceClient client = new localhost.EmployeeServiceClient();            client.GetEmployeesCompleted += new EventHandler(client_GetEmployeesCompleted);            client.GetEmployeesAsync();        }        void client_GetEmployeesCompleted(object sender, localhost.GetEmployeesCompletedEventArgs e)        {            Dispatcher.BeginInvoke(() =>            {                Employees.ItemsSource = e.Result;            });        }    }}
【注意】我再次强调,在Silverlight中访问外部的数据或者资源,都是异步的模型。WCF这种场景会自动生成一些方法,来做异步调用。
.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }

现在可以按下F5键进行调试了,看看会有什么效果?

image

好了,现在我们就已经确认了Silverlight程序能够正确调用到WCF的服务。至于下一步,我们就来修改WCF服务中具体的实现,我们让它去访问SharePoint好了。

【注意】通过这个例子,大家应该对面向服务的开发有更深的印象。面向服务的开发,客户端无需关心到底服务是怎么实现的,数据在什么地方。

 

7. 修改服务实现代码

等等,我们应该用什么方式去访问到SharePoint呢?我们依然可以使用服务器对象模型或者客户端模型。这个例子中我们使用客户端模型,实际上与控制台程序没有什么大的差别

【注意】关于在控制台程序中访问客户端模型,请参考http://www.cnblogs.com/chenxizhang/archive/2010/04/26/1721653.html

image

修改代码如下

using System;using System.Collections.Generic;using System.Linq;using System.Runtime.Serialization;using System.ServiceModel;using System.Text;using Microsoft.SharePoint.Client;namespace SilverlightApplication6.Web{    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "EmployeeService" in code, svc and config file together.    public class EmployeeService : IEmployeeService    {        public void DoWork()        {        }        public Employee[] GetEmployees()        {            var url = "http://localhost:45223/sites/dev";            using (ClientContext ctx = new ClientContext(url))            {                ctx.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;                var web = ctx.Web;                ctx.Load(web);                ctx.Load(web.Lists);                ctx.Load(web, w => w.Lists.Where(l => l.Title == "Employees"));                ctx.ExecuteQuery();                List list = web.Lists[0];                CamlQuery camlQuery = new CamlQuery();                camlQuery.ViewXml = "60100";                ListItemCollection collListItem = list.GetItems(camlQuery);                ctx.Load(collListItem);                ctx.ExecuteQuery();                List emps = new List();                foreach (var item in collListItem)                {                    emps.Add(new Employee()                    {                        FirstName = item["FirstName"].ToString(),                        LastName = item["LastName"].ToString()                    });                }                return emps.ToArray();            }                   }    }}

.csharpcode, .csharpcode pre{font-size: small;color: black;font-family: consolas, "Courier New", courier, monospace;background-color: #ffffff;/*white-space: pre;*/}.csharpcode pre { margin: 0em; }.csharpcode .rem { color: #008000; }.csharpcode .kwrd { color: #0000ff; }.csharpcode .str { color: #006080; }.csharpcode .op { color: #0000c0; }.csharpcode .preproc { color: #cc6633; }.csharpcode .asp { background-color: #ffff00; }.csharpcode .html { color: #800000; }.csharpcode .attr { color: #ff0000; }.csharpcode .alt {background-color: #f4f4f4;width: 100%;margin: 0em;}.csharpcode .lnum { color: #606060; }

然后,我们再次按下F5键进行调试

image

这样,我们就比较好的解决了Silverlight跨站访问的问题。事实上,我认为虽然可以通过在网站中定义所谓的跨站策略来支持Silverlight访问,但并不见得是非常好的做法。我们通过中间服务的方式来做,能更加易于维护和管理。

 

有的朋友可能会问啦,我没有看到在Silverlight程序的什么地方指定了服务的地址呀,它是怎么定位服务的呢?

其实是有一个配置文件

image

在编译好的那个 xap包中也是有这个文件的

image

也就是说,它其实是读取该配置文件,然后决定要访问哪一个服务的。

 

那么,如果希望Silverlight程序能够动态地根据自己所在网站位置,来访问服务。而不是硬编码的方式。则可以尝试修改一句代码

image

大家可以思考一下,为什么要这么修改呢?

原创粉丝点击