.NET架构小技巧(3)——反射,架构人员法宝I

如题,这是我的心声,反射在我的开发中用的频次还是比较高的,有一本万利的感觉,一段复杂的代码,可以节省大量的时间;但带来的一个问题性能相对较差,所以要选择适合的场景使用。

关于C#中的反射基本用法,这里不作详细介绍,官网有详细的说明。

下面是在网上找了一个医保接口的案件,比如有两个业务接口,033,027,这个接口的传输内容类似xml,但又是有区别的,不是严格意义上的xml格式。

医院就诊卡接口规范(业务:033)

输入参数:

<Request><TradeCode>业务编号</TradeCode><BeginDate>开始日期</BeginDate><EndDate>结束日期</EndDate>
</Request>

输出参数:

<Response><PatientId>病历编号</PatientId><SiHisOrderNo>HIS端结算流水</SiHisOrderNo><PubCost>医保统筹支付(元)</PubCost><PayCost>医保帐户支付(元)</PayCost><OwnCost>患者个人自付(元)</OwnCost><TotCost>本次结算总额(元)</TotCost><TransType>结算类别(1消费,0退费)</TransType><OperCode>操作人员编号</OperCode><OperName>操作人员姓名</OperName><PayType>支付方式</PayType><Invoices><INum>单据编号</INum><IType>单据类别</InvoiceType><ISum>单据金额(元)</ISum></Invoices>
</Request>

医院就诊卡接口规范(业务:027)

输入参数:

<Request><TradeCode>交易码(见上表交易代码)</TradeCode><Date>交易日期(YYYYMMDD)</Date><Time>交易时间(HHMMSS)</Time><InvoiceNo>发票号</InvoiceNo><TransType>交易类别</TransType>
</Request>

输出参数:

<Response><TradeCode>业务编号</TradeCode><Result>返回值:0 成功,其他失败</Result><Err>错误描述信息</Err><HospitalTransNO>本次交易流水</HospitalTransNO><Fees><Fee><fybm>收费项目编码</fybm><fymc>收费项目名称</fymc><ksbm>开立科室编码</ksbm><ksmc>开立科室名称</ksmc><ysbm>开立医生编码</ysbm><ysxm>开立医生姓名</ysxm><kdsj>开单时间(yyyy-MM-dd HH:mm:ss)</kdsj><sfsj>收费时间(yyyy-MM-dd HH:mm:ss)</sfsj><fysl>数量</fysl><yxbz>有效标志</yxbz><czy>收费人员编号</czy><zxks>执行科室编号</zxks><InvoiceNo>单据编号</InvoiceNo></Fee>......</Fees>
</Response>

这个医保接口的对接方式入参都是一个Request节点,出参都是Response节点,至少是调用dll,还是通过什么协议发送参数,接收参数,是另外一会事,所以咱们重点来说架构小技巧。

首先思考的是,我们在.net中是面向对应编程,这些接口的输入参数,输出参数应该对应成实体,当然用字符串拼接也可以,但当接口众多时,一点一点拼接,势必会出错率高,同时个性化处理非常麻烦,另一方面,显得比较low。这时,反射大显身手的机会就来了,假如我只要按输入参数,输出参数的属性定义好实体类,再造一个神器,能把实体转成输入参数字符串,也能把输出参数转成实体类,对我们来说,就是完全的OOP了,“辛苦两个转换方法,幸福所有接口”,当然对开发一个完整的医保接口来说,另一个难点是,从现有系统中组织输入参数实体类,和回写输出参数实体,但也不是架构技巧的重点。

还是上代码吧:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Xml;namespace ArchitectureDemo03
{class Program{static void Main(string[] args){var readCard033 = new ReadCard033{TradeCode = "033",BeginDate = DateTime.Now,EndDate = "20200102"};var readcard033Back = Send<ReadCard033Back>(readCard033);Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(readcard033Back));var readCard027 = new ReadCard027{Date = "20201212",InvoiceNo = "abcd",Time = "121212",TradeCode = "003",TransType = "123"};var readCard027Back = Send<ReadCard027Back>(readCard027);Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(readCard027Back));}/// <summary>/// 模拟输入函数与输出xml对应集合,以作mock/// </summary>static Dictionary<string, string> BackDic = new Dictionary<string, string> {{"ReadCard033" ,@"<Response><PatientId>病历编号</PatientId><SiHisOrderNo>HIS端结算流水</SiHisOrderNo><PubCost>1</PubCost><PayCost>2</PayCost><OwnCost>3</OwnCost><TotCost>4</TotCost><TransType>1</TransType><OperCode>操作人员编号</OperCode><OperName>操作人员姓名</OperName><PayType>支付方式</PayType><Invoices><INum>单据编号</INum><InvoiceType>单据类别</InvoiceType><ISum>5</ISum></Invoices>
</Response>"},{ "ReadCard027",$@"<Response><TradeCode>业务编号</TradeCode><Result>0</Result><Err>错误描述信息</Err><HospitalTransNO>本次交易流水</HospitalTransNO><Fees><Fee><fybm>收费项目编码</fybm><fymc>收费项目名称</fymc><ksbm>开立科室编码</ksbm><ksmc>开立科室名称</ksmc><ysbm>开立医生编码</ysbm><ysxm>开立医生姓名</ysxm><kdsj>2020-12-12 13:14:15</kdsj><sfsj>2020-12-12 13:14:15</sfsj><fysl>10</fysl><yxbz>有效标志</yxbz><czy>收费人员编号</czy><zxks>执行科室编号</zxks><InvoiceNo>单据编号</InvoiceNo></Fee> <Fee><fybm>收费项目编码</fybm><fymc>收费项目名称</fymc><ksbm>开立科室编码</ksbm><ksmc>开立科室名称</ksmc><ysbm>开立医生编码</ysbm><ysxm>开立医生姓名</ysxm><kdsj>2020-12-12 13:14:15</kdsj><sfsj>2020-12-12 13:14:15</sfsj><fysl>10</fysl><yxbz>有效标志</yxbz><czy>收费人员编号</czy><zxks>执行科室编号</zxks><InvoiceNo>单据编号</InvoiceNo></Fee> </Fees>
</Response>"}};/// <summary>/// 封装调用接口/// </summary>/// <typeparam name="T">输出参数类型</typeparam>/// <param name="request">输入参数</param>/// <returns></returns>static T Send<T>(Request request) where T : class{Console.WriteLine("输入参数:");Console.WriteLine(request.ToXML());var backXML = BackDic[request.GetType().Name];return request.ToResponse(typeof(T), backXML) as T;}}/// <summary>/// 请求父类/// </summary>abstract class Request{public override string ToString(){var requestSB = new StringBuilder();var type = this.GetType();//遍历属性,获取属性值foreach (var pro in type.GetProperties()){//处理Request的子类,所有输入参数都应该继承Requestif (pro.PropertyType.IsSubclassOf(typeof(Request))){requestSB.AppendLine($"<{pro.Name}>");requestSB.AppendLine($"{pro.GetValue(this)}");requestSB.AppendLine($"</{pro.Name}>");}else{//处理DateTime类型属性if (pro.PropertyType.IsAssignableFrom(typeof(DateTime))){var value = Convert.ToDateTime(pro.GetValue(this)).ToString("yyyyMMddHHMMSS");requestSB.AppendLine($"<{pro.Name}>{value}</{pro.Name}>");}else{requestSB.AppendLine($"<{pro.Name}>{pro.GetValue(this)}</{pro.Name}>");}}}return requestSB.ToString().Trim();}/// <summary>/// 输成xml输入参数/// </summary>/// <returns></returns>public string ToXML(){return $"<Request>\n{this}\n</Request>";}/// <summary>/// 输出参数xml转成实体类/// </summary>/// <param name="type">输出参数类型</param>/// <param name="xml">输出参数xml</param>/// <returns></returns>public object ToResponse(Type type, string xml){xml = $@"<?xml version=""1.0"" encoding=""utf-8""?>{xml}";var xmlDoc = new XmlDocument();xmlDoc.LoadXml(xml);var instance = Activator.CreateInstance(type);foreach (var pro in type.GetProperties()){//自定义实体类属性,利用在同一个命名空间里来解瘊这个事情if (pro.PropertyType.Namespace == this.GetType().Namespace){var xmlElement = xmlDoc.GetElementsByTagName(pro.Name);if (xmlElement.Count > 0){var response = ToResponse(pro.PropertyType, $"<{pro.Name}>{xmlElement[0].InnerXml}</{pro.Name}>");pro.SetValue(instance, response);}}else{//泛型集合实体类属性if (pro.PropertyType.IsGenericType){//获取泛型集合属性的类型var subType = pro.PropertyType.GetGenericArguments()[0];var xmlElement = xmlDoc.GetElementsByTagName(pro.Name);if (xmlElement.Count > 0){//生成泛型集合属性的实体类var list = Activator.CreateInstance(pro.PropertyType) as IList;//把输出字符串中的列表对应数据添加到泛型属性集合中foreach (XmlNode childItem in xmlElement[0].ChildNodes){if (childItem.ChildNodes.Count > 0){var subInstance = ToResponse(subType, $"<{subType.Name}>{xmlElement[0].ChildNodes[0].InnerXml}</{subType.Name}>");list.Add(subInstance);}}//设置泛型集合属性的值pro.SetValue(instance, list);}}else{//普通属性var xmlElement = xmlDoc.GetElementsByTagName(pro.Name);if (xmlElement.Count > 0){var value = Convert.ChangeType(xmlElement[0].InnerText, pro.PropertyType);pro.SetValue(instance, value);}}}}return instance;}}/// <summary>/// 医院就诊卡接口规范(业务:033)/// </summary>class ReadCard033 : Request{/// <summary>/// 业务编号/// </summary>public string TradeCode { get; set; }/// <summary>/// 开始日期/// </summary>public DateTime BeginDate { get; set; }/// <summary>/// 结束日期/// </summary>public string EndDate { get; set; }}/// <summary>/// 医院就诊卡接口规范(业务:033)输出参数/// </summary>class ReadCard033Back{/// <summary>/// 病历编号/// </summary>public string PatientId { get; set; }/// <summary>/// HIS端结算流水/// </summary>public string SiHisOrderNo { get; set; }/// <summary>/// 医保统筹支付(元)/// </summary>public decimal PubCost { get; set; }/// <summary>/// 医保帐户支付(元/// </summary>public decimal PayCost { get; set; }/// <summary>/// 患者个人自付(元)/// </summary>public decimal OwnCost { get; set; }/// <summary>/// 本次结算总额(元)/// </summary>public decimal TotCost { get; set; }/// <summary>/// 结算类别(1消费,0退费)/// </summary>public int TransType { get; set; }/// <summary>/// 操作人员编号/// </summary>public string OperCode { get; set; }/// <summary>/// 操作人员姓名/// </summary>public string OperName { get; set; }/// <summary>/// 支付方式/// </summary>public string PayType { get; set; }/// <summary>/// 单据信息/// </summary>public Invoices Invoices { get; set; }}/// <summary>/// 单据/// </summary>class Invoices{/// <summary>/// 单据编号/// </summary>public string INum { get; set; }/// <summary>/// 单据类别/// </summary>public string InvoiceType { get; set; }/// <summary>/// 单据金额(元)/// </summary>public decimal ISum { get; set; }}/// <summary>/// 医院就诊卡接口规范(业务:027)/// </summary>class ReadCard027 : Request{/// <summary>/// 交易码(见上表交易代码)/// </summary>public string TradeCode { get; set; }/// <summary>/// 交易日期(YYYYMMDD/// </summary>public string Date { get; set; }/// <summary>/// 交易时间(HHMMSS)/// </summary>public string Time { get; set; }/// <summary>/// 发票号/// </summary>public string InvoiceNo { get; set; }/// <summary>/// 交易类别/// </summary>public string TransType { get; set; }}/// <summary>/// 医院就诊卡接口规范(业务:027)输出参数/// </summary>class ReadCard027Back{/// <summary>/// 业务编号/// </summary>public string TradeCode { get; set; }/// <summary>/// 返回值:0 成功,其他失败/// </summary>public string Result { get; set; }/// <summary>/// 错误描述信息/// </summary>public string Err { get; set; }/// <summary>/// 本次交易流水/// </summary>public string HospitalTransNO { get; set; }/// <summary>/// 明细列表/// </summary>public List<Fee> Fees { get; set; }}/// <summary>/// 明细/// </summary>class Fee{/// <summary>/// 收费项目编码/// </summary>public string fybm { get; set; }/// <summary>/// 收费项目名称/// </summary>public string fymc { get; set; }/// <summary>/// 开立科室编码/// </summary>public string ksbm { get; set; }/// <summary>/// 开立科室名称/// </summary>public string ksmc { get; set; }/// <summary>/// 开立医生编码/// </summary>public string ysbm { get; set; }/// <summary>/// 开立医生姓名/// </summary>public string ysxm { get; set; }/// <summary>/// 开单时间(yyyy-MM-dd HH:mm:ss)/// </summary>public string kdsj { get; set; }/// <summary>/// 收费时间(yyyy-MM-dd HH:mm:ss)/// </summary>public string sfsj { get; set; }/// <summary>/// 数量/// </summary>public string fysl { get; set; }/// <summary>/// 有效标志/// </summary>public string yxbz { get; set; }/// <summary>/// 收费人员编号/// </summary>public string czy { get; set; }/// <summary>/// 执行科室编号/// </summary>public string zxks { get; set; }/// <summary>/// 单据编号/// </summary>public string InvoiceNo { get; set; }}
}

‍在上面代码中,重点是Request这个抽象类,其实原ToXML中把实体类转成输入参数字符串,ToResponse是把输出字符串转成实体供程序使用。这两个方法的具体实现,在代码中有注释,这是使用反射属性来实现转换的,当然这种转换,用XmlSerializer也可以实现,但这样就失去了对属性的灵活控制,比如demo中的时间类型属性的转换。

“辛苦两个转换方法,幸福所有接口”,就是ToXML和ToResponse,如果这个医保还有几十个接口,那就只用定义每个业务函数对应的输入参数,输出参数实体类就可以了。这样简化了程序模块的耦合,层次也很清晰,但你可能在问性能了,确实,反射带来灵活的同时就会损失性能,但大部分医保接口都是以dll的形式存在,和his系统配合使用,就是一个时刻只有一个接口在调用,对性能的容忍度还是够用的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/307165.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

米家扫地机器人充满电需要多长时间_米家扫地机器人充满电后能工作多久?

硬件及结构相关1、Q&#xff1a;米家扫地机器人的激光对小孩或宠物有伤害吗&#xff1f;A&#xff1a;米家扫地机器人通过了IEC 60825-1:2014激光安全等级Class 1的认证&#xff0c;不会对儿童或者宠物带来伤害请放心使用。2硬件及结构相关1、Q&#xff1a;米家扫地机器人的激光…

后端学习 - JavaWeb

技术体系 文章目录一 HTML1 网页的组成部分2 HTML 概述3 HTML 标签4 常用标签5 表单与表单的提交二 CSS1 语法格式2 使用方法三 JavaScript1 概述2 与 HTML 结合的两种方式3 变量类型及特殊值4 关系、逻辑运算5 数组6 函数7 事件8 DOM &#xff08;Document Object Model&#…

python中的while语句怎么居中_python基础之while语句操作

# i 0# while (i < 9):# print("i ----> ",i)# i i 1# print(i,"i即将大于或者等于9,while不在执行")#执行1-100的数字# a1 1# while a1 < 100:# print(a1,end" ")# a1 a1 1#执行100 -1 的数字# a1 100# while a1 > 0:# prin…

心想技术驱动业务,却在背道而驰

这里是Z哥的个人公众号每周五11&#xff1a;45 按时送达当然了&#xff0c;也会时不时加个餐&#xff5e;我的第「165」篇原创敬上大家好&#xff0c;我是Z哥。相信每一位真正的程序员心里都有这样一个念想&#xff1a;只要我的技术够牛&#xff0c;就能驱动业务的发展。但是往…

后端学习 - SpringMVC

文章目录一 SpringMVC 简介1 MVC2 SpringMVC3 创建第一个 SpringMVC 项目二 RequestMapping1 注解类与方法的区别2 value 属性3 method 属性4 params 属性5 headers 属性6 SpringMVC 支持路径中的占位符三 获取 Request 的一系列参数1 通过控制器方法的形参2 控制器方法形参 映…

hbase shell远程连接_hbase与phoenix集成

Phoenix是构建在HBase之上的关系型数据库层&#xff0c;作为内嵌的客户端JDBC驱动用以对HBase中的数据进行低延迟访问Phoenix会将用户编写的sql查询编译为一系列的scan操作&#xff0c;最终产生通用的JDBC结果集返回给客户端Phoenix可以看成是mysql准备安装包apache-phoenix-4.…

对精致码农大佬的 [理解 volatile 关键字] 文章结论的思考和寻找真相

一&#xff1a;背景1. 讲故事昨天在园里的编辑头条看到 精致码农大佬 写的一篇题为&#xff1a;[C#.NET 拾遗补漏]10&#xff1a;理解 volatile 关键字 (https://www.cnblogs.com/willick/p/13889006.html) 的文章&#xff0c;大概就是说在 多线程环境下&#xff0c;一个在debu…

后端学习 - SpringBoot

SpringBoot 是整合 Spring 技术栈的一站式框架&#xff0c;是简化 Spring 技术栈的快速开发脚手架约定大于配置 文章目录一 概述1 第一个 SpringBoot 项目2 SpringBoot 特性&#xff1a;依赖管理3 SpringBoot 特性&#xff1a;自动配置二 SpringBoot 的 IOC容器1 组件添加&…

centos rpm 安装 perl_Linux【常用软件安装篇】

摘要&#xff1a;本文介绍Linux常用的软件安装方式以及jdk、vim、mysql、tomcat、redis的安装过程。1 Linux常用软件安装方式常用方式有&#xff1a;rmp包安装、yum指令安装、源码包安装、解压免安装。1.1 rpm包安装rpm是Red-Hat Package Manager&#xff08;RPM软件包管理器&a…

日计不足涓滴成河-自定义响应结果格式化器

什么是响应结果响应结果就是&#xff0c;在客户端向服务器发出请求后&#xff0c;服务器根据客户端的请求参数&#xff0c;给出的结果&#xff0c;这就是一个完整的响应结果过程。响应的结果包含的内容非常多&#xff0c;主要的有 HTTP Status Code&#xff0c;Content-Type,Co…

docker 容器启动顺序_Docker容器启动时初始化Mysql数据库

1. 前言 Docker在开发中使用的越来越多了&#xff0c;最近搞了一个Spring Boot应用&#xff0c;为了方便部署将Mysql也放在Docker中运行。那么怎么初始化 SQL脚本以及数据呢&#xff1f; 我这里有两个传统方案。 第一种方案是在容器启动后手动导入&#xff0c;太low了不行。第二…

后端学习 - JVM(上)内存与垃圾回收

JVM 架构图 文章目录一 JVM 简介二 类加载子系统&#xff1a;1 作用2 类的三个加载过程3 类加载器的分类4 双亲委派机制 & Tomcat为何不遵循5 两个 class 对象为同一个类的必要条件三 运行时数据区&#xff1a;PC寄存器&#xff08;Program Counter Register&#xff09;四…

SM2 国密算法被 Linux 内核社区接受

喜欢就关注我们吧&#xff01;10 月 25 日&#xff0c;有开发者发文称&#xff0c;SM2 国密算法终于被 Linux 内核社区接受了。该作者表示&#xff0c;SM2 的补丁已经更新到了 v7 版本&#xff0c;这个版本的补丁最终被社区接受&#xff0c;目前已经合并到了 Linux 主线的 5.10…

后端学习 - MyBatis

MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的 持久层框架 文章目录一 基于配置文件的 MyBatis 搭建1 搭建过程&#xff08;增删改&#xff09;2 查询操作3 特殊操作二 MyBatis 获取参数值的方式1 单个字面量类型的参数2 多个字面量类型的参数3 Map 类型的参数4 实体…

向下兼容性格什么意思_担心对方只是向下兼容,并不是从心底里接纳我怎么办?...

无需刻意累计帮助了44人题主你好&#xff0c;不知道什么时候“向下兼容”这个词被用在了人际关系上&#xff0c;只就我个人所了解的话&#xff0c;这是一个很不严谨的用法。因为在自我接纳中用了“向下”本身就意味着不平等和不接纳。形容一个人“向下兼容”本身就自带了否认倾…

国产操作系统发展离不开人才和市场

日前&#xff0c;中国 1024 程序员节盛大举行&#xff0c;一大批开源大咖齐聚千年岳麓&#xff0c;围绕开源标准、生态、人才发展等主题分享&#xff0c;共议开源软件与操作系统未来。其中&#xff0c;统信软件总经理刘闻欢表示&#xff0c;“有了市场才会被真正的用起来”&…

zynq网络时钟控制寄存器_ZYNQ笔记(6):普通自定义IP封装实现PL精准定时中断...

软件的定时中断很难控制精准触发沿的位置&#xff0c;可以通过 PL-PS 的中断完成精准的定时中断。PL 的中断通过 Verilog 代码产生&#xff0c;这样紧密结合 PS-PL 的处理&#xff0c;发挥各自的优势。一、PL 侧定时中断1.实际要求① 上升沿中断&#xff1b;② 高电平宽度不小于…

后端学习 - Redis

文章目录一 Redis 概述Redis 为什么是单线程&#xff0c;单线程为什么这么快&#xff1f;数据存储结构二 常用数据类型1 String2 HashHash 的扩容机制&#xff1a;渐进式 rehash*3 List4 Set5 Zset三 Redis 事务1 乐观锁与 watch 命令2 事务的三个特性四 Redis 持久化1 RDB(Red…

再被补刀!Flash又遭抛弃,你会怀念它吗?

喜欢就关注我们吧&#xff01;微软近日发布通知&#xff0c;称更新了关于 Adobe Flash Player 的删除。微软更新目录站点可下载更新 KB4577586&#xff0c;用于删除 Flash Player。此更新适用于所有受支持的操作系统版本。重要版本 Windows 10 和 Windows 8.1 的可选更新也将在…

4位加法器的设计代码verilog_HDLBits:在线学习Verilog(六 · Problem 25-29)

本系列文章将和读者一起巡礼数字逻辑在线学习网站 HDLBits 的教程与习题&#xff0c;并附上解答和一些作者个人的理解&#xff0c;相信无论是想 7 分钟精通 Verilog&#xff0c;还是对 Verilog 和数电知识查漏补缺的同学&#xff0c;都能从中有所收获。附上传送门&#xff1a;M…