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通信的常用方式,已經全部演示完畢,也是對自己的學習進行了一次總結並與大家進行分享,希望與大家共同交流,共同探討。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM