码迷,mamicode.com
首页 > Windows程序 > 详细

c#进阶(5)—— WCF 实现简单预订功能

时间:2017-12-10 15:18:34      阅读:263      评论:0      收藏:0      [点我收藏+]

标签:on()   oda   pil   代理类   cts   3.4   分享   文件   div   

1、WCF概述
WCF全称为Windows Communication Foundation,在.Net 3.0 中引入,用于客户端与服务端通信,替换了之前的一些技术,如.Net Remoting 及 WSE。
WCF 相比ASP.NET Web API 复杂,但提供了更多的功能,如
(1)、可靠性
(2)、事务
(3)、Web服务安全
如果不需要这些先进的通信功能,ASP.NET Web API是更好的选择。
2、WCF 主要功能
(1)、存储组件和服务,可以将WCF服务存放在ASP.NET 运行库、Windows服务、COM+进程或WPF应用程序中,进行对等计算
(2)、声明行为,不需要继承基类,可以使用属性定义服务。
(3)、通信信道,WCF提供了用HTTP、TCP、IPC信道进行通信的多条信道,支持自定义信道。
(4)、安全结构,为实现独立于平台的WEB服务,必须使用标准化的安全环境,标准用WSE3.0实现。
(5)、可扩展性,支持将功能注入客户端和服务端的消息流。
(6)总结
!最终目标:通过进程或不同系统,通过本地网络或Internet收发客户端和服务之间的消息,如果需要以独立于平台的方式尽快收发消息,就应该使用WCF。
!远距离视图
服务提供了一个端点,由协定、绑定、地址描述。
a:协定,定义了服务提供的操作
b:绑定,给出了协议和编码信息
c:地址,是服务的位置,客户端需要一个兼容的端点来进行访问。
3、WCF 组件及步骤解读
 技术分享图片
步骤解读:
(1)、客户端,客户端调用代理的一个方法
(2)、代理,代理将方法调用转化为一条消息,并把该消息传输到信道上。
(3)、信道,包含客户端部分及服务端部分,他们通过网络协议进行通信,在信道上,把消息传递给调度程序。
(4)、调度程序,将消息转换为服务调用的方法调用。
4、WCF 其他重要技术
(1)、SOAP协议
服务从客户端接收SOAP消息,并返回一条SOAP响应消息,SOAP消息包含信封,信封包含标题和正文。
(2)、WSDL
WSDL文档描述了服务的操作和消息,WSDL定义了服务的元数据,这些元数据用于为客户端应用程序创建代理。
WSDL包含如下信息:
a:消息的类型——用XML架构描述
b:从服务中收发的信息——消息的各部分用XML架构定义的类型
c:端口类型——映射服务协定,列出用服务协定定义的操作,操作包含信息,如与请求和响应序列一起使用的输入和输出消息
d:绑定信息——包含用端口类型列出的操作,并定义使用的SOAP变体
e:服务信息——把端口类型映射到端点地址
5、WCF实战——预定会议室
5.1 前言
业务场景,存储会议室预订信息,应用mysql数据库中的roomreservations表存储预订信息,主要实施步骤包括
(1)创建服务和数据协定
(2)使用 自定义类库 访问数据库
(3)实现服务
(4)使用WCF服务宿主(Service Host)和WCF测试客户端(Test Client)
(5)创建定制的服务宿主
(6)使用元数据创建客户应用程序
(7)使用共享的协定创建客户应用程序
(8)配置诊断设置
5.2 具体实现
(1)创建数据协定和服务协定
  整体结构:
技术分享图片
1.1 创建数据协定
创建RoomReservationContracts类库,新建RoomReservation类,该类主要包含数据协定,具体属性字段与数据库中表对应。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
namespace RoomReservationContracts
{
    /// <summary>
    /// 数据协定
    /// </summary>
    [DataContract]
    public class RoomReservation : INotifyPropertyChanged
    {
        private int _id;
        private string _roomName;
        private DateTime _startTime;
        private DateTime _endTime;
        private string _contact;
        private string _text;

        /// <summary>
        ///要通过WCF服务发送数据, 引入DataContract 和 DataMember 特性对该类进行注解
        /// </summary>
        [DataMember]
        public int Id
        {
            get { return _id; }
            set { SetProperty(ref _id, value); }
        }
        [DataMember]
        [StringLength(45)]
        public string RoomName
        {
            get { return _roomName; }
            set { SetProperty(ref _roomName, value); }
        }
        [DataMember]
        public DateTime StartTime
        {
            get { return _startTime; }
            set { SetProperty(ref _startTime, value); }
        }
        [DataMember]
        public DateTime EndTime
        {
            get { return _endTime; }
            set { SetProperty(ref _endTime, value); }
        }
        [DataMember]
        [StringLength(45)]
        public string Contact
        {
            get { return _contact; }
            set { SetProperty(ref _contact, value); }
        }
        [DataMember]
        [StringLength(45)]
        public string Text
        {
            get { return _text; }
            set { SetProperty(ref _text, value); }
        }
        /// <summary>
        ///  接口需实现的事件方法
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// 得知属性发生变更时(定义为protected virtual 允许子类覆写本方法)
        /// </summary>
        /// <param name="propertyName">属性字段</param>
        protected virtual void OnNotifyPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        /// <summary>
        /// 设置属性
        /// </summary>
        /// <typeparam name="T">传入的类型参数</typeparam>
        /// <param name="item">字段名</param>
        /// <param name="value">字段值</param>
        /// <param name="propertyName"></param>
        protected virtual void SetProperty<T>(ref T item,T value,[CallerMemberName] string propertyName = null)
        {
            //如果两个类型的对象T 是否不等
            if (!EqualityComparer<T>.Default.Equals(item, value))
            {
                item = value;
                OnNotifyPropertyChanged(propertyName);
            }
        }
    }
}
1.2 创建服务协定
服务提供的操作可由接口定义,服务协定用ServiceContract特性定义,操作由OperationContract特性定义
using System;
using System.ServiceModel;
namespace RoomReservationContracts
{
    /// <summary>
    /// 服务协定
    /// </summary>
    [ServiceContract(Namespace ="http://www.myfirstwcfservice.com/RoomReservation/2017")]
    public interface IRoomService
    {
        [OperationContract]
        bool ReserveRoom(RoomReservation roomReservation);

        [OperationContract]
        RoomReservation[] GetRoomReservations(DateTime fromTime, DateTime dateTime);
    }
}
(2)、创建数据访问接口及实现类
技术分享图片

本实例中使用自定义的数据访问类,基于反射实现的数据访问接口。采用该类添加RoomReservationData 类库,实现具体操作协定。

using System;
using RoomReservationContracts;
using Net.BCloudSoft.Core.DataAccess;
using System.Data;
using System.Collections.Generic;

namespace RoomReservationData
{
    /// <summary>
    /// 实现服务类
    /// </summary>
    public class RoomReservationRepository
    {
        /// <summary>
        /// 预订房间
        /// </summary>
        /// <param name="roomReservation"></param>
       public bool ReserveRoom(RoomReservation roomReservation)
        {
            try
            {
                IDataAccess dataAccess = DataAccessFactory.Instance().GetDataAccess("server=127.0.0.1;database=wcfstudy_db;uid=root;pwd=admin;");
                string insertSql = string.Format("INSERT INTO wcfstudy_db.roomreservations VALUES" +
                    "({0},‘{1}‘,‘{2}‘,‘{3}‘,‘{4}‘,‘{5}‘)",
                    roomReservation.Id,
                    roomReservation.RoomName,
                    roomReservation.StartTime,
                    roomReservation.EndTime,
                    roomReservation.Contact,
                    roomReservation.Text);
                dataAccess.ExecuteNonQuery(insertSql);
                return true;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 获取所有预订信息
        /// </summary>
        /// <param name="fromTime"></param>
        /// <param name="toTime"></param>
        /// <returns></returns>
        public RoomReservation[] GetReservations(DateTime fromTime,DateTime toTime)
        {
            try
            {
                IDataAccess dataAccess = DataAccessFactory.Instance().GetDataAccess("MySqlDataAccess");
                DataTable dt = dataAccess.ExecuteDataTable(string.Format("SELECT * FROM [wcfstudy_db].[roomreservations] WHERE [StartTime] > ‘{0}‘ AND [EndTime] <‘{1}‘", fromTime, toTime));
                List<RoomReservation> list = new List<RoomReservation>();
                foreach (DataRow row in dt.Rows)
                {
                    RoomReservation roomReservation = new RoomReservation();
                    roomReservation.Id = int.Parse(row["Id"].ToString());
                    roomReservation.RoomName = row["RoomName"].ToString();
                    roomReservation.StartTime = Convert.ToDateTime(row["StartTime"]);
                    roomReservation.EndTime = Convert.ToDateTime(row["EndTime"]);
                    roomReservation.Contact = row["Contact"].ToString();
                    roomReservation.Text = row["Text"].ToString();
                    list.Add(roomReservation);
                }
                return list.ToArray();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }
}
(3)、服务的实现
  3.1 基于 WCF 服务库直接生成的模型,默认包含服务协定和服务实现。
  a:根据模板创建的服务实现
 
技术分享图片
  b:根据模板创建的服务协定
技术分享图片
  3.2 如果客户应用程序只使用 元数据 信息来创建访问服务的代理,则使用visio studio 创建基于 WCF 服务库的模板是可行的。
但是,如果客户端直接使用协定类型,则最好把协定放在一个独立的程序集中如本例所示
技术分享图片

 

  3.3 本案例中服务的实现
using System;
using RoomReservationContracts;
using RoomReservationData;
using System.ServiceModel;

namespace RoomReservationService
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    public class RoomReservationService : IRoomService
    {
        /// <summary>
        /// 获取所有预订信息
        /// </summary>
        /// <param name="fromTime">开始时间</param>
        /// <param name="dateTime">结束时间</param>
        /// <returns></returns>
        public RoomReservation[] GetRoomReservations(DateTime fromTime, DateTime dateTime)
        {
            RoomReservationRepository repository = new RoomReservationRepository();
            return repository.GetReservations(fromTime, dateTime);
        }

        /// <summary>
        /// 预订房间
        /// </summary>
        /// <param name="roomReservation"></param>
        /// <returns></returns>
        public bool ReserveRoom(RoomReservation roomReservation)
        {
            RoomReservationRepository repository = new RoomReservationRepository();
            return repository.ReserveRoom(roomReservation);
        }
    }
}
    3.4 配置AppConfig
  在创建WCF 服务库时会创建AppConfig 文件,主要用于配置服务实现及服务协定,具体配置方法如下所示
<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" />
  </system.web>
  <!-- 部署服务库项目时,必须将配置文件的内容添加到
 主机的 app.config 文件中。System.Configuration 不支持库的配置文件。 -->
  <system.serviceModel>
    <services>
      <!--service 节点中 配置 实现类-->
      <service name="RoomReservationService.RoomReservationService">
        <!--contract 节点中配置接口类-->
        <endpoint address="" binding="basicHttpBinding" contract="RoomReservationContracts.IRoomService">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8733/Design_Time_Addresses/RoomReservationService/Service1/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- 为避免泄漏元数据信息,
          请在部署前将以下值设置为 false -->
          <serviceMetadata httpGetEnabled="True" httpsGetEnabled="True"/>
          <!-- 要接收故障异常详细信息以进行调试,
          请将以下值设置为 true。在部署前设置为 false 
          以避免泄漏异常信息 -->
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

</configuration>

 (4)使用WCF服务宿主和WCF测试客户端

  将RoomReservationService 类设置为启动项目,启动项目,WCF服务主机会启动WCF测试客户端,该测试客户端可用于测试应用程序,在输入参数后,点击‘调用’,会执行操作,输入测试数据可查看到响应结果。如下图:
技术分享图片

   查看数据库,可知数据已经完成写入操作

 技术分享图片

(5)创建定制的服务宿主

使用WCF可以在任何宿主上运行服务,可以为对等服务创建一个WPF应用程序,可以创建一个Windows 服务,使用WAS 或 IIS 存放该服务,控制台程序也可以演示简单的自定义宿主。创建自定义宿主必须引入 System.ServiceModel和RoomReservationService。具体实现思路是,调用Open方法启动服务的监听器信道,该服务准备用于监听请求。Close方法会停止信道。代码如下:
using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using RoomReservationService;
using static System.Console;

/// <summary>
/// 自定义服务宿主
/// </summary>
namespace RoomReservationHost
{
    public class Program
    {
        internal static ServiceHost _serviceHost = null;
        
        /// <summary>
        /// 开启服务监听
        /// </summary>
        internal static void StartService()
        {
            try
            {
                //ServiceHost的构造函数第二个参数定义了服务的基地址,可以设置默认绑定,HTTP
                //的默认值是basicHttpBinding
                _serviceHost = new ServiceHost(typeof(RoomReservationService.RoomReservationService),
                    new Uri("http://localhost:9000/RoomReservation"));
                _serviceHost.Description.Behaviors.Add(
                    new ServiceMetadataBehavior
                    {
                        //获取设置一个值,指示是否发布服务元数据以使用Http/Get请求进行检索。
                        HttpGetEnabled = true
                    });

            }
            catch (AddressAccessDeniedException)
            {
                WriteLine("地址禁止访问,使用netsh.exe 注册监听端口");
                throw;
            }
        }
        
        /// <summary>
        /// 停止服务监听
        /// </summary>
        internal static void StopService()
        {
            if(_serviceHost != null && _serviceHost.State== CommunicationState.Opened)
            {
                _serviceHost.Close();
            }
        }

        static void Main()
        {
            StartService();
            WriteLine("服务正在运行,请退出");
            ReadLine();
            StopService();
        }
    }
}
除以上使用编码方式实现WCF配置外,还可以通过,右键RoomReservationServcie 的App.config文件,点击 编辑 WCF 配置,如下图所示
技术分享图片
技术分享图片

 

(6)使用元数据创建客户应用程序
  此业务场景下,创建一个包含控件的WPF应用程序,命名为RoomReservationClient。如下图所示
技术分享图片
添加服务引用,右键选取RoomReservationClient类库,添加 服务引用,点击发现,查找当前解决方案下的服务,将名称空间设置为RoomReservationService,这将为生成的代理类定义名称。如下图所示:
技术分享图片

  根据数据协定把RoomReservation 引入到RoomReservationClient 中,RoomServiceClient 是RoomReservationService的客户端代理类(RoomServiceClient 是由上图执行后生成的),该客户端包含由操作协定定义的方法,使用这个客户端,可以将会议室预定信息发送给正在运行的服务。

在代码MainWindow.xaml.cs中,通过click 事件调用的OnReserveRoom方法,通过代理类调用ReserveRoomAsync。_roomReservation 为操作的数据源。代码如下
using System;
using System.Windows;
using RoomReservationClient.RoomReservationService;

namespace RoomReservationClient
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        private RoomReservationService.RoomReservation _roomReservation;

        public MainWindow()
        {
            InitializeComponent();
            _roomReservation.StartTime = DateTime.Now;
            _roomReservation.EndTime = DateTime.Now.AddHours(1);
            this.DataContext = _roomReservation;
        }

        private async void onReserveRoom(object sender, RoutedEventArgs e)
        {
            //RoomServiceClient 是客户端的代理,该客户端包含由操作协定定义的方法,使用这个客户端,
            //可以将会议室预定信息发送给正在运行的服务。
            var client = new RoomServiceClient();
            bool reserved = await client.ReserveRoomAsync(_roomReservation);
            client.Close();
            if (reserved)
            {
                MessageBox.Show("实体保存成功!");
            }
        }
    }
}

6、最后

  参考的案例还有诊断及与客户端共享协定程序集两部分内容,两部分内容的具体实现并没有跟着书本完成,有兴趣的同学可以自行查看,参考的源代码路径为http://www.wrox.com/go/professionalcsharp6

c#进阶(5)—— WCF 实现简单预订功能

标签:on()   oda   pil   代理类   cts   3.4   分享   文件   div   

原文地址:http://www.cnblogs.com/cklovefan/p/7956449.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!