ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

C# WCF与Web API接口开发实战:从契约设计到混用部署避坑

C# WCF与Web API接口开发实战:从契约设计到混用部署避坑 简介这是一套完整的C# WCF与Web项目接口开发实战案例面向具备基础C#、ASP.NET知识、希望掌握分布式服务接口设计的开发者也适合准备在Windows平台构建服务端接口并集成到Web应用中的项目团队。案例围绕ServiceContract服务契约定义、DataContract数据契约序列化、增删改查CRUD操作、终结点绑定配置、IIS及自托管部署、客户端通过代理类调用等多个环节展开完整演示了从创建服务到发布调用的全流程能够帮助读者将WCF理论落地到实际Web项目中并理解服务元数据交换、终结点地址与绑定设置等工作原理。压缩包共499个文件约62.67MB其中78个cs源码文件、24个config配置文件、11个csproj工程文件及32个xml文档构成核心学习内容177个dll与44个pdb等编译产物便于运行时对照检查sln解决方案、svcinfo、wsdl等文件则辅助理解服务元数据和配置结构包内多个示例工程均可编译运行便于从完整项目层面观察服务端与客户端的配合方式。目前已有658人学习下载适合想通过真实案例串起WCF服务端、宿主配置与客户端调用完整知识链的.NET学习者。1. C# WCF 和 WEB 项目接口开发先搞清楚这套方案解决什么问题当你手上同时有 WCF 老服务和 Web 新页面要对接时会立刻理解“C# WCF 和 WEB 项目接口开发”这句话的分量。它讲的不是把代码从 WCF 改成 Web API而是怎么在同一套 C# 技术栈里让设备端、ERP、上位机和浏览器端互相说上话。WCF 适合内网设备采集、MES 数据上报、老系统服务调用WEB 方式适合页面、小程序、第三方系统对接。很多工程师一听见 WCF 就想推翻重写其实只要把契约、绑定、序列化这几个点理清典型的接口开发需求并不复杂。本文会从最小可运行服务讲到一个完整的 WCF Web API 混用案例再列出实际项目中高频踩到的坑让你照着能落地上线。2. WCF 服务端搭建从契约到宿主的最小可运行模型2.1 为什么企业老项目还在用 WCF契约优先与服务治理Windows Communication Foundation 从 .NET Framework 3.0 时代就成了微软分布式通信的主力它把 TCP、HTTP、命名管道、MSMQ 这些传输方式收敛成一套服务模型。写 WCF 服务时你首先定义的往往不是实现类而是服务契约和数据契约。这种“契约优先”的开发方式让服务端和客户端可以各自开发只要契约不变两端就不容易漂移。WCF 的核心是 ABCAddress 决定服务在哪Binding 决定怎么传输Contract 决定暴露什么方法。很多 C# 老项目之所以到今天还在维护 WCF是因为它对这些点的控制比 Web API 细得多。比如设备采集场景需要长连接和可靠会话netTcpBinding 能比 HTTP 轮询做得更省资源再比如老 ERP 系统提供给外部的接口很多是以 SOAP 形式存在的你想绕也绕不开。加上 WCF 自带元数据交换MEX客户端可以直接从地址拉取契约描述自动生成调用代码省去大量手写文档的时间。我见过不少新同事一上来就说“WCF 过时了”但在制造业、仓储物流这类 C# 上位机占主导的环境里WCF 依旧是坚挺的存在。它不性感但是可靠。尤其当传输通道需要走 TCP、需要并发控制、需要和 Windows 服务完美共宿时Web API 并不能直接替代。理解这一点你才不会在接口开发时犯“看到 WCF 就换技术栈”的方向性错误。2.2 用 ServiceContract 定义接口一个可运行的 WCF 服务常见做法是先建一个类库项目专门放接口和数据契约。文件拆分要清晰接口一个文件、实现一个文件、数据契约一个文件后面生成客户端代码和写单元测试都会方便许多。下面是最小模型using System; using System.Runtime.Serialization; using System.ServiceModel; namespace DeviceInterfaces { [DataContract] public class DeviceData { [DataMember(Order 1)] public string DeviceId { get; set; } [DataMember(Order 2)] public string Value { get; set; } [DataMember(Order 3)] public DateTime Timestamp { get; set; } } [ServiceContract] public interface IDeviceDataService { [OperationContract] bool PushDeviceData(DeviceData data); [OperationContract] DeviceData GetLatestData(string deviceId); } }这里 [ServiceContract] 和 [OperationContract] 是 WCF 的“开关”只有被标记的方法才会出现在服务元数据里。值得留意的是 [DataMember] 的 Order 属性它控制字段在序列化时的顺序。WCF 默认排序规则不一定和声明顺序一致如果服务端升级时新增字段老客户端很容易因为顺序变化而反序列化错位。我一般从第一个版本就给所有字段标 Order不让运行时猜。实现类如果只是把数据扔内存看起来很简单但它代表了一类真实场景比如设备端上位机采集到数值后通过 WCF 推到中央服务暂存using System.Collections.Concurrent; namespace DeviceInterfaces { public class DeviceDataService : IDeviceDataService { private static readonly ConcurrentDictionarystring, DeviceData _cache new ConcurrentDictionarystring, DeviceData(); public bool PushDeviceData(DeviceData data) { if (data null || string.IsNullOrEmpty(data.DeviceId)) { return false; } _cache[data.DeviceId] data; return true; } public DeviceData GetLatestData(string deviceId) { _cache.TryGetValue(deviceId, out var data); return data; } } }逻辑说明PushDeviceData 先做空值和设备号校验再用 ConcurrentDictionary 缓存。这里的字典 kv 设计只是为了跑通链路正式项目一般会换成 SQLite 或 SQL Server。从接口开发角度实现类关注点不应该是缓存而是“能不能在宿主进程里被正确实例化”。WCF 默认每来一个请求会创建新的服务实例如果实现类里放太多状态容易引入并发问题。如果后续要单实例模式需要在服务行为上加 InstanceContextMode新手阶段先不用折腾。2.3 宿主选择与配置文件控制台宿主与 IIS 宿主差异WCF 服务的实现类不会自己跑起来它必须寄宿在一个进程里。最低成本的是控制台宿主适合开发调试和上位机内嵌using System; using System.ServiceModel; using DeviceInterfaces; class Program { static void Main(string[] args) { using (var host new ServiceHost(typeof(DeviceDataService))) { host.Open(); Console.WriteLine(WCF 服务已启动监听地址); foreach (var endpoint in host.Description.Endpoints) { Console.WriteLine($ {endpoint.Address} [{endpoint.Binding.GetType().Name}]); } Console.ReadLine(); } } }有人问为什么不用 IIS 宿主Web 项目里放个 .svc 文件不也能跑确实可以但有两个现实问题一是 IIS 宿主只能使用 HTTP 类绑定netTcpBinding 在 IIS 里配置麻烦二是应用池回收会把服务状态一起带走设备采集场景的在线连接会断。所以我更倾向用控制台或 Windows 服务来承载 WCF让 C# 上位机和服务端各自独立。对应 App.config 配置configuration system.serviceModel services service nameDeviceInterfaces.DeviceDataService endpoint addressnet.tcp://localhost:9001/ bindingnetTcpBinding contractDeviceInterfaces.IDeviceDataService / endpoint addresshttp://localhost:9002/ bindingbasicHttpBinding contractDeviceInterfaces.IDeviceDataService / /service /services /system.serviceModel /configuration参数说明同一个 service 节点下挂了两个 endpoint地址和绑定各不相同。net.tcp 走 9001 端口给内网设备端http 走 9002 给外部测试这是 WCF 多端点能力的典型用法。注意 service name 必须写实现类的完全限定名endpoint 里的 contract 写接口完全限定名如果程序集换了命名空间这里就会在启动时直接报错。配置文件里没有写 mex 端点开发时建议加上否则生成客户端封装类时无法从元数据拉取结构。3. WEB 接口开发ASP.NET Web API 与 WCF 的选型边界3.1 WCF 和 WEB 项目接口开发各自适合什么场景C# 生态里的 Web 接口开发主流已经不是 ASMX而是 ASP.NET Web API。它和 WCF 不是替代关系而是互补关系。下表是我选型时会参照的关键维度。维度WCFASP.NET Web API数据格式SOAP、XML、二进制JSON、XML传输方式TCP、HTTP、命名管道、MSMQHTTP/HTTPS客户端形态自动生成封装类、Java/其他语言可调用浏览器、App、Postman、任意 HTTP 客户端长连接与可靠会话支持内网场景优势明显无状态依赖 SignalR 等补充开发调试WcfTestClient、svctraceviewerSwagger、curl、Postman部署环境Windows 服务、IIS、控制台IIS、Kestrel、容器这个表格背后的抉择点是你的调用方是谁。设备端 C# 上位机、同机房 MES、老 ERP 模块走 WCF 很顺手页面、手机、外部合作方走 Web API 更合适。很多所谓的“C# WCF 和 WEB 项目接口开发”实际工作就是在这两类接口之间做桥接。比如车间拧紧工具项目里上位机读出的扭矩值先交给 WCF 服务落库浏览器报表再从 Web API 读同一份数据。两条链路各司其职硬把它们合并成一个接口形态反而会让职责变乱。我在做接口选型时还有一个保守原则如果一个系统五年前就在用 WCF且现场运维人员已经熟悉它那么新增接口也应该优先沿用 WCF而不是为了新潮改成 Web API。反过来如果是全新开发的面向互联网的产品就没必要再起 SOAP 那套直接上 ASP.NET Core Web API。技术选型要考虑生态、运维惯性还有实际链路上的设备数量。3.2 用 ASP.NET Core Web API 暴露 JSON 接口最小实现先不用管数据库先用一个内存集合把接口跑通后面再接 EF Core 或其它 ORM。定义一个简单控制器using Microsoft.AspNetCore.Mvc; namespace WebPortal.Controllers { [ApiController] [Route(api/[controller])] public class DeviceController : ControllerBase { private static readonly ListDeviceDataView _data new() { new DeviceDataView { DeviceId A001, Value 12.3 } }; [HttpGet(latest/{deviceId})] public IActionResult GetLatest(string deviceId) { var item _data.FirstOrDefault(d d.DeviceId deviceId); if (item null) { return NotFound(new { code NOT_FOUND }); } return Ok(item); } [HttpPost(push)] public IActionResult Push([FromBody] DeviceDataView data) { _data.RemoveAll(d d.DeviceId data.DeviceId); _data.Add(data); return Ok(new { code OK }); } } public class DeviceDataView { public string DeviceId { get; set; } public string Value { get; set; } public DateTime Timestamp { get; set; } } }Program.cs 里的注册方式比其他老框架简单var builder WebApplication.CreateBuilder(args); builder.Services.AddControllers(); var app builder.Build(); app.UseAuthorization(); app.MapControllers(); app.Run();参数说明AddControllers 注册 MVC Core 控制器MapControllers 把路由映射到请求管道。api/[controller]表示控制器名去掉 Controller 后缀所以 DeviceController 的根路径是 api/device。[HttpPost(push)]配合[FromBody]让前端可以直接提交 JSON模型绑定会自动把 JSON 字段对应到 DeviceDataView 属性。如果你忘了 AddControllers路由会全部 404这是最常翻车的起点。3.3 与 WCF 共存同一解决方案里同时跑两种接口实际案例里WCF 和 Web API 出现在同一个解决方案很常见。我的习惯是WCF 服务宿主独立部署Web API 也独立部署两者通过数据库或同一个缓存服务共享状态。它们不一定要在同一个进程里。非要放同一个进程也可以在 Web API 的启动代码里 new 一个 ServiceHost 并 Open能够满足轻量联调。但我不推荐生产环境这么干原因有三个第一Web 应用池一旦回收WCF 服务也会被带走第二WCF 和 Web API 的日志、超时、线程池设置会互相干扰第三端口和 HTTPS 证书配置会变得极难排查。所以即便你在开发机上一个工程跑两个项目发布时也应当把它们拆成两个服务。这样任何一个接口出问题都不会把另一条链路拖下水。4. 实际案例WCF 与 Web 接口联调的完整落地方案4.1 案例背景与接口契约集成商对接场景这个案例是典型的“设备数据往上层流”车间里有一台拧紧工具控制器C# 上位机定期读取实时扭矩值MES 系统需要这些数据做工艺追溯而管理层要看 Web 报表。MES 方因为历史原因只开放 WCF 接口报表前端则只认 JSON。这种环境我在集成商项目里见过多次设备类型可以是 Power Focus 6000、RFID 考勤系统、称重仪表本质都一样。接口被拆成两段。第一段是上位机到中央服务设备数据通过 netTcpBinding 推送到 WCF 服务端保证内网传输效率第二段是 Web API它不直接和上位机通信而是查询同一数据库把结果转成 JSON 给前端。这样前端不会因为设备协议差异而频繁改版后端也可以统一做权限和限流。整个方案的重点不是代码有多炫而是契约清晰WCF 负责写Web API 负责读数据模型独立于设备协议。4.2 WCF 服务端实现与数据契约在这个案例里WCF 服务端不是简单存一条单点数据而是要按工单批量提交并返回业务码。服务契约这样定义[ServiceContract] public interface IMesDataService { [OperationContract] bool SubmitWorkOrder(string workOrderNo, ListDeviceData items); [OperationContract] string GetServerTime(); }实现类里要处理一点现场细节设备端上报的时间往往是本机时间如果设备时钟不准进数据库的时间就不可信。所以在 SubmitWorkOrder 里我会覆盖客户端时间以服务端时间为准public bool SubmitWorkOrder(string workOrderNo, ListDeviceData items) { if (string.IsNullOrEmpty(workOrderNo) || items null || items.Count 0) return false; foreach (var item in items) { item.Timestamp DateTime.Now; _repo.Save(item); } return true; }参数说明List 在 WCF 序列化时会被转成数组客户端传 null 时会收到空列表不会直接抛异常这对设备端上报很友好。返回 bool 只适合演示生产环境建议定义业务码枚举或字符串比如 “OK” “DEVICE_NOT_FOUND” “WORK_ORDER_EXPIRED”否则客户端只能看到成功或失败没法区分原因。服务端并发配置要提前写进行为节点behaviors serviceBehaviors behavior nameThrottleBehavior serviceThrottling maxConcurrentCalls200 maxConcurrentSessions100 maxConcurrentInstances50 / /behavior /serviceBehaviors /behaviorsmaxConcurrentCalls 控制同时执行的操作数maxConcurrentSessions 控制会话数maxConcurrentInstances 控制服务实例数。设备采集场景往往是大量短消息调用数不高但连接可能很多给 session 留出余量比调高 calls 更有效。我这里给的参数是按车间设备数量估出来的不是万能值需要按实际压测往上调。4.3 WEB 客户端调用 WCF 服务SVCUtil / HttpClient 两种方式Web 项目要调用 WCF常见方式有两种。第一种是在 Visual Studio 里添加服务引用或者用 svcutil 命令生成客户端代码svcutil.exe /language:C# /out:MesServiceClient.cs http://192.168.1.50:9002/mex这个命令会抓取 MEX 元数据生成一个可调用的封装类。接着在代码里使用var channel new MesDataServiceClient(); try { var result channel.SubmitWorkOrder(WO-001, items); Console.WriteLine(result); } finally { channel.Abort(); }这里有个经验关闭封装类时不要用 Close用 Abort。因为 WCF 客户端调用完成后如果底层通道状态不是正常关闭Close 会再次抛出异常掩盖原始错误。Abort 不会它会立即释放资源代价是连接不优雅关闭。在不需要反复复用的调用场景里Abort 更安全。第二种方式是用 HttpClient 直接发 SOAP适合接口极少、又不想生成封装类的场景using var http new HttpClient(); var soap s:Envelope xmlns:shttp://schemas.xmlsoap.org/soap/envelope/ s:Body SubmitWorkOrder xmlnshttp://tempuri.org/ workOrderNoWO-001/workOrderNo /SubmitWorkOrder /s:Body /s:Envelope; var content new StringContent(soap, Encoding.UTF8, text/xml); var resp await http.PostAsync(http://192.168.1.50:9002/, content);SOAP 手拼最大的坑是命名空间。服务端的 targetNamespace 如果是 tempuri.org那么元素名前缀必须完全匹配少一个斜杠都会收到无法理解的消息。所以我建议能用 svcutil 就用 svcutil手拼 SOAP 只作为临时验证手段。4.4 把 WCF 服务包装成 WEB API给外部系统用的方案当外部系统不接受 SOAP只想要 JSON 时我们就在 WCF 外包一层 Web API。这层也叫防腐层作用是隔离下游契约变化让前端看到稳定的 REST 模型。[HttpPost(submit)] public async TaskIActionResult Submit([FromBody] SubmitRequest req) { var channel new MesDataServiceClient(); try { var items req.Items.Select(i new DeviceData { DeviceId i.DeviceId, Value i.RawValue, Timestamp i.Time }).ToList(); var ok channel.SubmitWorkOrder(req.WorkOrderNo, items); return Ok(new { code ok ? OK : REJECTED }); } catch (EndpointNotFoundException ex) { return StatusCode(502, new { code MES_UNAVAILABLE, detail ex.Message }); } finally { channel.Abort(); } }代码逻辑控制器的 Submit 接收前端 JSON映射成 WCF 数据契约然后调用 MES 服务。如果 WCF 服务没起来EndpointNotFoundException 会被捕获并转成 HTTP 502而不是让异常直接打到前端。finally 块里的 Abort 保证 Web API 实例不泄漏连接。这里有一个常见的架构问题Web API 和 WCF 共享了 DeviceData 数据契约是否应该在两个项目间引用同一个类库可以但我更推荐在 Web API 里定义独立的 DTO而不是直接引用 WCF 契约类。原因是可以解耦。WCF 的 DataContract 一旦变更Web API 的模型也可能被拖累独立 DTO 让 WCF 内部字段和外部 JSON 字段可以各自演进。代价是多写一段映射代码但长期维护会轻松很多。5. 接口开发避坑WCF 和 WEB 混用时的常见问题与排查5.1 现象WCF 连接超时与“由于目标计算机积极拒绝”Web API 调用 WCF 服务时最常见的异常是内层 SocketException由于目标计算机积极拒绝。这个提示看着吓人但多数是配置问题。我按下面的顺序排查先看宿主进程是否还活着再看端口是否被监听最后看客户端 endpoint 地址和宿主地址是否完全一致。用命令行查端口很直接netstat -ano | findstr 9001如果没有 LISTENING说明宿主没起来或者端口被其它程序占用。如果有 LISTENING则检查地址里的主机名localhost 和 IP 之间的差异比许多人想象的大。服务端配置监听 localhost客户端用机器名访问可能因为 DNS 解析到 IPv6 地址导致连接失败。解决办法是服务端和客户端都写明确的 IP 或机器名并且保证防火墙放行对应 TCP 端口。不要图省事把整个程序加白名单只放行监听的端口对生产环境的服务器来说更安全。5.2 现象返回的中文变成问号或乱码设备数据里常常带操作员姓名、工单描述一旦出现乱码系统的可信度立刻崩掉。乱码现象有两种一种是全程问号说明字符根本不被识别另一种是“锟斤拷”这类经典替换字符说明 UTF-8 字节被当成 GBK 解析了。原因集中在编码声明不一致。WCF 服务端如果使用 BasicHttpBinding默认文本编码是 UTF-8客户端如果按 GBK 生成 SOAP 报文服务端解析出来的字段就废了。解决方法是把两端的编码统一成 UTF-8服务端在创建绑定时显式声明var binding new BasicHttpBinding { TextEncoding System.Text.Encoding.UTF8 };同一方案里Web API 端也要统一 UTF-8。ASP.NET Core 默认 JSON 输出 UTF-8反而没问题。真正麻烦的是老数据库、老上位机程序用 GBK 存数据读出来再序列化前端看到的是 UTF-8 字节但数据库原始就是乱的这种必须把上游数据洗一遍。乱码不是玄学它就是“你以为的编码”和“实际使用的编码”不一致。5.3 现象老客户端反序列化失败服务端升级给 DeviceData 增加了一个新字段结果老客户端调用直接抛 InvalidDataContractException甚至数据串位。原因在于 WCF 数据契约虽然有版本兼容策略但顺序和必选属性没控制好。规避手段有三个第一所有 DataMember 从一开始就标上 Order新增字段追加到末尾第二新字段不要设 IsRequiredtrue否则老客户端如果发送空值服务端会认为请求不合法第三不要修改已有方法签名而是新增 OperationContract 方法发布新版本。如果你直接给旧接口添加返回字段老客户端在解析时遇到不认识的 XML 节点严格模式下就会失败。接口开发不是一锤子买卖契约版本管理要当作长期投资。5.4 现象HTTPS 调用时证书错误把 WCF 端点改成 HTTPS 后Web API 调用时经常报“证书链是由不受信任的颁发机构颁发的”。开发阶段用自签名证书联调很常见但生产环境不能把校验收掉。解决思路分两类。开发环境可以在客户端配置证书校验模式为 PeerTrust或者写一个回调函数校验服务端证书的指纹再放行。正式环境应由运维申请域名匹配的证书并在 WCF 绑定里设置 security mode 为 TransportWithMessageCredential客户端看到的就是标准 HTTPS 加消息层认证。很多人图省事在生产环境也把校验收掉这样的接口等于裸奔。设备数据涉及工艺参数和人员信息接口安全不能靠侥幸。实在要临时排查证书问题可以先在浏览器里访问 HTTPS 地址看证书是否受信而不是整个链路去猜。5.5 现象对象循环引用导致序列化失败或性能骤降Web API 直接返回 EF Core 实体时报循环引用是家常便饭。原因是导航属性形成了父子互引JSON 序列化器会无限递归。WCF 这边的大数据问题则更隐蔽传输超过默认 64KB 的消息会被直接拒绝。解决循环引用和最直接的办法是返回 DTO不要把实体扔给前端。实体的导航属性留给服务层内部用对外输出一律用扁平结构。在 MVC 配置里即便有引用处理选项也只是修补表现底层查询的开销还在。WCF 大数据量则要调整绑定上的消息大小限制binding nameLargeDataTcp maxReceivedMessageSize10485760 maxBufferSize10485760 /这里是一次性允许 10MB 消息单条消息太大时再往上调。但调大不等于高效超过 5MB 的 WCF 响应就该考虑分页或压缩。我曾经遇到过一个接口返回 3 万条历史数据慢到前端直接超时后来改成按小时分页查询问题立刻缓解。性能问题大多数不是框架不行而是请求边界没设计好。6. WCF 和 WEB 接口开发进阶验证与调试技巧6.1 用 WCF 服务跟踪查看器定位请求细节WCF 自带的 svctraceviewer.exe 在 .NET 工具目录里配合配置文件可以记录每次请求的 SOAP 报文和活动事件。我通常在开发环境开 Verbose 级日志线上只开 Warning。system.diagnostics sources source nameSystem.ServiceModel switchValueVerbose, ActivityTracing listeners add nametrace typeSystem.Diagnostics.TextWriterTraceListener initializeDataD:\logs\wcf.svclog / /listeners /source /sources /system.diagnostics日志文件用 svctraceviewer 打开左侧可以看到请求链路右侧看到消息正文。排查中文乱码时直接看报文里的字符串显示就能判断是序列化阶段的问题还是业务层的问题。注意生产环境不要开 Verbose不然几小时就能写满磁盘。6.2 用 PowerShell 验证 Web API 连通性每次写完 Web API 接口我不用急着打开浏览器先用 PowerShell 打一发$body { workOrderNo WO-001 items ({ deviceId A001; rawValue 12.3 }) } | ConvertTo-Json Invoke-RestMethod -Uri https://localhost:5001/api/device/submit -Method Post -ContentType application/json -Body $bodyInvoke-RestMethod 会自动把返回 JSON 转成对象方便直接看 code 字段。返回非 2xx 时会抛异常如果只想看状态码加 -SkipHttpErrorCheck 再输出 Response 内容。这个方法对前后端联调非常省事不需要写任何 C# 测试代码。6.3 手写 ChannelFactory 做免生成调用当一个 Web 项目不方便添加服务引用时可以用 ChannelFactory 硬编码契约来调用 WCF。前提是你必须和 WCF 服务端共享同一个服务契约接口或者手工写一个相同的接口。var binding new NetTcpBinding(); var factory new ChannelFactoryIMesDataService(binding, net.tcp://192.168.1.50:9001/); var channel factory.CreateChannel(); var result channel.SubmitWorkOrder(WO-001, items);这种做法的好处是工程文件干净不依赖 svcutil 生成的庞大客户端代码。坏处是契约变更时没有编译期警告如果服务端加了方法这里只有运行时异常。所以我只在已经注册到 DI 容器、且接口变更不频繁的内部项目里用。回到开始时的问题WCF 和 Web 项目接口开发到底怎么落地我的答案是画清边界、守住契约。每次联调前先把地址、端口、编码、契约顺序这四项列在纸上然后按照“宿主 → 客户端 → 数据内容 → 并发”的层次逐层验证大部分报错都能在半小时内定位。调试 WCF 和 Web 混用项目真正让人头疼的往往不是某段代码而是两边配置各自为政。多花一点时间把配置统一成 UTF-8、把字段顺序固定下来、把生产环境日志级别设好后面会省下大量半夜救火的精力。希望帮到你。本文还有配套的精品资源点击获取
返回列表