Silverlight中数据访问的实现方式非常之多,可以直接通过HTTP页面提供访问接口,也可以通过WebService、WCF以及其他的相关技术来实现。在MIX 09上,Nikhil Kothari发布了微软的一神作——Microsoft .NET RIA Services,主要用来提供RIA应用的数据访问服务,详细可以查阅博友 流牛木马 的《Microsoft .NET RIA Services快速上手 》这篇文章。在开源项目FluorineFx中也提供了供Silverlight实现RPC的类库,究竟谁的传输效率高呢这里暂不讨论,本文作重介绍
如何使用FluorineFx Silverlight库去实现Silverlight远程过程调用(RPC)。
本文实例程序开发环境:Microsoft Visual Studio 2008 + SP1、.net framework 3.5 + SP1、 Silverlight 3 Beta、FluorineFx v1.0.0.15 。本文最终的项目解决方案如下图:
一、在Silverlight中的网络安全访问限制
如果连接请求是从 WebClient 或 HTTP 类到某个跨域站点的,则 Silverlight 2 运行时将使用 HTTP 协议尝试下载安全策略文件。Silverlight 2 运行时首先尝试使用 HTTP 协议下载所请求目标域的根目录下名为“clientaccesspolicy.xml”的 Silverlight 策略文件。如果返回 Silverlight 策略文件(即使在分析该文件时出现错误),则在 Silverlight 应用程序的整个会话期间,此文件将用作该跨域请求以及针对该服务器的所有后续请求的策略文件。如果找不到 Silverlight 策略文件,则 Silverlight 2 运行时尝试使用 HTTP 协议下载所请求目标域的根目录下名为“crossdomain.xml”的 Flash 策略。[载自MSDN]
<?xml version="1.0" encoding="utf-8"?>
<access-policy>
<cross-domain-access>
<allow-from http-request-headers="*">
<domain uri="*"/>"
</allow-from>
<grant-to>
<resource path="/" include-subpaths="true"/>
</grant-to>
</cross-domain-access>
</access-policy>
二、基与FluorineFx的远程调用服务接口
接下来创建FluorineFx类库,提供远程访问服务接口(RemotingService),并提供相应的方法让Sliverlight客户端通过FluorineFx通信网关实行RPC调用。
namespace ServiceLibrary
{
[RemotingService]
public class DataService
{
public DataService()
{
}
[DataTableType("Book", "ServiceLibrary.Book")]
public DataTable GetBookTable()
{
DataTable dt = new DataTable("Book");
dt.Columns.Add("ID", typeof(int));
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("Author", typeof(string));
dt.Columns.Add("Price", typeof(double));
DataRow dr = dt.NewRow();
dr["ID"] = 1;
dr["Name"] = "《三国演义》";
dr["Author"] = "罗贯中";
dr["Price"] = 100.00;
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["ID"] = 2;
dr["Name"] = "《西游记》";
dr["Author"] = "吴承恩";
dr["Price"] = 200.00;
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["ID"] = 3;
dr["Name"] = "《水浒传》";
dr["Author"] = "施耐庵";
dr["Price"] = 300.00;
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["ID"] = 4;
dr["Name"] = "《红楼梦》";
dr["Author"] = "曹雪芹";
dr["Price"] = 400.00;
dt.Rows.Add(dr);
return dt;
}
}
}
三、使用FluorineFx网站宿主FluorineFx远程服务
供远程访问(RemotingService)接口完成后,还需要建立一个FluorineFx网站,用来承载这个接口的远程服务。并同时配置AMF通信的信道(在services-config.xml中配置)。
<channels>
<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
<endpoint uri="http://{server.name}:{server.port}/{context.root}/Gateway.aspx" class="flex.messaging.endpoints.AMFEndpoint"/>
<properties>
</properties>
</channel-definition>
</channels>
然后运行这个网站以得到客户端RPC访问时所需要的网关地址,这里是本地测试。如果部署到服务器上则根据实际的部署信息(服务器IP/网站域名等)访问。通过FluorineFx控制台测试便可以看到FluorineFx远程服务接口方法的调用情况,如下图示:
四、创建Silverlight应用程序
现在创建一个Silverlight 应用程序,并为此应用程序创建一个宿主这个应用程序客户端的Web应用程序,这里的宿主Web应用程序就直接创建在上面提供远程服务接口的FluorineFx网站中,当然你也可以创建新的Web应用程序来宿主Silverlight应用程序客户端。
五、通过FluorineFx的Silverlight库实现Silverlight应用的RPC
这里首先定义一个DTO(数据传输对象)对象Book,如下:
namespace SilverlightApp
{
public class Book
{
public int ID { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public double Price { get; set; }
public Book()
{ }
}
}
在Siverlight应用中,在舞台上布局一个按扭,用来发起远程调用。使用DataGrid控件来显示调用结果。
<UserControl x:Class="SilverlightApp.MainPage"
xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300">
<Canvas Background="DarkBlue">
<Button Content="RPC调用" Width="80" Height="30"
Canvas.Top="10" Canvas.Left="10"
Background="Blue" Click="Button_Click">
</Button>
<data:DataGrid Canvas.Top="60" Canvas.Left="10" RowHeight="30"
x:Name="bookGrid" AutoGenerateColumns="True">
</data:DataGrid>
</Canvas>
</UserControl>
现在这一步很关键,要使Siverlight能够成功实现RPC,需要使用FluorineFx所提供的Siverlight库,不是使用FluorineFx for .NET framework,正确的是使用FluorineFx for Siverlight库,此库位于FluorineFx的安装目录x:\FluorineFx\Bin\net\Silverlight2.0\FluorineFx.dll。库类封装了Siverlight实现RPC的相关API。
NetConnection nc;
public MainPage()
{
InitializeComponent();
//初始化到FluorineFx网关的连接
nc = new NetConnection();
nc.ObjectEncoding = ObjectEncoding.AMF3;
nc.NetStatus += new NetStatusHandler(onNetStatus);
nc.Connect("http://localhost:3103/WebHost/Gateway.aspx");
nc.Client = this;
}
在Siverlight应用程序初始化的时候对远程访问连接进行初始化,并为其委托一个网络连接状态处理函数onNetStatus。
void onNetStatus(object sender, NetStatusEventArgs e)
{
string code = e.Info["code"] as string;
}
和Flex开发一样,同样使用NetConnection的call()方法去调用远程服务方法,不同的是Siverlight中没有Resonder去处理方法的返回结果,而是需要自己定义一个回调处理程序去处理返回结果,此处理程序必须实现接口IPendingServiceCallback,详细如下:
private void Button_Click(object sender, RoutedEventArgs e)
{
nc.Call("ServiceLibrary.DataService.GetBookTable", new RpcResultHandler(this));
}
public void Bind(IList<Book> list)
{
Dispatcher.BeginInvoke(delegate()
{
this.bookGrid.ItemsSource = list;
});
}
namespace SilverlightApp
{
public class RpcResultHandler : IPendingServiceCallback
{
MainPage page;
public RpcResultHandler(MainPage ctl)
{
page = ctl;
}
#region IPendingServiceCallback Members
public void ResultReceived(IPendingServiceCall call)
{
object result = call.Result;
ArrayCollection items = result as ArrayCollection;
IList<Book> list = new List<Book>();
foreach (var item in items)
{
IDictionary<string, object> dic = (((FluorineFx.ASObject)(item))) as IDictionary<string, object>;
string Name = dic["Name"].ToString();
Book book = new Book
{
ID = int.Parse(dic["ID"].ToString()),
Name = dic["Name"].ToString(),
Author = dic["Author"].ToString(),
Price = double.Parse(dic["Price"].ToString())
};
list.Add(book);
}
page.Bind(list);
}
#endregion
}
}
IPendingServiceCallback接口中的ResultReceived()方法专门用来处理RPC调用的结果,这里将返回结果通过相应的解析处理后回调Siverlight应用中的Bind()方法,实现将返回的结果绑定到Siverlight控件DataGrid上。程序运行截图: