上一篇我讲到了如何在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站点内部,那种做法相对简单一些,有两个好处:
看起来很不错吧,但是,如果我们的Silverlight应用程序是独立运行在一个网站,那么即便我们确实修改了网站地址,例如我们的代码做了如下修改
ClientContext ctx = new ClientContext(http://localhost:45223/sites/dev);
但这样却仍然是无法运行的,请看下面的错误
这是什么错误呢?其实一点都不奇怪,如果你以前做过Silverlight开发,调用过外部网站的Web Service的话。这叫跨站访问控制,默认情况下,Silverlight是不可以访问外部服务的。
关于这一点,我之前有一个文章专门介绍,请参考http://www.cnblogs.com/chenxizhang/archive/2010/03/12/1683939.html
那好吧,我们该如何解决这个问题呢?其实很简单,我们可以在当前网站中添加一个服务,让这个服务去访问SharePoint,然后Silverlight访问该服务。这不就可以了吗?
按照这样的思路,这一篇我准备用WCF来实现该服务,并且演示如何在Silverlight中使用它。我们的目的仍然是筛选得到年龄小于60的员工清单。
我们暂时离开Silverlight项目,切换到刚才创建项目时一起创建的一个Web项目中
在这个项目中,我们添加一个WCF服务
【注意】它会增加两个文件,一个是IEmployeeService.cs(如上图所示),一个是EmployeeService.svc(如下图所示)
同时,还会修改web.config文件如下
【注意】这些属于是WCF最基本的知识,如果你对此不清楚,可以参考我有关其他的文章,例如你可以在我的博客中搜索WCF关键字
首先修改服务契约
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
如果能看到下面这样的界面,则表示服务是成功的
然后,我们可以找到一个专门的测试工具,来确认该服务真的是可以运行的。可以在下面找到一个工具
双击左侧的GetEmployees
点击”Invoke”按钮
点击“OK”
到这里为止,我们就确认了该服务确实是可以正常工作的。
首先,我们要在Silverlight应用程序中添加服务引用
点击“OK”之后就可以在程序中生成所谓的客户代理
那么到底如何使用这个服务呢?
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键进行调试了,看看会有什么效果?
好了,现在我们就已经确认了Silverlight程序能够正确调用到WCF的服务。至于下一步,我们就来修改WCF服务中具体的实现,我们让它去访问SharePoint好了。
【注意】通过这个例子,大家应该对面向服务的开发有更深的印象。面向服务的开发,客户端无需关心到底服务是怎么实现的,数据在什么地方。
等等,我们应该用什么方式去访问到SharePoint呢?我们依然可以使用服务器对象模型或者客户端模型。这个例子中我们使用客户端模型,实际上与控制台程序没有什么大的差别
【注意】关于在控制台程序中访问客户端模型,请参考http://www.cnblogs.com/chenxizhang/archive/2010/04/26/1721653.html
修改代码如下
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键进行调试
这样,我们就比较好的解决了Silverlight跨站访问的问题。事实上,我认为虽然可以通过在网站中定义所谓的跨站策略来支持Silverlight访问,但并不见得是非常好的做法。我们通过中间服务的方式来做,能更加易于维护和管理。
有的朋友可能会问啦,我没有看到在Silverlight程序的什么地方指定了服务的地址呀,它是怎么定位服务的呢?
其实是有一个配置文件
在编译好的那个 xap包中也是有这个文件的
也就是说,它其实是读取该配置文件,然后决定要访问哪一个服务的。
那么,如果希望Silverlight程序能够动态地根据自己所在网站位置,来访问服务。而不是硬编码的方式。则可以尝试修改一句代码
大家可以思考一下,为什么要这么修改呢?