Silverlight与WCF通信(四) :Silverlight访问控制台宿主WCF

    今日更新本系列最后一篇,Silverlight访问控制台宿主WCF,我们分别介绍tcp信道和http信道两种方式。无论用IIS宿主还是控制台等程序宿主,对于WCF服务代码没有任何区别,但由于是Silverlight来进行调用,就会面临一个跨域的问题,我们今天也主要是来演示如何解决这个问题的,其思路就是在WCF端增加一个跨域服务,当客户端Silverlight调用的时候,Silverlight就会通过跨域服务得到跨域文件,这个方法也是之前在网上查找了大量的资料后,学习到的,在此记下并与大家分享。

   首先,我们要知道跨域文件放到什么地方?前几篇IIS宿主的时候已经讲过,如果通过http绑定方式访问,跨域文件则需要放到http协议指定的端口下面(例如http://localhost:8080,则应该放到8080端口网站下),而通过net.Tcp绑定的时候,跨域文件应该放到80端口的网站下面,这是Silverlight的官方解释。好了话不多说,看代码,同样先展示一下项目结构。

项目结构

结构和以前一样,四层结构:

类库 说明 项目引用
LxContracts 数据契约及操作契约

System.Runtime.Serialization

System.ServiceModel

System.ServiceModel.Web
LxSerivces WCF服务的实现

LxContracts [项目]

System.Runtime.Serialization

System.ServiceModel
LxWcfHost 控制台程序宿主WCF,需要修改Framework版本为.Net Framework 4.0

LxContracts [项目]

LxSerivces [项目]

System.ServiceModel
SilverlightClient Silverlight客户端进行服务的调用  

 

 

 

 

 

 

 

 

 

 

 

 

 

代码实现:

类库LxContracts(包括数据契约student.cs  操作契约IStudent.cs 和 跨域契约IDomain.cs

Student.cs 代码
using System.Runtime.Serialization;

namespace LxContracts
{
    [DataContract]
    public class Student
    {
        /// <summary>
        /// 学生编号
        /// </summary>
        [DataMember]
        public int StuId { get; set; }

        /// <summary>
        /// 学生姓名
        /// </summary>
        [DataMember]
        public string StuName { get; set; }

        /// <summary>
        /// 所在班级
        /// </summary>
        [DataMember]
        public string ClassName { get; set; }

        /// <summary>
        /// 联系电话
        /// </summary>
        [DataMember]
        public string TelPhoneNum { get; set; }
    }
}
IStudent.cs 代码
using System.ServiceModel;
using System.Collections.Generic;

namespace LxContracts
{
    [ServiceContract]
    public interface IStudent
    {
        [OperationContract]
        List<Student> GetStudent();
    }
}

 关键是这个跨域契约 

IDomain.cs
using System.ServiceModel;
using System.Runtime.Serialization;
using System.ServiceModel.Channels;
using System.ServiceModel.Web;

namespace LxContracts
{
    [ServiceContract]
    public interface IDomain
    {
        [OperationContract]
        [WebGet(UriTemplate = "ClientAccessPolicy.xml")]
        Message ProvidePolicyFile();
    }
}

类库LxServicers(集合类StudentList.cs  服务类StudentService.cs 和 契约服务DomainService.cs

StudentList.cs
using LxContracts;
using System.Collections.Generic;

namespace LxServices
{
    public class StudentList : List<Student>
    {
        public StudentList()
        {
            this.Add(new Student() { StuId = 1, StuName = "小明", ClassName = "计算机一班", TelPhoneNum = "123456" });
            this.Add(new Student() { StuId = 2, StuName = "小红", ClassName = "计算机二班", TelPhoneNum = "234567" });
            this.Add(new Student() { StuId = 2, StuName = "小兰", ClassName = "计算机三班", TelPhoneNum = "890123" });
        }
    }
}
StudentService.cs
using LxContracts;
using System.Collections.Generic;

namespace LxServices
{
    public class StudentService : IStudent
    {
        
        public List<Student> GetStudent()
        {
            //实际情况应该为从数据库读取
            //本例手动生成一个StudentList
            StudentList ListStuent = new StudentList();
            return ListStuent;
        }
    }
}

 关键是这个实现跨域服务

DomainService.cs 代码
using LxContracts;
using System.IO;
using System.Xml;
using System.ServiceModel.Channels;

namespace LxServices
{
    public class DomainService : IDomain
    {
        public System.ServiceModel.Channels.Message ProvidePolicyFile()
        {
            MemoryStream ms = new MemoryStream();
            using (FileStream fs = File.OpenRead(@"clientaccesspolicy.xml"))
            {
                int length = (int)fs.Length;
                byte[] data = new byte[length];
                fs.Position = 0;
                fs.Read(data, 0, length);
                ms = new MemoryStream(data);
            }
            XmlReader reader = XmlReader.Create(ms);
            Message result = Message.CreateMessage(MessageVersion.None, "", reader);
            return result;
        }
    }
}

OK,服务代码已经编写好了,我们来进行控制台宿主,我们用配置文件先演示http方式宿主

宿主控制台LxWcfHost(我们为该控制台添加一App.config 文件 和 clientaccesspolicy.xml 跨域文件,并把跨域文件的属性中的“复制到输出目录” 设置成为 “始终复制”)

此跨域文件把两种跨域文件进行了合并,可以同时支持http和Tcp 

clientaccesspolicy.xml 代码
<?xml version="1.0" encoding="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from http-request-headers="*">
        <domain uri="*" />
      </allow-from>
      <grant-to>
        <socket-resource port="4502-4534" protocol="tcp" />
        <resource path="/" include-subpaths="true"/>
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>
Program.cs 控制台代码
using System;
using System.ServiceModel;

namespace LxWcfHost
{
    class Program
    {
        static void Main(string[] args)
        {  
            ServiceHost host = new ServiceHost(typeof(LxServices.StudentService));
            ServiceHost crossDomainserviceHost = new ServiceHost(typeof(LxServices.DomainService));
            host.Opened += delegate
            {
                Console.WriteLine("服务已经启动,按任意键终止...");
            };

            crossDomainserviceHost.Opened += delegate
            {
                Console.WriteLine("跨域服务已经启动,按任意键终止...");
            };
            crossDomainserviceHost.Open();
            host.Open();
            Console.ReadKey();
            host.Close();
            host.Abort();
            crossDomainserviceHost.Close();
            crossDomainserviceHost.Abort();
        }
    }
}

 使用Http绑定方式的App.config文件的配置如下:

App.config Http方式配置代码
<?xml version="1.0"?>
<configuration>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="LxBehavior">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="DomainServiceBehavior">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
    </behaviors>

    <services>
      <service name="LxServices.StudentService" behaviorConfiguration="LxBehavior">
        <endpoint address="" binding="basicHttpBinding" contract="LxContracts.IStudent" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:9090/StudentSrv" />
          </baseAddresses>
        </host>
      </service>
      <service name="LxServices.DomainService">
        <endpoint address="" behaviorConfiguration="DomainServiceBehavior"
            binding="webHttpBinding" contract="LxContracts.IDomain" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:9090/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>
</configuration>

注意:配置文件中的跨域服务的基地址是和WCF服务的基地址端口是一样的都是9090 

至此,我们的控制台宿主程序和WCF服务代码都已经完成,我们来启动一下服务:

SilverlightClient 客户端代码:

MainPage.xaml 代码
<UserControl xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" 
             x:Class="SilverlightClient.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">

    <Grid x:Name="LayoutRoot" Background="White">
        <sdk:DataGrid x:Name="dgStudnet" Grid.Row="0" AutoGenerateColumns="False">
            <sdk:DataGrid.Columns>
                <sdk:DataGridTextColumn Header="学生编号" Width="80" Binding="{Binding StuId}" />
                <sdk:DataGridTextColumn Header="学生姓名" Width="100" Binding="{Binding StuName}" />
                <sdk:DataGridTextColumn Header="所在班级" Width="120" Binding="{Binding ClassName}" />
                <sdk:DataGridTextColumn Header="电话号码" Width="100" Binding="{Binding TelPhoneNum}" />
            </sdk:DataGrid.Columns>
        </sdk:DataGrid>
    </Grid>
</UserControl>

 我们为该Silverlight客户添加服务引用输入:http://localhost:9090/StudentSrv

 

MainPage.xaml.cs 代码
using System;
using System.Windows.Controls;
using System.Collections.ObjectModel;
using SilverlightClient.WCF.StudentSrv;

namespace SilverlightClient
{
    public partial class MainPage : UserControl
    {
        ObservableCollection<Student> listStudent;
        public MainPage()
        {
            InitializeComponent();
            listStudent = new ObservableCollection<Student>();
            this.Loaded += new System.Windows.RoutedEventHandler(MainPage_Loaded);
        }
        void MainPage_Loaded(object sender, System.Windows.RoutedEventArgs e)
        {
            StudentClient proxyClient = new StudentClient();
            proxyClient.GetStudentCompleted += new EventHandler<GetStudentCompletedEventArgs>(proxyClient_GetStudentCompleted);
            proxyClient.GetStudentAsync();
        }

        void proxyClient_GetStudentCompleted(object sender, GetStudentCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                listStudent = e.Result;
                this.dgStudnet.ItemsSource = listStudent;
            }
        }
    }
}

 OK,我们来调试一下,Silverlight客户端进行一下调用WCF服务,结果如下图:

Http绑定方式已经可以成功调用了,我们如何使用TCP方式呢,很简单,我们只要修改一下App.config文件即可完成,如下:

App.config Tcp方式配置代码
<?xml version="1.0"?>
<configuration>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="LxBehavior">
          <serviceMetadata httpGetEnabled="false"/>
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="DomainServiceBehavior">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <bindings>
      <netTcpBinding>
        <binding name="LxBinding">
          <security mode="None"/>
        </binding>
      </netTcpBinding>
    </bindings>
    <services>
      <service name="LxServices.StudentService" behaviorConfiguration="LxBehavior">
        <endpoint address="StudentService" binding="netTcpBinding" bindingConfiguration="LxBinding" contract="LxContracts.IStudent"/>
        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
        <host>
          <baseAddresses>
            <add baseAddress="net.tcp://localhost:4505/"/>
          </baseAddresses>
        </host>
      </service>

      <service name="LxServices.DomainService">
        <endpoint address="" behaviorConfiguration="DomainServiceBehavior"
            binding="webHttpBinding" contract="LxContracts.IDomain" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:80/" />
          </baseAddresses>
        </host>
      </service>
      
    </services>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
  </system.serviceModel>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>

 注意:配置文件中的跨域服务的基地址已经变成了 http://localhost:80 端口了

我们将先前的Silverlight客户端服务引用重新配置或者删除后重新添加:

然后,再次运行Silverlight客户端程序,也会得到相应的结果。

  到此为止,Silverlight与WCF通信的常用方式,已经全部演示完毕,也是对自己的学习进行了一次总结并与大家进行分享,希望与大家共同交流,共同探讨。

posted @ 2012-08-02 17:18  Rising_Sun  阅读(2995)  评论(11编辑  收藏  举报