上一篇博文中,利用属性反射的特点,用两个方法完成了字符转实体,实体转字符的工作,但有些复杂的场景,上面方法就没那么好用了,就需要更复杂的方式来组装处理。

先来看一个接口文档,下面是接口的调用方式

long  OltpTransData(unsigned long msgType,unsigned long packageType,unsigned long packageLength,char *str,LPTSTR com);

I.msgType:业务请求类型;

II.packageType:数据解析格式类型,系统重组数据时使用

III.packageLength:数据串的长度;

IV. str:数据串;调用时,通过数据串传入参数;函数返回时,数据串中包含返回的数据,数据按字符串方式组合,并且在字符串第一位保留一个空格;另外,除了16位日期右补空格,其他的字段均以左空格补位

V. com:数据请求串口

业务请求类型

数据解析格式类型

数据串最小长度

说明

1001

101

126

实时验卡(读卡、验卡)

1002

12

610

实时结算

1003

7

291

实时明细数据传输

1004

9

253

实时住院登记数据传输

1005

8

309

实时医嘱数据传输

1006

12

610

实时结算预算

1007

2

1101

实时住院首次病程记录传输

1009

504

85

医师信息查询

1010

510

331

出入院标准传输

读卡

序号

定义

数据原型

起始位置

数据长度

备注

数据填充

1

个人编号

CHAR

1

8

医保编号,以’E’开头的为异地社保卡,详见说明

院端

2

姓名

CHAR

9

20

中心

3

身份证号

CHAR

29

18

18位或15位

中心

4

IC卡号

CHAR

47

9

院端

5

治疗序号

NUM

56

4

中心

6

职工就医类别

CHAR

60

1

A在职、B退休、L事业离休、T特诊、Q企业离休、E退老、N农民工、X未成年居民、O老年居民(老年居民、低收入人员、残疾人)、D低保人员、S  三无人员、U 大学生

中心

7

基本个人帐户余额

NUM

61

10

中心

8

补助个人帐户余额

NUM

71

10

现用于公务员单独列帐

中心

9

统筹累计

NUM

81

10

中心

10

门诊慢病统筹累计

NUM

91

10

中心

11

月缴费基数

NUM

101

10

月缴费工资

中心

12

帐户状态

CHAR

111

1

A正常、B半止付、C全止付、D销户

中心

13

参保类别1

CHAR

112

1

是否享受高额:

0 不享受高额、1  享受高额、2 医疗保险不可用

中心

14

参保类别2

CHAR

113

1

是否享受补助(商业补助、公务员补助):0  不享受、1 商业、2  公务员、3事业离休差额拨款人员

中心

15

参保类别3

CHAR

114

1

0 企保、1 事保、2企业慢病、3事业慢病、4异地就医,详见说明

中心

16

参保类别4

CHAR

115

1

0或2生育不可用、1或3生育可用

中心

17

参保类别5

CHAR

116

1

0工伤不可用、1工伤可用

中心

18

住院次数累计

NUM

117

4

中心

19

家床次数累计

NUM

121

4

中心

查询医师

序号

定义

数据原型

起始位置

数据长度

备注

填写方式

1

医师编码

CHAR

1

8

院端

2

医师姓名

CHAR

9

20

中心

3

身份证号

CHAR

29

18

中心

4

可出诊医院编号

CHAR

47

20

详见说明

中心

5

终止日期

DATETIME

67

16

YYYYMMDDHHMMSS

中心

6

有效标志

CHAR

83

1

‘0’无效,‘1’有效

中心

这个接口是拼接方式,每个数据项都有固定的长度,有些数据也有固定的格式,比如日期,还有很多类型,都是有固定值固定含义的。这种情况下需要更多的信息来告诉两个转换方法该怎么转换,这里就引出了Attribute——一个专门来给类或属性方法加特征的知识点。

    /// <summary>/// 发送报文类型/// </summary>[AttributeUsage(AttributeTargets.Class)]public class PackageTypeAttribute : Attribute{/// <summary>/// 发关报文实体类属性特性类/// </summary>/// <param name="SN">属性的序号,从1开始</param>/// <param name="Length">属性转成字符串后长度</param>public PackageTypeAttribute(uint OperationType, uint DataFormaterType, uint MinLength){this.OperationType = OperationType;this.DataFormaterType = DataFormaterType;this.MinLength = MinLength;}/// <summary>/// 业务请求类型/// </summary>public uint OperationType{ get; private set; }/// <summary>/// 数据解析格式类型/// </summary>public uint DataFormaterType{ get; private set; }/// <summary>/// 数据串最小长度/// </summary>public uint MinLength{ get; private set; }}/// <summary>/// 报文中属性的顺序SN和长度Length/// </summary>[AttributeUsage(AttributeTargets.Property)]public class PackageAttribute : Attribute{/// <summary>/// 序号,从1开始/// </summary>public int SN{ get; private set; }/// <summary>/// 转成字符串后的长度/// </summary>public int Length{ get; private set; }/// <summary>/// 发关报文实体类属性特性类/// </summary>/// <param name="SN">属性的序号,从1开始</param>/// <param name="Length">属性转成字符串后长度</param>public PackageAttribute(int SN, int Length){this.SN = SN;this.Length = Length;}/// <summary>/// 是否是时间类型,因为时间类型是左对齐,右补空格/// </summary>public bool IsDateTime{ get; set; }}/// <summary>/// 取枚举的值还是/// </summary>[AttributeUsage(AttributeTargets.Enum)]public class EnumValeuNumberAttribute : Attribute{/// <summary>/// 是否把枚举类型属性的的值数转成Char类型/// </summary>public bool IsChar{ get; set; }}

定义了三个特性类,PackageTypeAttribute主用来区分不同的交易类型,从实体类上获取不同交易的函数参数;PackageAttribute是在实体类的属性上的,是核心特性类,它标记了属性的序号,和每个属性的长度,和属性是否是DateTime类型;EnumValeuNumberAttribute是用来专门处理枚举类型的属性的。

    /// <summary>/// 医师信息查询/// </summary>[PackageType(1009, 504, 85)]public class DoctorQuery : Entity{/// <summary>/// 医师编码/// </summary>[Package(1, 8)]public virtual String DoctorCode{get; set;}/// <summary>/// 医师姓名/// </summary>[Package(2, 20)]public virtual String DoctorName{ get; set; }/// <summary>/// 身份证号/// </summary>[Package(3, 18)]public virtual string PersonID{ get; set; }///编号为0002的医院, 下属有编号为0113的定点, 在总院注册登记的医师可以在这样的2家医院出诊, 则“可出诊医院编号”为00020113,若长度不足20位则前补空格。/// <summary>/// 可出诊医院编号/// </summary>[Package(4, 20)]public virtual string CanVisitHospitalCode{ get; set; }/// <summary>/// 终止日期/// </summary>[Package(5, 16, IsDateTime = true)]public virtual string TerminationTime{ get; set; }/// <summary>/// 有效标志/// </summary>[Package(6, 1)]public virtual DLYBAvailableMarker DLYBAvailableMarker{ get; set; }}/// <summary>/// 有效标志/// </summary>[EnumValeuNumber]public enum DLYBAvailableMarker{/// <summary>/// 无效/// </summary>nullity = 0,/// <summary>/// 有效/// </summary>Valid = 1}/// <summary>/// 实时验卡(读卡、验卡)/// </summary>[PackageType(1001, 101, 126)] public  class QueryCardEntity : Entity{        /// <summary>/// 个人编号/// </summary>[Package(1, 8)]public virtual string PersonNumber{ get; set; }/// <summary>/// 姓名/// </summary>[Package(2, 20)]public virtual string Name{ get; set; }/// <summary>/// 身份证号/// </summary>[Package(3, 18)]public virtual string PersonID{ get; set; }/// <summary>/// IC卡号/// </summary>[Package(4, 9)]public virtual string ICCardNumber{ get; set; }long therapyNumber;/// <summary>/// 治疗序号/// </summary>[Package(5, 4)]public virtual long TherapyNumber{get{return therapyNumber;}set{if (value >= 0 && value <= 9999){therapyNumber = value;}else{throw new Exception("治疗号在0-9999之间");}}}/// <summary>/// 职工就医类别/// </summary>[Package(6, 1)]public virtual string TherapyCategory{ get; set; }/// <summary>/// 基本个人帐户余额/// </summary>[Package(7, 10)]public virtual decimal BasePersonBalance{ get; set; }/// <summary>/// 补助个人帐户余额/// </summary>[Package(8, 10)]public virtual decimal SubsidyPersonBalance{ get; set; }/// <summary>/// 统筹累计/// </summary>[Package(9, 10)]public virtual decimal PlannerTotal{ get; set; }/// <summary>/// 门诊慢病统筹累计///新/// </summary>[Package(10, 10)]public virtual decimal MZSlowDisease{ get; set; }/// <summary>/// 月缴费基数/// </summary>[Package(11, 10)]public virtual decimal BaseFeeByMonth{ get; set; }/// <summary>/// 帐户状态/// </summary>[Package(12, 1)]public virtual string AccoutState{ get; set; }/// <summary>/// 参保类别1/// </summary>[Package(13, 1)]public virtual string InsuranceCategory1{ get; set; }/// <summary>/// 参保类别2/// </summary>[Package(14, 1)]public virtual string InsuranceCategory2{ get; set; }/// <summary>/// 参保类别3/// </summary>[Package(15, 1)]public virtual string InsuranceCategory3{ get; set; }/// <summary>/// 参保类别4/// </summary>[Package(16, 1)]public virtual string InsuranceCategory4{ get; set; }/// <summary>/// 参保类别5/// </summary>[Package(17, 1)]public virtual string InsuranceCategory5{ get; set; }/// <summary>/// 住院次数累计新/// </summary>[Package(18, 4)]public virtual int ZYAddNumber{ get; set; }/// <summary>/// 家床次数累计新/// </summary>[Package(19, 4)]public virtual int AddBedNumber{ get; set; }}

上面的实体类分别使用了特性类,参照文档就OK。

    public static class StringExtension{  /// <summary>/// 右边不够长度补空格,汉字算两个空格/// </summary>/// <param name="str"></param>/// <param name="length">设定长度</param>/// <returns></returns>public static string ChineseCharacterLeft(this string str, int length){var len = Encoding.Default.GetBytes(str).Length;if (len < length){for (int i = 0; i < length - len; i++){str = " " + str;}}return str;}/// <summary>/// 右边不够长度补空格,汉字算两个空格/// </summary>/// <param name="str"></param>/// <param name="length">设定长度</param>/// <returns></returns>public static string ChineseCharacterRight(this string str, int length){var len = Encoding.Default.GetBytes(str).Length;if (len < length){for (int i = 0; i < length - len; i++){str += " ";}}return str;}/// <summary>/// 切除字符串/// </summary>public static string ChineseCharacterSubstring(this string str, int length, out string remaining){var arr = Encoding.Default.GetBytes(str);var barr = arr.Take(length).ToArray();var valuestr = Encoding.Default.GetString(barr);barr = arr.Skip(length).ToArray();remaining = Encoding.Default.GetString(barr); ;return valuestr;}}

‍上面代码是对某些属性的对齐方式作了处理。

    /// <summary>/// 报文类的父类/// </summary>public abstract class Entity{/// <summary>/// 组装发送报文格式/// </summary>/// <returns></returns>public override string ToString(){var pros = this.GetType().GetProperties();var sortPro = new SortedList<int, PropertyInfo>();  foreach (var pro in pros){foreach (var att in pro.GetCustomAttributes(false)){if (att is PackageAttribute){var packageAtt = att as PackageAttribute;   sortPro.Add(packageAtt.SN, pro);}}}var content = new StringBuilder();#region 组合发送字符串//遍历属性 foreach (var pro in sortPro){//遍历属性上的特性                foreach (var att in pro.Value.GetCustomAttributes(false)){//判断是否为自定义的PackageAttribute类型if (att is PackageAttribute){//转换属性上的特性类var packageAtt = att as PackageAttribute;//取拼接时字符长度var length = packageAtt.Length;//取属性的值var proValue = pro.Value.GetValue(this, new Object[0]);//对decimal作处理if (pro.Value.PropertyType.Name.ToLower() == "decimal"){proValue = Math.Round(Convert.ToDecimal(proValue), 2);if (Encoding.Default.GetByteCount(proValue.ToString()) > length){proValue = "0";}}//判断字符串长度过长if (proValue != null && (pro.Value.PropertyType.Name.ToLower() == "string")){if (System.Text.Encoding.Default.GetBytes(proValue.ToString()).Length > length){throw new Exception(string.Format("属性{0}的值{1},长度超过{2}", pro.Value.Name, proValue, length));}}//如果值为非空if (proValue != null){//日期是右补空格,其他是左补空格if (!packageAtt.IsDateTime){//这里注册,有些属性是枚举类型,有些属性拼接枚举的值,有些取枚举值对应的枚举数值,这里是从该属性类型上的EnumValeuNumberAttribute特性的IsValue属性来判断的,IsValue为true,就取枚举的值,为false取该值对应的枚举数if (pro.Value.PropertyType.IsEnum){foreach (var eatt in pro.Value.PropertyType.GetCustomAttributes(false)){if (eatt is EnumValeuNumberAttribute){var enumVaNu = eatt as EnumValeuNumberAttribute;if (enumVaNu.IsChar){var enumNumber = ((char)(int)Enum.Parse(pro.Value.PropertyType, proValue.ToString())).ToString();content.Append(enumNumber.ChineseCharacterLeft(length));}else{var enumNumber = ((int)Enum.Parse(pro.Value.PropertyType, proValue.ToString())).ToString();content.Append(enumNumber.ChineseCharacterLeft(length));}}}}else{content.Append(proValue.ToString().ChineseCharacterLeft(length));}}else//日期类型右补空格{content.Append(proValue.ToString().ChineseCharacterRight(length));}}else{content.Append("".ChineseCharacterLeft(length));}}}}#endregionreturn content.ToString();}/// <summary>/// 把一个字符串转成一个对象/// </summary>/// <param name="content"></param>/// <returns></returns>public  Entity ToEntity(Type entityType,string content){var pros = entityType.GetProperties();//按照特性类上的SN序号把属性名存入集合proPackageList中List<PropertyInfo> proPackageList = new List<PropertyInfo>(pros.Length);//初始化属性集合for (int i = 0; i < pros.Length; i++){foreach (var att in pros[i].GetCustomAttributes(false)){if (att is PackageAttribute){proPackageList.Add(null);break;}}}//按属性顺序排列属性foreach (var pro in pros){foreach (var att in pro.GetCustomAttributes(false)){if (att is PackageAttribute){var packageAtt = att as PackageAttribute;var index = packageAtt.SN - 1;proPackageList[index] = pro;}}}//创建实体对象var constructor = entityType.GetConstructor(new Type[0]);var entity = constructor.Invoke(new object[0]);foreach (var pro in proPackageList){//遍历属性上的特性foreach (var att in pro.GetCustomAttributes(false)){//判断是否为自定义的PackageAttribute类型if (att is PackageAttribute){//转换属性上的特性类var packageAtt = att as PackageAttribute;var length = packageAtt.Length;var valuestr = content.ChineseCharacterSubstring(length, out content).Trim();if (pro.PropertyType.IsEnum){foreach (var eatt in pro.PropertyType.GetCustomAttributes(false)){if (eatt is EnumValeuNumberAttribute){var eat = eatt as EnumValeuNumberAttribute;if (eat.IsChar){var chr = Convert.ToChar(valuestr);var value = Convert.ChangeType(Enum.Parse(pro.PropertyType, ((int)chr).ToString()), pro.PropertyType);pro.SetValue(entity, value, null);}else{var value = Convert.ChangeType(Enum.Parse(pro.PropertyType, valuestr), pro.PropertyType);pro.SetValue(entity, value, null);}break;}}}else{var value = Convert.ChangeType(valuestr, pro.PropertyType);pro.SetValue(entity, value, null);}}}}return (Entity)entity;}}

这两个方法核心里通过反射属性上的特性,取特性中定义的固定值,来生成接口要求的字符串,合理的设计特性,可以使两个转换方法更优雅,更简便,在开发过程中,也需要不断调整理,适配,逐渐完善。

可以用下面的代码完成测试

using System;
namespace ArchitectureDemo04
{class Program{static void Main(string[] args){var backQueryCard = Send(new QueryCardEntity { PersonNumber = "0000001", ICCardNumber = "C00000001" });var backDoctorQuery = Send(new DoctorQuery { DoctorCode = "0001" });}/// <summary>/// 发送/// </summary>/// <param name="entity"></param>/// <returns></returns>static Entity Send(Entity entity){try{foreach (var att in entity.GetType().GetCustomAttributes(false)){if (att is PackageTypeAttribute){var attPackage = att as PackageTypeAttribute;    Console.WriteLine($"入参:");Console.WriteLine(entity);Console.WriteLine("模拟函数调用:");Console.WriteLine($"OltpTransData({attPackage.OperationType},{attPackage.DataFormaterType},{attPackage.MinLength},{entity})");var backContent = BackOperation(entity);var backEntity = entity.ToEntity(entity.GetType(),backContent);return backEntity;}}return null;}catch{throw;}}/// <summary>/// 模拟医保中心返回/// </summary>/// <param name="entity">参数</param>/// <returns></returns>static string BackOperation(Entity entity){switch (entity.GetType().Name){case "QueryCardEntity":return " 0000001                Jack210213198411113111C00000001   1A   1000.66         0         0         0      1800A00131   0   0"; case "DoctorQuery":return "    0001            DcotorLi210211198707182233            0002011320201029190850  1";}return null;}}
}

.NET架构小技巧(4)——反射,架构人员法宝II相关推荐

  1. .NET架构小技巧(5)——反射,架构人员法宝III

    通过两篇博文,我们了解到,反射是通过非实例化(new)的手段来对对象和对象内的成员访问的,不仅仅如此,反射还可以突破访问修饰符的限制,以上帝视角来窥探对象内部全部成员(字段,属性,方法),包括priv ...

  2. .NET架构小技巧(6)——什么是好的架构

    首先声明,可能本篇文章的含金量配不上这个标题,因为说起架构,可能大家都比较关注高大上的架构,比如分布式的,高并发的,低耦合的,易扩展的等等,本篇可能使你失望了,因为这些全没有,这篇博客的中心思想是-- ...

  3. 如何设置打印的时候不加上页面链接_excel表格的这10个打印小技巧,办公室财务人员记得收藏...

    打印工作表是日常工作中最常见的一个工作,也是Excel在工作中最常用的一个地方,如何做到快速准确地打印文件呢.今天文理会计小编给大家从网上整理了几个打印文件的小技巧. 1.打印标题 打印的工作表的数据 ...

  4. .NET架构小技巧(7)——做好小的项目

    一屋不扫,何以扫天下.再说也没有那么多天下(大系统)可扫,更多的是一个个自己居住的小屋(手边的小项目,子模块),所以认真的开始扫自己的小屋. 在visual studio中,解决方案(Solution ...

  5. .NET架构小技巧(8)——优待异常

    天有不测风云,人有旦夕祸福,程序呢--会有异常错误. C#中用try,catch,finally来捕捉处理异常,捕捉谁的异常呢?一般都是系统类库或三方类库中抛出的异常,那如果我自己架构程序,异常也是要 ...

  6. .NET架构小技巧(2)——访问修饰符正确姿势

    在C#中,访问修饰符是使用频率很高的一组关键字,一共四个单词六个组合:public,internal,protected internal,protected,private protected,pr ...

  7. 一些你可能不知道的 Python 小技巧!

    [CSDN 编者按]Python 风头正盛,未来一段时间内想必也会是热门编程语言之一.因此,熟练掌握 Python 对开发者来说极其重要,说不定能给作为开发者的你带来意想不到的财富. 作者 | Seb ...

  8. 一个 15 年 SAP ABAP 开发人员分享的 SAPGUI 一些个性化设置和实用小技巧试读版

    零基础 ABAP 学习教程系列文章的目录 ABAP 标准培训教程 BC400 学习笔记之一:ABAP 服务器的架构和一个典型的 ABAP 程序结构介绍 ABAP 标准培训教程 BC400 学习笔记之二 ...

  9. 6000星人气深度学习资源!架构模型技巧全都有,图灵奖得主LeCun推荐

    铜灵 发自 凹非寺 量子位 出品 | 公众号 QbitAI 暑假即将到来,不用来充电学习岂不是亏大了. 有这么一份干货,汇集了机器学习架构和模型的经典知识点,还有各种TensorFlow和PyTorc ...

最新文章

  1. ApacheBench(ab)使用详解
  2. 如何正确清理Excel互操作对象?
  3. Mybatis各种模糊查询
  4. sklearn逻辑回归 极大似然 损失_收藏!攻克目标检测难点秘籍二,非极大值抑制与回归损失优化之路...
  5. 计算机组成与系统结构名词解释,北京邮电大学 计算机系统结构(体系结构) 期末复习 术语解释...
  6. 将传统 ASP.NET 应用迁移到 .NET Core
  7. jsp文字上下居中显示_微信朋友圈又有骚技巧,一键设置居中签名,好友傻眼了...
  8. BZOJ 3304: [Shoi2005]带限制的最长公共子序列( LCS )
  9. mysql分组查询n条记录
  10. linux 汇编语言ldreq,请教一个中断句柄的问题 - ARM技术论坛-ARM嵌入式论坛-人气最火爆ARM学习论坛 - 21ic电子技术开发论坛...
  11. Regularized least-squares classification(RLSC)
  12. linux用户和组管理的实验报告,LINUX实验三   用户管理
  13. CTO、首席架构师、技术总监、研发Leader、高级程序员的职责
  14. js 利用window.getSelection() 实现简单的文字标注(鼠标选中文本,给其加下滑线、背景色)
  15. 函数式编程对象Either
  16. 安装软件时出现解析包出现问题是什么原因
  17. 如何在网站集成Payssion的国外支付方式?
  18. /dev/sda1 is mounted:will not make a filesystem here!
  19. PDF怎么合并?这些合并方法你该知道
  20. 网络连接处出现红叉的解决方法

热门文章

  1. FallbackFactory启动的时候抛出异常
  2. 010 使用list和tuple
  3. 【第四周作业】参加项目开发之后的一些体会
  4. centos安装与配置R语言
  5. Java设计模式-状态模式(State)
  6. Wave 文件(5): 获取 Wave 文件的格式信息
  7. Teams App如何选择用户
  8. sci-hub谷歌插件_Google Home Hub具有隐藏屏幕设置菜单
  9. IT:如何在Windows Server 2008 R2上安装Hyper-V虚拟化
  10. 什么是“ rpcsvchost”,以及为什么它在Mac上运行?