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

上一篇我讲到了如何在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; }
    }
}

 

然后修改服务定义

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"}
            };

           
        }
    }
}

【注意】目前我们只是做演示,还没有读取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<localhost.GetEmployeesCompletedEventArgs>(client_GetEmployeesCompleted);
            client.GetEmployeesAsync();
        }

        void client_GetEmployeesCompleted(object sender, localhost.GetEmployeesCompletedEventArgs e)
        {
            Dispatcher.BeginInvoke(() =>
            {
                Employees.ItemsSource = e.Result;
            });

        }
    }
}
【注意】我再次强调,在Silverlight中访问外部的数据或者资源,都是异步的模型。WCF这种场景会自动生成一些方法,来做异步调用。

现在可以按下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 = "<View><Query><Where><Lt><FieldRef Name='Age' /><Value Type='Number'>60</Value></Lt></Where></Query><RowLimit>100</RowLimit></View>";

                ListItemCollection collListItem = list.GetItems(camlQuery);

                ctx.Load(collListItem);
                ctx.ExecuteQuery();

                List<Employee> emps = new List<Employee>();

                foreach (var item in collListItem)
                {
                    emps.Add(new Employee()
                    {
                        FirstName = item["FirstName"].ToString(),
                        LastName = item["LastName"].ToString()
                    });
                }

                return emps.ToArray();
            }

           
        }
    }
}

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

image

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

 

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

其实是有一个配置文件

image

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

image

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

 

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

image

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

posted @ 2010-04-28 12:52  陈希章  阅读(2215)  评论(0编辑  收藏  举报