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表存储预订信息,

a:主要实施步骤包括

(1)创建服务和数据协定

(2)使用 自定义类库 访问数据库

(3)实现服务

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

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

(6)使用元数据创建客户应用程序

(7)使用共享的协定创建客户应用程序

(8)配置诊断设置

b:主要包括:数据访问类、协定(服务协定、数据协定)、自定义宿主程序、客户端、服务实现。

类说明:

(1)Net.BCloudSoft.Core.DataAccess 数据访问底层代码。

RoomReservationData 调用数据访问提供的ExecuteNonQuery方法,实现数据的写入。

(2)RoomReservationContracts ,定义数据协定及服务协定,其中数据协定是数据实体映射,采用DataContract 及 DataMemeber 分别标记类及属性。服务协定,是一个接口类,包括服务协定及操作协定,接口类中声明方法,如ReserveRoom等,在类上添加ServiceContract,在方法上标记OperationContract。

(3)RoomReservationHost,自定义服务宿主,控制台程序,由StartService、StopService组成,Open方法会启动服务的监听器信道,Close方法会停止信道,主要用到的核心类是ServiceHost,ServiceHost类实例化时,可以将服务及服务地址进行处理。

(4)RoomReservationService 服务实现,实现服务协定中定义的接口方法。

(5)RoomReservationClient ,WPF作为客户端,右键添加服务引用,选取Service服务,此时会为RoomReservationService服务生成代理,代理类中会自动生成异步方法,使用async及await关键字调用ReserveRoomAsync() 异步方法,使订阅功能支持异步。

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 实现简单预订功能相关推荐

  1. 用ExtJs+Linq+Wcf打造简单grid

    本系列文章列表 1)Ajax访问Xml Web Service的安全问题以及解决方案 2)Ajax与WCF交互-WCF之美 3) Ajax与Wcf交互-JSON 4) ExtJs与WCF交互:生成树 ...

  2. 10年磨一剑,软件编程走火入魔之:把简单的功能做个彻彻底底、把劳动成果重复利用

    让管理软件开发人员早点儿回家休息,做个跟老外有的一PK的软件组件,铜墙铁壁的权限组件. 年轻时.精力旺盛,有用不完的劲儿,但是工作经验不多,对各种行业实际应用没有深入的了解,大多停留在表面问题上,做不 ...

  3. JavaScript进阶必会的手写功能(二)

    JavaScript进阶必会的手写功能(一) 6. 手写浅拷贝 6.1 JavaScript数据类型分类 1. 简单数据类型: Number. String.Boolean.null.undefine ...

  4. python简单计算器综合实验报告_Python实现的简单计算器功能详解

    本文实例讲述了Python实现的简单计算器功能.分享给大家供大家参考,具体如下: 使用python编写一款简易的计算器 计算器效果图 首先搭建计算器的面板: 计算器面板结构 建造一个继承于wx.Fra ...

  5. SharePoint 2013 调用WCF服务简单示例

    内容比较简单,主要记录自己使用SharePoint 2013WCF服务遇到的小问题和小经验,分享给大家,希望能够给需要的人有所帮助.好吧,进入正题! 第一部分 SharePoint 2013调用自带W ...

  6. 10年磨一剑,软件编程走火入魔之:把简单的功能做个彻彻底底、把劳动成果重复利用...

    让管理软件开发人员早点儿回家休息,做个跟老外有的一PK的软件组件,铜墙铁壁的权限组件. 年轻时.精力旺盛,有用不完的劲儿,但是工作经验不多,对各种行业实际应用没有深入的了解,大多停留在表面问题上,做不 ...

  7. python怎么建立画板_Python基于opencv实现的简单画板功能示例

    本文实例讲述了Python基于opencv实现的简单画板功能.分享给大家供大家参考,具体如下: import cv2 import numpy as np drawing = False # true ...

  8. python实现简单计算器功能键介绍_Python实现的简单计算器功能详解

    本文实例讲述了Python实现的简单计算器功能.分享给大家供大家参考,具体如下: 使用python编写一款简易的计算器 计算器效果图 首先搭建计算器的面板: 计算器面板结构 建造一个继承于wx.Fra ...

  9. php聊天功能_php实现简单聊天功能

    搜索热词 1.创建聊天消息表,其表的字段有消息内容,发送时间和发送者的名称: CREATE TABLE `guanhui`.`message` ( `id` INT(10) NOT NULL AUTO ...

最新文章

  1. System.Runtime.InteropServices.Marshal.GetTypeFromCLSID(System.Guid) 问题解决方法
  2. hihocoder 后缀自动机专题
  3. mysql视图登录_mysql视图
  4. linux命令:fsck
  5. 学习的过程和挖矿其实很像
  6. gitlab mysql 配置_gitlab的安装与修改端口配置
  7. Python的reshape(-1,1)
  8. 【jenkins】jenkins CI/CD搭建基本过程
  9. 一、第一个注解的 SpringMVC 程序
  10. 构造方法的应用 0107 c#
  11. java机器PDF_机器人制作入门(第3版)PDF 下载
  12. z tree 如何把选中的节点保存为标准的json格式_为什么MongoDB使用B-Tree?
  13. 程序员面试金典——18.9实时中位数
  14. Apex 中插入更新数据的事件执行顺序
  15. 【Python基础】from pygame.base import * # pylint: disable=wildcard-import; lgtm[py/polluting-import] Mod
  16. 【英语:基础进阶_原著扩展阅读】J6.原著阅读实战训练
  17. 外包公司的运作模式和赚钱之道-聊聊IT外包公司
  18. 矩阵论-线性空间与线性映射
  19. node.js版本不同,跑不起项目(个人笔记)
  20. AndroidStudio 跑马灯效果不自动滚动的原因

热门文章

  1. Linux系统添加永久静态路由的方法
  2. NLayerAppV3--.net Core2实现的DDD分层架构
  3. 鸡啄米vc++2010系列2(项目文件分析)
  4. 久等了,41连开班仪式!
  5. 【转】“线程间操作无效: 从不是创建控件的线程访问它”
  6. 使用SDL打造游戏世界之入门篇 - 7
  7. [Leedcode][JAVA]第[945]题
  8. mysql合并多条纪录字段_Mysql应用mysql合并多条记录的单个字段去一条记录编辑
  9. Educational Codeforces Round 112 (Rated for Div. 2)(A-D)
  10. 街上第一台电子计算机是,南京信息工程大学滨江学院2009级《计算机基础》(文科)a试卷(含答案)【最新】.doc...