首頁

目前文章總數:157 篇

  

最後更新:2024年 12月 07日

0032. 取得Class下的Property的Summary資訊方法,與轉換應用方式

日期:2022年 06月 23日

標籤: C# Asp.NET Core CSS XML

摘要:C# 學習筆記


應用所需:1. .netCore 6.0
     2. Visual Studio 2022以上
解決問題:1. 如何取得Class下的Property的Summary
     2. 應用取得Summary後的內容
     3. 強型別將Class ViewModel轉換的方法
範例檔案:連結※要回上一層Git clone
基本介紹:本篇分為4大部分。
第一部分:範例專案說明Class XML產生方式
第二部分:如何取得Class下的Property的Summary
第三部分:應用取得Summary後的內容
第四部分:強型別將Class ViewModel轉換的方法






第一部分:範例專案說明Class XML產生方式

Step 1:不能取得的Summary

以下圖為例,在.net中用反射是取不到ViewModel Class中Property的Summary
原因是這個Summary是紀錄於Xml中,而不是在建置階段時存於 .NET assembly
一種解法是將Xml檔案匯出到指定位址,讀取並做解析


Step 2:Visual Studio開啟專案屬性

對Visual Studio專案 -> 滑鼠右鍵 -> 屬性


Step 3:Visual Studio開啟專案屬性

依序選擇:”建置” -> “輸入” -> “產生包含API文件的檔案”打勾
打勾完成後”建置”專案,使其產生.xml檔案
※這是Visual Studio 2022 的畫面,每個版本屬性裡面多少會有差異


Step 4:到輸出的.Xml檔案位置

對Visual Studio專案 -> 滑鼠右鍵 -> 在檔案總管中開啟資料夾


Step 5:看到Xml檔案

檔案路徑會在Debug 或 Release 底下,這邊是Debug的路徑下

$\bin\Debug\net6.0-windows\GetClassAndPropertyDescriptionExample.xml




Step 6:說明XML與Class關係

開啟XML檔案後,與原範例專案比較可以發現Class內的所有Summary資訊都可以匹配到




第二部分:如何取得Class下的Property的Summary

Step 1:代碼範例中的專案架構

範例專案架構如下,檔案3個
1. 主程式:所有的轉換邏輯與取得路徑與畫面顯示皆在此檔案
2. 原Class:ViewMode含有的Property與Summary
3. 改Class:在第3部分應用章節用到,為對應原Class ViewModel的Property與Summary


Step 2:Demo WinForm介面 - 1.查詢按鈕

介面如下,主要有3個區塊
1. 輸入ClassName:輸入ViewModel的完整路徑,才可以查出Class
2. 功能區塊:主要有3個功能,第二部分會介紹 “1.查詢” 功能
3. 輸出結果:執行的結果會顯示在此


Step 3:Demo WinForm介面 - 使用方法

開啟範例檔案中的Class檔案,取得 命名空間 + ClassName
並且組成後貼到下面的TextBox上


Step 4:Demo WinForm介面 - 執行結果

按下查詢後,可以取得該Class的Summary 與Propery Name


Step 5:查詢XML-代碼區塊

開啟主程式代碼,會有關於”1.查詢” 按鈕的功能Region
1. 點擊查詢按鈕事件
2. 檢核 Xml檔案是否存在
3. 解析XML檔案,並回傳到UI畫面上


Step 6:查詢XML-查詢事件與檢核存在

  1. 載入XML
  _docuDoc.Load(_xmlPath); 


2. 取得Property屬性的資料 P: 開頭

_docuDoc.SelectNodes("//member[starts-with(@name, '" + $@"P:{className}" + "')]");


3. Regex 正則表示式截斷出資料

var dictionary = new Dictionary<string, string>();
Regex filter = new Regex(@"\..*$");
for (int i = 0; i < xmlDocuOfMethod.Count; i++)
{
    var nameTag = xmlDocuOfMethod[i].Attributes["name"].Value.Replace(className, "");
    var name = filter.Match(nameTag).Value.Replace(".", ""); ;
    var summary = xmlDocuOfMethod[i].InnerText;
    dictionary.Add(name, summary);
}


4. 最後輸出到畫面上,完整代碼如下:

/// <summary>
/// 【1】 取得特定的Class裡面的PropertyName 與 Summary
/// </summary>
private void GetSpecifyClassNameInfomation(string className)
{
    var _docuDoc = new System.Xml.XmlDocument();
    _docuDoc.Load(_xmlPath);
    var xmlDocuOfMethod = _docuDoc.SelectNodes(
        "//member[starts-with(@name, '" + $@"P:{className}" + "')]");
    if (xmlDocuOfMethod != null)
    {
        var dictionary = new Dictionary<string, string>();
        Regex filter = new Regex(@"\..*$");
        for (int i = 0; i < xmlDocuOfMethod.Count; i++)
        {
            var nameTag = xmlDocuOfMethod[i].Attributes["name"].Value.Replace(className, "");
            var name = filter.Match(nameTag).Value.Replace(".", ""); ;
            var summary = xmlDocuOfMethod[i].InnerText;
            dictionary.Add(name, summary);
        }
        var resultString = new StringBuilder();
        foreach (var dicItem in dictionary)
        {
            resultString.AppendLine($"PropertyName:{dicItem.Key}");
            resultString.AppendLine($"Summary:{dicItem.Value.Replace("\r\n", "").Trim()}");
            resultString.AppendLine();
        }
        ConverttextBoxMessage.Text = resultString.ToString();
    }
        }





第三部分:應用取得Summary後的內容

Step 1:假設有一個需求,Class做Mapping

假設有一需求:需要將原本的Login物件裡面的名稱全部重新命名,但原本的要保留
並且新的Login物件要與原本的Login物件 1對1
假設Property有100個項目,用手寫必定會瘋掉,這時就可以借助XML解析的方式


Step 2:Demo WinForm介面 - 使用轉換方法

開啟範例檔案中的Change.LoginViewModel.cs 的檔案,取得 命名空間 + ClassName
並且組成後貼到下面的TextBox上


Step 3:Demo WinForm介面 - 貼上代碼完成

  1. 執行”2.轉換對應物件”,結果會輸出到下方
    2. 再將代碼貼上到程式碼中,即可完成轉換
    ※此為範例,因為有Xml檔案,可以連Method都全部撰寫,達到完全自動化產代碼


Step 4:轉換對應物件-代碼區塊

開啟主程式代碼,會有關於”2.轉換對應物件按鈕” 按鈕的功能Region
1. 轉換對應物件按鈕
2. 取得客製的Mapping 代碼


Step 5:轉換對應物件-取得輸出的Code

  1. 檢核部分跳過不在敘述,可參考第二部分
    2.取出Summary中的原字,與改變後的對應值

    3. 將XML取回的資料二次使用,解析出遇到的第一段英文與數字”[A-Za-z0-9]+”,做為名稱
var resultString = new StringBuilder();
foreach (var dicItem in dictionary)
{
    string path = "P:" + className + "." + dicItem.Key;
    XmlNode findXmlDocuOfMethod = _docuDoc.SelectSingleNode(
        "//member[starts-with(@name, '" + path + "')]");
    var filterWord = new Regex(@"[A-Za-z0-9]+");
    var match = filterWord.Match(findXmlDocuOfMethod.InnerText.Replace("\r\n", ""));
    //match.Groups[0].Value  取得原名稱
    //prop.Name 取得新名稱
    if (match.Success)
    {
        resultString.AppendLine($@"{dicItem.Key} = input.{match.Groups[0].Value},");
    }
}


4. 最後輸出到畫面上,完整代碼如下:


/// <summary>
/// 【2】 取得客製的Mapping 代碼
/// </summary>
private void GetSpecifyClassNameConvertObjecString(string className)
{
    var _docuDoc = new System.Xml.XmlDocument();
    _docuDoc.Load(_xmlPath);
    var xmlDocuOfMethod = _docuDoc.SelectNodes(
        "//member[starts-with(@name, '" + $@"P:{className}" + "')]");
    if (xmlDocuOfMethod != null)
    {
        var dictionary = new Dictionary<string, string>();
        Regex filter = new Regex(@"\..*$");
        for (int i = 0; i < xmlDocuOfMethod.Count; i++)
        {
            var nameTag = xmlDocuOfMethod[i].Attributes["name"].Value.Replace(className, "");
            var name = filter.Match(nameTag).Value.Replace(".", ""); ;
            var summary = xmlDocuOfMethod[i].InnerText;
            dictionary.Add(name, summary);
        }
        var resultString = new StringBuilder();
        foreach (var dicItem in dictionary)
        {
            string path = "P:" + className + "." + dicItem.Key;
            XmlNode findXmlDocuOfMethod = _docuDoc.SelectSingleNode(
                "//member[starts-with(@name, '" + path + "')]");
            var filterWord = new Regex(@"[A-Za-z0-9]+");
            var match = filterWord.Match(findXmlDocuOfMethod.InnerText.Replace("\r\n", ""));
            //match.Groups[0].Value  取得原名稱
            //prop.Name 取得新名稱
            if (match.Success)
            {
                resultString.AppendLine($@"{dicItem.Key} = input.{match.Groups[0].Value},");
            }
        }
        ConverttextBoxMessage.Text = resultString.ToString();
    }
}



第四部分:強型別將Class ViewModel轉換的方法

Step 1:Demo WinForm介面 - 轉換已存在物件

另一種方法是將代碼裡的指定Class,直接進行轉換
執行按鈕”3.轉換已存在物件”
如下執行結果可以直接取得


Step 2:轉換已存在物件-代碼區塊

開啟主程式代碼,會有關於”3.轉換已存在物件” 按鈕的功能Region
1. 按鈕事件-取得強型別類型的物件內容方法
2. 泛型物件轉換,將指定的Class丟進去做轉換


Step 3:轉換已存在物件-代碼區塊

  1. 檢核部分跳過不在敘述,可參考第二部分
    2.核心是typeof(T) 取出,LoginViewModel的PropertyName以做為搜尋條件
var properties = typeof(T).GetProperties();

foreach (var prop in properties)
{
   //prop.Name
}


3. 完整泛型轉換代碼如下

/// <summary>
///【3】 泛型物件轉換
/// </summary>
private void LoadXml<T>()
{
    var properties = typeof(T).GetProperties();
    var _docuDoc = new System.Xml.XmlDocument();
    _docuDoc.Load(_xmlPath)
    var resultString = string.Empty;
    foreach (var prop in properties)
    {
        var fullname = prop.DeclaringType.FullName.Replace('+', '.');
        string path = "P:" + fullname + "." + prop.Name
        XmlNode xmlDocuOfMethod = _docuDoc.SelectSingleNode(
            "//member[starts-with(@name, '" + path + "')]");
        Regex filter = new Regex(@"([A-Za-z]+)");
        var match = filter.Match(xmlDocuOfMethod.InnerText.Replace("\r\n", ""));
        //match.Groups[0].Value  取得原名稱
        //prop.Name 取得新名稱
        if (match.Success)
        {
            resultString += $@"{prop.Name} = input.{match.Groups[0].Value}," + Environment.NewLine;
        }
        ConverttextBoxMessage.Text = resultString; 
    }
}