. NET architecture tips - reflection, architect magic weapon I

Posted by kc11 on Sun, 30 Jan 2022 19:31:33 +0100

For example, this is my voice. The frequency of reflection in my development is still relatively high. It feels like a lot of money. A piece of complex code can save a lot of time; However, it brings a problem that the performance is relatively poor, so we should choose a suitable scenario to use.

The basic usage of reflection in C # is not described in detail here. It is described in detail on the official website.

The following is a case of finding a medical insurance interface on the Internet. For example, there are two business interfaces, 033027. The transmission content of this interface is similar to xml, but it is different, not in the strict sense of xml format.

Hospital visit card interface specification (business: 033)

Input parameters:

<Request>
    <TradeCode>Business number</TradeCode>
    <BeginDate>Start date</BeginDate>
    <EndDate>End date</EndDate>
</Request>

Output parameters:

<Response>
    <PatientId>Medical record No</PatientId>
    <SiHisOrderNo>HIS End settlement flow</SiHisOrderNo>
    <PubCost>Overall payment of medical insurance (yuan)</PubCost>
    <PayCost>Medical insurance account payment (yuan)</PayCost>
    <OwnCost>Individual self payment of patients (yuan)</OwnCost>
    <TotCost>Total amount of this settlement (yuan)</TotCost>
    <TransType>Settlement category (1 consumption, 0 refund)</TransType>
    <OperCode>Operator number</OperCode>
    <OperName>Name of operator</OperName>
    <PayType>Payment method</PayType>
    <Invoices>
        <INum>Document No</INum>
        <IType>Document Categories </InvoiceType>
        <ISum>Document amount (yuan)</ISum>
    </Invoices>
</Request>

Hospital visit card interface specification (business: 027)

Input parameters:

<Request>
  <TradeCode>Transaction code(See the above table for transaction codes)</TradeCode>
  <Date>Transaction date( YYYYMMDD)</Date>
  <Time>Trading time( HHMMSS)</Time>
  <InvoiceNo>Invoice No</InvoiceNo>
  <TransType>Transaction category</TransType>
</Request>

Output parameters:

<Response>
    <TradeCode>Business number</TradeCode>
    <Result>Return value:0 Success, other failures</Result>
    <Err>Error description information</Err>
    <HospitalTransNO>Current transaction flow</HospitalTransNO>
    <Fees>
        <Fee>
            <fybm>Charge item code</fybm>
            <fymc>Charge item name</fymc>
            <ksbm>Issuing department code</ksbm>
            <ksmc>Name of issuing department</ksmc>
            <ysbm>Issuing doctor code</ysbm>
            <ysxm>Name of issuing doctor</ysxm>
            <kdsj>Billing time(yyyy-MM-dd HH:mm:ss)</kdsj>
            <sfsj>Charging time(yyyy-MM-dd HH:mm:ss)</sfsj>
            <fysl>quantity</fysl>
            <yxbz>Valid flag</yxbz>
            <czy>Toll collector No</czy>
            <zxks>Execution department number</zxks>
            <InvoiceNo>Document No</InvoiceNo>
        </Fee>
        ......
    </Fees>
</Response>

The connection mode of this medical insurance interface is that the input parameter is a Request node and the output parameter is a Response node. At least calling dll or sending and receiving parameters through what protocol is another matter. So let's focus on the architecture tips.

The first thing to think about is that we are net is oriented to corresponding programming. The input parameters and output parameters of these interfaces should correspond to entities. Of course, string splicing can also be used. However, when there are many interfaces, splicing bit by bit is bound to have a high error rate. At the same time, personalized processing is very troublesome. On the other hand, it is relatively low. At this time, the opportunity for reflection to show its skill comes. If I just define the entity class according to the attributes of input parameters and output parameters, and recreate an artifact, I can convert the entity into input parameter string and output parameters into entity class. For us, it is complete OOP, "two conversion methods for hard work and all interfaces for happiness", Of course, another difficulty in developing a complete medical insurance interface is to organize input parameter entity classes and write back output parameter entities from the existing system, but it is not the focus of architecture skills.

Let's go to the code:

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>
        /// Analog input function and output xml Corresponding set for mock
        /// </summary>
        static Dictionary<string, string> BackDic = new Dictionary<string, string> {
            {"ReadCard033" ,@"<Response>
    <PatientId>Medical record No</PatientId>
    <SiHisOrderNo>HIS End settlement flow</SiHisOrderNo>
    <PubCost>1</PubCost>
    <PayCost>2</PayCost>
    <OwnCost>3</OwnCost>
    <TotCost>4</TotCost>
    <TransType>1</TransType>
    <OperCode>Operator number</OperCode>
    <OperName>Name of operator</OperName>
    <PayType>Payment method</PayType>
    <Invoices>
        <INum>Document No</INum>
        <InvoiceType>Document Categories </InvoiceType>
        <ISum>5</ISum>
    </Invoices>
</Response>"},
            { "ReadCard027",$@"<Response>
    <TradeCode>Business number</TradeCode>
    <Result>0</Result>
    <Err>Error description information</Err>
    <HospitalTransNO>Current transaction flow</HospitalTransNO>
    <Fees>
        <Fee>
            <fybm>Charge item code</fybm>
            <fymc>Charge item name</fymc>
            <ksbm>Issuing department code</ksbm>
            <ksmc>Name of issuing department</ksmc>
            <ysbm>Issuing doctor code</ysbm>
            <ysxm>Name of issuing doctor</ysxm>
            <kdsj>2020-12-12 13:14:15</kdsj>
            <sfsj>2020-12-12 13:14:15</sfsj>
            <fysl>10</fysl>
            <yxbz>Valid flag</yxbz>
            <czy>Toll collector No</czy>
            <zxks>Execution department number</zxks>
            <InvoiceNo>Document No</InvoiceNo>
        </Fee> 
        <Fee>
            <fybm>Charge item code</fybm>
            <fymc>Charge item name</fymc>
            <ksbm>Issuing department code</ksbm>
            <ksmc>Name of issuing department</ksmc>
            <ysbm>Issuing doctor code</ysbm>
            <ysxm>Name of issuing doctor</ysxm>
            <kdsj>2020-12-12 13:14:15</kdsj>
            <sfsj>2020-12-12 13:14:15</sfsj>
            <fysl>10</fysl>
            <yxbz>Valid flag</yxbz>
            <czy>Toll collector No</czy>
            <zxks>Execution department number</zxks>
            <InvoiceNo>Document No</InvoiceNo>
        </Fee> 
    </Fees>
</Response>"}
        };

        /// <summary>
        /// Encapsulation call interface
        /// </summary>
        /// <typeparam name="T">Output parameter type</typeparam>
        /// <param name="request">input parameter </param>
        /// <returns></returns>
        static T Send<T>(Request request) where T : class
        {
            Console.WriteLine("Input parameters:");
            Console.WriteLine(request.ToXML());

            var backXML = BackDic[request.GetType().Name];
            return request.ToResponse(typeof(T), backXML) as T;
        }
    }
    /// <summary>
    /// Request parent class
    /// </summary>
    abstract class Request
    {
        public override string ToString()
        {
            var requestSB = new StringBuilder();
            var type = this.GetType();
            //Traverse the attribute to get the attribute value
            foreach (var pro in type.GetProperties())
            {
                //handle Request Subclass of, all input parameters should inherit Request
                if (pro.PropertyType.IsSubclassOf(typeof(Request)))
                {
                    requestSB.AppendLine($"<{pro.Name}>");
                    requestSB.AppendLine($"{pro.GetValue(this)}");
                    requestSB.AppendLine($"</{pro.Name}>");
                }
                else
                {
                    //handle DateTime Type properties
                    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>
        /// Input into xml input parameter 
        /// </summary>
        /// <returns></returns>
        public string ToXML()
        {
            return $"<Request>\n{this}\n</Request>";
        }
        /// <summary>
        /// Output parameters xml Convert to entity class
        /// </summary>
        /// <param name="type">Output parameter type</param>
        /// <param name="xml">Output parameters 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())
            {
                //User defined entity class attributes can be used to solve this problem in the same namespace
                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
                {
                    //Generic collection entity class properties
                    if (pro.PropertyType.IsGenericType)
                    {
                        //Gets the type of the generic collection property
                        var subType = pro.PropertyType.GetGenericArguments()[0];
                        var xmlElement = xmlDoc.GetElementsByTagName(pro.Name);
                        if (xmlElement.Count > 0)
                        {
                            //Generate entity classes for generic collection properties
                            var list = Activator.CreateInstance(pro.PropertyType) as IList;
                            //Add the list corresponding data in the output string to the generic property collection
                            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);
                                }
                            }
                            //Sets the value of the generic collection property
                            pro.SetValue(instance, list);
                        }
                    }
                    else
                    {
                        //General properties
                        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>
    /// Hospital visit card interface specification (business: 033)
    /// </summary>
    class ReadCard033 : Request
    {
        /// <summary>
        /// Business number
        /// </summary>
        public string TradeCode { get; set; }
        /// <summary>
        /// Start date
        /// </summary>
        public DateTime BeginDate { get; set; }
        /// <summary>
        /// End date
        /// </summary>
        public string EndDate { get; set; }

    }
    /// <summary>
    /// Output parameters of hospital visit card interface specification (business: 033)
    /// </summary>
    class ReadCard033Back
    {
        /// <summary>
        /// Medical record No
        /// </summary>
        public string PatientId { get; set; }

        /// <summary>
        /// HIS End settlement flow
        /// </summary>
        public string SiHisOrderNo { get; set; }
        /// <summary>
        /// Overall payment of medical insurance (yuan)
        /// </summary>
        public decimal PubCost { get; set; }
        /// <summary>
        /// Medical insurance account payment (yuan)
        /// </summary>
        public decimal PayCost { get; set; }
        /// <summary>
        /// Individual self payment of patients (yuan)
        /// </summary>
        public decimal OwnCost { get; set; }
        /// <summary>
        /// Total amount of this settlement (yuan)
        /// </summary>
        public decimal TotCost { get; set; }
        /// <summary>
        /// Settlement category (1 consumption, 0 refund)
        /// </summary>
        public int TransType { get; set; }
        /// <summary>
        /// Operator number
        /// </summary>
        public string OperCode { get; set; }
        /// <summary>
        /// Name of operator
        /// </summary>
        public string OperName { get; set; }
        /// <summary>
        /// Payment method
        /// </summary>
        public string PayType { get; set; }
        /// <summary>
        /// Document information
        /// </summary>
        public Invoices Invoices { get; set; }

    }
    /// <summary>
    /// bill
    /// </summary>
    class Invoices
    {
        /// <summary>
        /// Document No
        /// </summary>
        public string INum { get; set; }
        /// <summary>
        /// Document Categories 
        /// </summary>
        public string InvoiceType { get; set; }
        /// <summary>
        /// Document amount (yuan)
        /// </summary>
        public decimal ISum { get; set; }
    }
    /// <summary>
    /// Hospital visit card interface specification (business: 027)
    /// </summary>
    class ReadCard027 : Request
    {
        /// <summary>
        /// Transaction code(See the above table for transaction codes)
        /// </summary>
        public string TradeCode { get; set; }
        /// <summary>
        /// Transaction date( YYYYMMDD
        /// </summary>
        public string Date { get; set; }
        /// <summary>
        /// Trading time( HHMMSS)
        /// </summary>
        public string Time { get; set; }
        /// <summary>
        /// Invoice No
        /// </summary>
        public string InvoiceNo { get; set; }
        /// <summary>
        /// Transaction category
        /// </summary>
        public string TransType { get; set; }
    }
    /// <summary>
    /// Output parameters of hospital visit card interface specification (business: 027)
    /// </summary>
    class ReadCard027Back
    {
        /// <summary>
        /// Business number
        /// </summary>
        public string TradeCode { get; set; }
        /// <summary>
        /// Return value: 0 succeeded, others failed
        /// </summary>
        public string Result { get; set; }
        /// <summary>
        /// Error description information
        /// </summary>
        public string Err { get; set; }
        /// <summary>
        /// Current transaction flow
        /// </summary>
        public string HospitalTransNO { get; set; }
        /// <summary>
        /// Detailed list
        /// </summary>
        public List<Fee> Fees { get; set; }

    }
    /// <summary>
    /// detailed
    /// </summary>
    class Fee
    {
        /// <summary>
        /// Charge item code
        /// </summary>
        public string fybm { get; set; }
        /// <summary>
        /// Charge item name
        /// </summary>
        public string fymc { get; set; }
        /// <summary>
        /// Issuing department code
        /// </summary>
        public string ksbm { get; set; }
        /// <summary>
        /// Name of issuing department
        /// </summary>
        public string ksmc { get; set; }
        /// <summary>
        /// Issuing doctor code
        /// </summary>
        public string ysbm { get; set; }
        /// <summary>
        /// Name of issuing doctor
        /// </summary>
        public string ysxm { get; set; }
        /// <summary>
        /// Billing time(yyyy-MM-dd HH:mm:ss)
        /// </summary>
        public string kdsj { get; set; }
        /// <summary>
        /// Charging time(yyyy-MM-dd HH:mm:ss)
        /// </summary>
        public string sfsj { get; set; }
        /// <summary>
        /// quantity
        /// </summary>
        public string fysl { get; set; }
        /// <summary>
        /// Valid flag
        /// </summary>
        public string yxbz { get; set; }
        /// <summary>
        /// Toll collector No
        /// </summary>
        public string czy { get; set; }
        /// <summary>
        /// Execution department number
        /// </summary>
        public string zxks { get; set; }
        /// <summary>
        /// Document No
        /// </summary>
        public string InvoiceNo { get; set; }
    }
}

In the above code, the focus is on the abstract class Request. In fact, the original ToXML converts the entity class into an input parameter string, and ToResponse converts the output string into an entity for program use. The specific implementation of these two methods is annotated in the code, which uses reflection attributes to realize the conversion. Of course, this conversion can also be realized by XmlSerializer, but in this way, the flexible control of attributes is lost, such as the conversion of time type attributes in demo.

"Two conversion methods and all interfaces of happiness" are ToXML and ToResponse. If there are dozens of interfaces in this medical insurance, you can only define the input parameters and output parameter entity classes corresponding to each business function. In this way, the coupling of program modules is simplified and the level is clear, but you may be asking about performance. Indeed, reflection brings flexibility and performance will be lost at the same time, but most medical insurance interfaces exist in the form of dll, which is used in conjunction with his system, that is, there is only one interface calling at a time, and the tolerance for performance is enough.

 
You can pay more attention to WeChat official account if you want to know more quickly and conveniently.
 

 

 

Topics: .NET