簡體   English   中英

將列添加到數據集中以用作XML父節點

[英]Add columns to dataset to be used as XML parent nodes

我正在嘗試從MySQL查詢格式化XML以模仿客戶端前端期望輸入的內容。 我無法控制客戶端的需求,因此我必須匹配從Wireshark捕獲中獲得的內容。 我不贊成在數據集中添加列以完成此操作,因此我可以搜索並替換XML的添加內容,但是,我有大量非常相似但又不同的查詢和輸出寫,而我寧願做一些可以擴展的事情。 不幸的是,它將被丟棄的代碼,因為當我為此編寫新的前端客戶端時,我們將不會跟蹤當前遺留系統所做的大量數據,例如客戶端IP地址或所謂的唯一“ ActionID”您將在下面看到它,我也不必對XML做任何事情,它們全都是MySQL驅動的查詢。

我的輸出應采用以下形式:

<PCBDatabaseReply>
  <SearchResult>
    <SBE_PCB_Data PCBID="53">
      <Termination ActionID="97DF" User="UName:192.168.255.255" Date="2012-09-26T13:15:51" PCBID="53">
        <Reason>Other</Reason>
      </Termination>
    </SBE_PCB_Data>
  </SearchResult>
</PCBDatabaseReply>

我的查詢結果如下所示:

EventType   User    Date                PCBID   Reason 
Termination UName   2012-09-26T13:15:51 53      Other 

我的輸出XML當前如下所示:

<PCBDatabaseReply>
  <Termination User="UName" Date="2012-09-26T13:15:51" PCBID="53">
    <EventType>Termination</EventType>
    <Reason>Other</Reason>
  </Termination>
</PCBDatabaseReply>

使用此代碼:

string mysqlConnection = "server=server;\ndatabase=database;\npassword=password;\nUser ID=user;";
MySqlConnection connection = new MySqlConnection(mysqlConnection);
connection.Open();
string command = "SELECT eventtypes.EventType, events.User, DATE_FORMAT(events.DateTime,'%Y-%m-%dT%T') AS Date, pcbid.PCBID, getReasons.ItemValue AS Reason " +
                "FROM events " +
                "INNER JOIN pcbid ON events.PCBID = pcbid.PCBID " +
                "INNER JOIN eventtypes " +
                "ON events.EventType_ID = eventtypes.EventType_ID " +
                "LEFT JOIN getReasons " + 
                "ON getReasons.Event_ID = events.Event_ID " +
                "WHERE eventtypes.EventType = 'termination'";
//create fake "ActionID"
var random = new Random();
string ActionID = String.Format("{0}\"{1:X4}\"", "ActionID=", random.Next(0xffff));

MySqlDataAdapter adapter = new MySqlDataAdapter(command, connection);
DataSet dataSet = new DataSet();
adapter.Fill(dataSet);
//change upper level node name to what's expected in client-speak
dataSet.DataSetName = "PCBDatabaseReply";

//change first child node name to client-speak eventType
dataSet.Tables[0].TableName = dataSet.Tables[0].Rows[0][0].ToString();
StringWriter writer = new StringWriter();

var ds1 = dataSet.Tables[0];
DataColumn dcEventType = ds1.Columns[0];
DataColumn dcUser = ds1.Columns[1];
DataColumn dcDate = ds1.Columns[2];
DataColumn dcPCBID = ds1.Columns[3];

dcEventType.ColumnMapping = MappingType.Element;
dcUser.ColumnMapping = MappingType.Attribute;
dcDate.ColumnMapping = MappingType.Attribute;
dcPCBID.ColumnMapping = MappingType.Attribute;

dataSet.Tables[0].WriteXml(writer, true);
Console.WriteLine(writer.ToString());

我需要注入幾件事

在<PCBDatabaseReply>下的頂部:

<SearchResult>
  <SBE_PCB_Data PCBID="53">

在終止標簽中:(來自代碼中的虛假ActionID)

ActionID="0xnnnn"  & append ":192.168.255.255" to the end of the user name

然后使用適當的標簽關閉:

  </SBE_PCB_Data>
</SearchResult>

我嘗試為“ SBE_PCB_Data”標記添加一個虛擬列,但該列無效。

DataColumn dcSBE_PCB_Data = new DataColumn("SBE_PCB_Data", System.Type.GetType("System.String"), "SBE_PCB_Data", MappingType.Element);
dcSBE_PCB_Data.DefaultValue = "SBE_PCB_Data";
//add to the dataset
dataSet.Tables[0].Columns.Add(dcSBE_PCB_Data);
//move it to the zeroth position
dcSBE_PCB_Data.SetOrdinal(0);

這只是使其顯示為:

<SBE_PCB_Data>SBE_PCB_Data</SBE_PCB_Data>

我需要它包裝XML的其余部分作為祖先節點。

如何最好地將我需要的XML注入結果中?

編輯:根據下面的出色示例進行重構 **編輯:使用最終代碼更新

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Xml.Linq;
using MySql.Data.MySqlClient;

namespace TerminationResults
{
public class SearchResult
{
    //all possible event detail tags (test items are excluded)
    public string EventType { get; set; }
    public string User { get; set; }
    public string Date { get; set; }
    public string PCBID { get; set; }
    public string EAReason { get; set; }
    public string ETReason { get; set; }
    public string Notes { get; set; }
    public string Reason { get; set; }
    public string SBEJobNumber { get; set; }
    public string SBEModelNumber { get; set; }
    public string SBEPN { get; set; }
    public string SBESerialNumber { get; set; }
    //create fake IP address since we no longer track it
    public string UserAndIP
    {
        get { return String.Format("{0}:192.168.255.255", User); }
        set {}
    }
    //create fake actionID since the originals weren't inserted into the database because they weren't unique.
    public string ActionId
    {
        get { return String.Format("{0:X4}", new Random().Next(0xffff)); }
        set {}
    }
}

internal class Program
{
    private static void Main(string[] args)
    {
        var searchResults = GetSearchResults();
        var xml = TransformList(searchResults);
        Console.WriteLine(xml);
        Console.ReadLine();
    }

    public static IEnumerable<SearchResult> GetSearchResults()
    {
        List<SearchResult> searchResults = new List<SearchResult>();
        try
        {
            const string mysqlConnection = @"server=server;
                                        database=database;
                                        password=password;
                                        User ID=username;";
            MySqlConnection conn = new MySqlConnection(mysqlConnection);
            conn.Open();
            using (conn)
            {
                string cmd = @"SELECT eventtypes.EventType, events.User, 
                    DATE_FORMAT(events.DateTime,'%Y-%m-%dT%T') AS Date,
                    pcbid.PCBID, 
                    getEAReasons.ItemValue AS EAReason,
                    getETReasons.ItemValue AS ETReason,
                    getReasons.ItemValue AS Reason, 
                    getNotes.ItemValue AS Notes,
                    getSBEJobNumbers.ItemValue AS SBEJobNumber,
                    getSBEModelNumbers.ItemValue AS SBEModelNumber,
                    getSBEPNs.ItemValue as SBEPN,
                    getSBESerialNumbers.ItemValue as SBESerialNumber
                    FROM events 
                    INNER JOIN pcbid ON events.PCBID = pcbid.PCBID 
                    INNER JOIN eventtypes 
                    ON events.EventType_ID = eventtypes.EventType_ID 
                    LEFT JOIN getEAReasons
                    ON getEAReasons.Event_ID = events.Event_ID
                    LEFT JOIN getETReasons
                    ON getETReasons.Event_ID = events.Event_ID
                    LEFT JOIN getReasons
                    ON getReasons.Event_ID = events.Event_ID
                    LEFT JOIN getNotes
                    ON getNotes.Event_ID = events.Event_ID
                    LEFT JOIN getSBEJobNumbers
                    ON getSBEJobNumbers.Event_ID = events.Event_ID
                    LEFT JOIN getSBEModelNumbers
                    ON getSBEModelNumbers.Event_ID = events.Event_ID
                    LEFT JOIN getSBEPNs
                    ON getSBEPNs.Event_ID = events.Event_ID
                    LEFT JOIN getSBESerialNumbers
                    ON getSBESerialNumbers.Event_ID = events.Event_ID
                    WHERE eventtypes.EventType = 'termination'";
                try
                {
                    using (MySqlDataAdapter adapter = new MySqlDataAdapter(cmd, conn))
                    {
                        DataSet dataSet = new DataSet();
                        adapter.Fill(dataSet);
                        DataTable ds = dataSet.Tables[0];
                        for (int row = 0; row < ds.Rows.Count; row++ )
                        {
                            SearchResult result = new SearchResult()
                                {
                                    EventType = ds.Rows[row]["EventType"].ToString(),
                                    User = ds.Rows[row]["User"].ToString(),
                                    Date = ds.Rows[row]["Date"].ToString(),
                                    PCBID = ds.Rows[row]["PCBID"].ToString(),
                                    EAReason = ds.Rows[row]["EAReason"].ToString().Any() ? ds.Rows[row]["EAReason"].ToString() : null,
                                    ETReason = ds.Rows[row]["ETReason"].ToString().Any() ? ds.Rows[row]["ETReason"].ToString() : null,
                                    Notes = ds.Rows[row]["Notes"].ToString().Any() ? ds.Rows[row]["Notes"].ToString() : null,
                                    Reason = ds.Rows[row]["Reason"].ToString().Any() ? ds.Rows[row]["Reason"].ToString() : null,
                                    SBEJobNumber = ds.Rows[row]["SBEJobNumber"].ToString().Any() ? ds.Rows[row]["SBEJobNumber"].ToString() : null,
                                    SBEModelNumber = ds.Rows[row]["SBEModelNumber"].ToString().Any() ? ds.Rows[row]["SBEModelNumber"].ToString() : null,
                                    SBEPN = ds.Rows[row]["SBEPN"].ToString().Any() ? ds.Rows[row]["SBEPN"].ToString() : null,
                                    SBESerialNumber = ds.Rows[row]["SBESerialNumber"].ToString().Any() ? ds.Rows[row]["SBESerialNumber"].ToString() : null
                                };
                            searchResults.Add(result);
                        }

                    }
                }
                catch (MySqlException ex)
                {
                    Console.WriteLine(ex);
                }
                catch(Exception ex)
                {
                    Console.WriteLine(ex);
                }

            }
        }
        catch (MySqlException ex)
        {
            Console.WriteLine(ex);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
        return searchResults;
    }

    public static XElement TransformSearchResult (SearchResult result)
    {
        return new XElement("SBE_PCB_Data", 
            new XAttribute("PCBID", result.PCBID),
            new XElement(result.EventType,
            new XAttribute("ActionID", result.ActionId),
            new XAttribute("User", result.UserAndIP),
            new XAttribute("Date", result.Date),
            new XAttribute("PCBID", result.PCBID),
            result.EAReason == null ? null : new XElement("EAReason", result.EAReason),
            result.ETReason == null ? null : new XElement("ETReason", result.ETReason),
            result.Reason == null ? null : new XElement("Reason", result.Reason),
            result.Notes == null ? null : new XElement("Note", result.Notes),
            result.SBEJobNumber == null ? null : new XElement("SBEJobNumber", result.SBEJobNumber),
            result.SBEModelNumber == null ? null : new XElement("SBEModelNumber", result.SBEModelNumber),
            result.SBEPN == null ? null : new XElement("SBEPN", result.SBEPN),
            result.SBESerialNumber == null ? null : new XElement("SBESerialNumber", result.SBESerialNumber)
            )
        );
    }

    public static XElement TransformList (IEnumerable<SearchResult> listOfResults)
    {
        return new XElement("PCBDatabaseReply",
            new XElement("SearchResult", 
                            from r in listOfResults
                            select TransformSearchResult(r)));
    }
}

}
必須進行一些調整才能使其運行,但是這個概念很合理,我喜歡它是可擴展的。 它並沒有給出正確的輸出,但是我也可以進行調整。

好的,讓我們重構一下。

讓我們不要嘗試直接從數據集中執行此操作,您正在此處嘗試對方法中的許多事情執行操作,這很難維護並且很難進行單元測試。

我們應該做的第一件事是創建一個我們可以更輕松地使用的SearchResult類,這也是放置我們的業務規則(將Ip添加到User和隨機ActionId)的便捷位置,這也意味着我們可以輕松地模擬數據無需訪問數據庫即可進入此類,然后我們可以將轉換邏輯作為單元測試而不是集成測試(速度較慢且具有更多依賴性)進行測試

public class SearchResult
{
    public string EventType {get ;set;}
    public string User {get ; set;}
    public DateTime Date {get;set;}
    public int PCBID {get;set;}
    public string Reason {get;set;}

    public string UserAndIP
    {
        get
        {
            return String.Format("{0}:192.168.255.255",User);
        }
    }

    public string ActionId
    {
        get
        {
            return String.Format("{0:X4}", new Random().Next(0xffff));
        }
    }
}

因此,讓我們重寫查詢以現在填充SearchResult的列表而不是數據集

public IEnumerable<SearchResult> GetSearchResults()
{
    using(var conn = GetYourConnection())
    {
        conn.open();
        using(var cmd = conn.CreateCommand())
        {
            cmd.CommandText = GetYourQueryString();
            using(var reader = cmd.ExecuteReader())
            {
                while(reader.Read())
                {
                    var result = new SearchResult
                    {
                        .... populate from reader...
                    }
                    yield return result;
                }
            }           
        }
    }
}

因此,現在我們有了SearchResult類和一個提供給我們列表的查詢方法,讓我們將其轉換為所需的XML。 首先,我將提出一些假設,這些假設在您的問題中並非100%明確。 (如果這些不正確,將很容易進行修改)

  1. 我假設我們正在為查詢返回的每個搜索結果創建一個搜索結果標簽。 並且這些將包含在PCBDatabaseReply標記中。

  2. xml標記“ Termination”是事件類型的值,因此我假設標記應該是EventType值。

讓我們使用Linq到XML從SearchResults列表中創建XML

首先,我們將創建一個方法來轉換單個SearchResults(SearchResult標記的內容)

public XElement TransformSearchResult(SearchResult result)
{
    return new XElement("SearchResult",
        new XElement("SBE_PCB_Data", new XAttribute("PCBID", result.PCBID)),
        new XElement(result.EventType,
            new XAttribute("ActionID", result.ActionId),
            new XAttribute("User", result.UserAndIP),
            new XAttribute("Date", result.Date),
            new XAttribute("PCBID", result.PCBID)),
        new XElement("Reason", result.Reason));
}

其次,我們將創建轉換列表的方法

public XElement TransformList(IEnumerable<SearchResult> listOfResults)
{
    return new XElement("PCBDatabaseReply", 
            from r in listOfResults
            select TransformSearchResult(r));
}

現在我們的主要調用方法變成...

var searchResults = GetSearchResults();
var xml = TransformList(searchResults);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM