WCF是构建和运行互联系统的一系列技术的总称,它是建立在Web Service架构上的一个全新的通信平台。你可以把它看成是.NET平台上的新一代的Web Service。WCF为我们提供了安全、可靠的的消息通信,也为我们提供了更好的可互操作性是的我们可以和其他的平台进行“交流”。
下面介绍两种方式建立wcf服务器端:
当使用方式二建立的服务端在本机上运行一点问题都没有,但当服务端移植到远程服务器上就会报异常
未经处理的异常: System.ServiceModel.Security.SecurityNegotiationException: 调用 方未由服务进行身份验证。
解决方案参考:https://blog.csdn.net/zxy13826134783/article/details/85267969
2020年6月12日补充
遇到的需求:
1 WCF服务端需要使用配置文件的方式启动,同时方便调试(即客户端启动调试时能进入WCF服务端调试)
2 数据格式需要传递实体对象
解决方案结构:
涉及到的DLL引用:
System.ServiceModel.dll
System.Runtime.Serialization.dll
一 WCF服务端配置
要实现方便调试(即客户端启动调试时能进入WCF服务端调试)的需求,需要新建一个空的ASP.NET网站项目
1.1 WCF服务端实体类:
namespace WpfApplication2
{
public class CodeDterm
{
public int CodeDtErmId { set; get; }
public string Name { set; get; }
public string Description { set; get; }
}
}
namespace WpfApplication2
{
public class CodeDtErmValue
{
public int CodeDtErmId { set; get; }
public int CodeId { set; get; }
public int Code { set; get; }
public string DisplayName { set; get; }
}
}
1.2 查询Oracle数据库相关类(数据库的连接字符串对于的配置文件在后面),至于如何查询数据库数据,不是重点
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Dapper;
using Oracle.ManagedDataAccess.Client;
using System.Windows;
using System.Data;
namespace WpfApplication2
{
public class DapperHelper
{
private static string ConnectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
/// <summary>
/// 获取读取器
/// </summary>
/// <returns></returns>
public static OracleDataReader GetReader(string sql) {
OracleConnection conn = new OracleConnection(ConnectionString);
OracleCommand cmd = new OracleCommand(sql, conn);
try
{
conn.Open();
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
catch (Exception)
{
throw;
}
}
}
}
using Oracle.ManagedDataAccess.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WCFService
{
public class CodeDtermService
{
public List<CodeDterm> GetCodeDterm(string KeyWord) {
string sql = "";
if (KeyWord.Length==0)
{
sql = "select CodeDtErmId,Name,Description from Concept.Codedterm";
}
else
{
sql = "select CodeDtErmId,Name,Description from Concept.Codedterm where Description like '%{0}%'";
}
sql = string.Format(sql, KeyWord);
List<CodeDterm> CodeDtermList = null;
OracleDataReader reader = null;
try
{
reader = DapperHelper.GetReader(sql);
CodeDtermList = new List<CodeDterm>();
while (reader.Read())
{
CodeDtermList.Add(new CodeDterm() {
CodeDtErmId=Convert.ToInt32(reader["CodeDtErmId"]),
Name=reader["Name"].ToString(),
Description=reader["Description"].ToString()
});
}
return CodeDtermList;
}
catch (Exception)
{
throw;
}
finally {
if (reader != null)
{
reader.Close();
}
}
}
}
}
using Oracle.ManagedDataAccess.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WCFService
{
public class CodeDtErmValueService
{
public List<CodeDtErmValue> CodeDtErmValueById(string CodeDtErmId)
{
string sql = "select CodeDtErmId,CodeId,Code,DisplayName from Concept.codedtermvalue where codedtermid='{0}'";
sql = string.Format(sql, CodeDtErmId);
List<CodeDtErmValue> CodeDtermErmList = null;
OracleDataReader reader = null;
try
{
reader = DapperHelper.GetReader(sql);
CodeDtermErmList = new List<CodeDtErmValue>();
while (reader.Read())
{
CodeDtermErmList.Add(new CodeDtErmValue()
{
CodeDtErmId = Convert.ToInt32(reader["CodeDtErmId"]),
Code = Convert.ToInt32(reader["Code"]),
CodeId = Convert.ToInt32(reader["CodeId"]),
DisplayName = reader["DisplayName"].ToString()
});
}
return CodeDtermErmList;
}
catch (Exception)
{
throw;
}
finally
{
if (reader != null)
{
reader.Close();
}
}
}
}
}
1.3 WCF服务契约接口及实现的服务
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
namespace WCFService
{
[ServiceContract]
interface ICodedterm
{
[OperationContract]
CodeDterm GetCodeDterm(string KeyWord);
[OperationContract]
List<CodeDtErmValue> CodeDtErmValueById(string CodeDtErmId);
[OperationContract]
string TestMethod();
}
public class CodedtermService : ICodedterm
{
public List<CodeDtErmValue> CodeDtErmValueById(string CodeDtErmId)
{
return new CodeDtErmValueService().CodeDtErmValueById(CodeDtErmId);
}
public CodeDterm GetCodeDterm(string KeyWord)
{
return new CodeDtermService().GetCodeDterm(KeyWord)[0];
}
public string TestMethod()
{
return "调用成功";
}
}
}
1.4 新建一个后缀为svc的文件CodeERM.svc,名字随意起,并编辑如下:
1.5 配置文件Web.config编写如下:
<?xml version="1.0" encoding="utf-8"?>
<!--
有关如何配置 ASP.NET 应用程序的详细信息,请访问
https://go.microsoft.com/fwlink/?LinkId=169433
-->
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.5.2"/>
<httpRuntime targetFramework="4.5.2"/>
</system.web>
<connectionStrings>
<add name="ConnectionString" connectionString= "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=172.18.99.228)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=S12)));Persist Security Info=True;User ID=apps;Password=jetsun"/>
</connectionStrings>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="NoneSecurity"
maxBufferPoolSize="12000000" maxReceivedMessageSize="12000000" useDefaultWebProxy="false">
<readerQuotas maxStringContentLength="12000000" maxArrayLength="12000000"/>
<security mode="None"/>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service name="WCFService.CodedtermService" behaviorConfiguration="TestBehavior">
<endpoint address = "" binding="wsHttpBinding" contract="WCFService.ICodedterm"></endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="TestBehavior">
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
其中
1.6 设置WCF服务的端口(鼠标右键项目,选择属性,弹出对话框如下图):
1.7 把该项目设为启动项目并点击调试后,在浏览器中输入:http://localhost:8090//CodeERM.svc
出现下图界面,表示WCF服务建立成功
二 客户端调用WCF服务
方式有两种:
1 自己编写代码,但这种方式貌似传递的数据不能为实体类对象,只能传递系统确定的值类型数据,如字符串、数值型和byte数组
2 鼠标右键添加服务引用的方式,具体操作参考文章前面部分,测试发现可以传递实体类对象、List<实体类对象>
方式1 自己编写代码的方式
2.1 客户端服务契约接口(WCFClient类库中编写)
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
namespace WCFClient
{
[ServiceContract]
interface ICodedterm
{
[OperationContract]
CodeDterm GetCodeDterm(string KeyWord);
[OperationContract]
List<CodeDtErmValue> CodeDtErmValueById(string CodeDtErmId);
[OperationContract]
string TestMethod();
}
}
2.2 调用WCF服务(WCFClient类库中编写)
namespace WCFClient
{
public class CodeDtErmManager
{
EndpointAddress address;
WSHttpBinding binding;
WCFClient.ICodedterm channel;
ChannelFactory<WCFClient.ICodedterm> factory;
public CodeDtErmManager()
{
address = new EndpointAddress("http://localhost:8090/CodeERM.svc");
binding = new WSHttpBinding();
//binding.Security.Mode = SecurityMode.None;
factory = new ChannelFactory<WCFClient.ICodedterm>(binding, address);
channel = factory.CreateChannel();
}
public string TestMethod() {
return channel.TestMethod();
}
}
}
2.3 调用测试(Test控制台中编写)
namespace Test
{
class Program
{
static void Main(string[] args)
{
WCFClient.CodeDtErmManager manager = new WCFClient.CodeDtErmManager();
Console.WriteLine(manager.TestMethod());
Console.ReadKey();
}
}
}
测试结果如下图:
方式2:添加服务引用的方式:
WCFClient类库不用,直接在Test控制台项目添加http://localhost:8090//CodeERM.svc服务引用
测试调用如下:
namespace Test
{
class Program
{
static void Main(string[] args)
{
ServiceReference2.CodedtermClient client = new ServiceReference2.CodedtermClient();
Console.WriteLine(client.TestMethod());
ServiceReference2.CodeDterm codeDterm=client.GetCodeDterm("史");
Console.WriteLine(codeDterm.CodeDtErmId+"\t"+codeDterm.Description);
Console.ReadKey();
}
}
}
调用结果如下:
但是如果服务的返回的数据类型为List<实体对象>,则客户端调用后返回的是数组,如 实体对象[]
常见问题:
问题1:当发生这样的异常:
已经超过传入消息的最大消息大小配额。若要增加配额,请使用相应绑定元素上的MaxReceivedMessageSize
解决方案:这时客户端接收到的数据长度大于默认的长度了,这时需要增加客户端的接收数据的长度,通过配置文件操作
App.config配置如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_ICodedterm" maxBufferPoolSize="12000000" maxReceivedMessageSize="12000000"/>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:8090/CodeERM.svc" binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_ICodedterm" contract="ServiceCodeERM.ICodedterm"
name="WSHttpBinding_ICodedterm">
<identity>
<userPrincipalName value="zhangxiangyu@hissoft.com" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
核心配置,把下面的数值改大一点: