2013-08-13 3 views
1

클라이언트 프론트 엔드가 입력을 기다리고있는 것을 에뮬레이트하기 위해 MySQL 쿼리에서 XML을 포맷하려고합니다. 나는 클라이언트가 요구하는 것을 통제 할 수 없기 때문에 Wireshark 캡처에서 얻은 정보와 일치시켜야합니다. 필자는 데이터 집합에 열을 추가하여이 작업을 수행 할 생각이 없습니다. XML에 추가 된 내용을 검색하고 바꿀 수는 있지만 매우 비슷하지만 많은 쿼리가 있습니다. & 출력 글을 쓸 때, 나는 비례가 잘되는 것을하고 싶다. 불행하게도이 코드는 새로운 프런트 엔드 클라이언트를 작성할 때 현재의 레거시 시스템이 클라이언트 IP 주소 나 고유 한 "ActionID"와 같이 많은 데이터를 추적하지 않기 때문에 코드를 버릴 수 있습니다. 아래에서 참조 할 것입니다. XML로 아무것도하지 않아도됩니다. 모두 MySQL 기반의 쿼리가 될 것입니다. 내 쿼리XML 부모 노드로 사용할 데이터 세트에 열 추가

<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> 
탈회 태그에서

<SearchResult> 
    <SBE_PCB_Data PCBID="53"> 

: 나는 <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()); 

:

이 코드를 사용하여 (코드의 가짜 ActionID에서)

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

그리고 적절한 태그 다음 닫기 :

</SBE_PCB_Data> 
</SearchResult> 

내가 작동하지 않았다 "SBE_PCB_Data"태그에 대한 더미 열을 추가 노력했다. 나는 조상 노드로 XML의 나머지 부분을 빙 돌아 필요

<SBE_PCB_Data>SBE_PCB_Data</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); 

이 그냥로 표시합니다.

결과에 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))); 
    } 
} 

}
이 실행 얻기 위해 일부 조정을 할 수 있었다, 그러나 개념은 소리로 업데이트, 나는 그것을 확장 할 수있는 것을 좋아한다. 아직 정확한 결과물을 제공하지는 못하지만,이를 조정할 수도 있습니다.

답변

1

자, 이것을 리펙토링합시다.

데이터 집합에서 직접 시도하지 말고 여기에서 여러 가지 작업을 수행하려고합니다. 유지하기가 어렵고 단위 테스트가 매우 어렵습니다.

우리가해야 할 일은 우리가보다 쉽게 ​​작업 할 수있는 SearchResult 클래스를 만드는 것입니다. 이것은 비즈니스 규칙 (사용자와 임의의 ActionId에 추가 된 Ip)에 넣기에 편리한 장소이기도합니다. 쉽게 우리가 다음 우리가 (느리게, 더 많은 의존성이있는)

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 값이어야한다고 가정합니다.

는 첫째로 우리는 개인의 SearchResult합니다 (의 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)); 
} 
를 변환하는 방법을 만듭니다의 SearchResult

목록에서 XML을 생성하는 XML Linq에를 사용할 수 있습니다

두 번째로 우리는 목록 이제

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

를 변환하는 방법을 만듭니다 우리의 주요 호출 방법은 간단하게됩니다 ...

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

팀, 그건 일하고, 감사합니다! 나는 그것을 쉽게 확장 할 수있는 방법을 좋아한다. (내게는 이전에 일주일에 썼던 무차별 대입 방식의 한 세대 만이 확장 성이 전혀 없다.) 내일 당신의 대답을 위해 일할 것이고 질문이 있다면 알려주 겠소.귀하의 가정은 대부분 정확했으며, "EventType"은 변경 될 수 있습니다 (7 가지 가능성 있음).이 중 6 가지가이 테마의 변형으로 이어질 것입니다. 7 번째 버전은 약 5 가지 유형의 테스트 데이터이기 때문에 곰이 될 것입니다. 장치의. 많은 노력에 감사 드리며, 정말로 감사드립니다. – delliottg

관련 문제