Microsoft MVP성태의 닷넷 이야기
.NET Framework: 548. Linq는 결국 메서드 호출! [링크 복사], [링크+제목 복사],
조회: 22127
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 1개 있습니다.)

Linq는 결국 메서드 호출!

다음과 같은 질문이 있군요. ^^

2개의 DataTable Join 결과 전체 컬럼을 DataTable 로 리턴하기
; https://www.sysnet.pe.kr/3/0/3701

정리해 보면, 다음과 같은 식으로 DataTable이 마련되어 있을 때,

DataTable dtResult = new DataTable();
dtResult.Columns.Add("ID", typeof(string));
dtResult.Columns.Add("name", typeof(string));
dtResult.Columns.Add("stock", typeof(int));
dtResult.Columns.Add("ID2", typeof(string));
dtResult.Columns.Add("name2", typeof(string));
dtResult.Columns.Add("stock2", typeof(int));

DataTable table1 = new DataTable();

table1.Columns.Add("ID", typeof(string));
table1.Columns.Add("name", typeof(string));
table1.Columns.Add("stock", typeof(int));

table1.LoadDataRow(new object[] { "5", "test1", 500 }, true);
table1.LoadDataRow(new object[] { "6", "test2", 600 }, true);
table1.LoadDataRow(new object[] { "7", "test3", 700 }, true);

DataTable table2 = new DataTable();

table2.Columns.Add("ID2", typeof(string));
table2.Columns.Add("name2", typeof(string));
table2.Columns.Add("stock2", typeof(int));

table2.LoadDataRow(new object[] { "5", "test12", 510 }, true);
table2.LoadDataRow(new object[] { "6", "test22", 610 }, true);
table2.LoadDataRow(new object[] { "7", "test32", 710 }, true);

한번의 LINQ 쿼리로 tabl1과 table2의 join 결과를 dtResult에 담고 싶다는 것입니다. 물론, 질문하신 분의 2가지 Linq 쿼리는 그런 것을 못한다는 것이고, 제가 답변을 한 StackOverflow의 글에서는 그에 대한 해결책이 표현되어 있습니다.

간단합니다. 다음과 같이 해주면 해결됩니다.

var result = from dataRows1 in table1.AsEnumerable()
             join dataRows2 in table2.AsEnumerable()
             on dataRows1.Field<string>("ID") equals dataRows2.Field<string>("ID2")
             select dtResult.LoadDataRow(dataRows1.ItemArray.Concat(dataRows2.ItemArray).ToArray<object>(), false);

DataTable my = result.CopyToDataTable();

실제로 위의 코드를 실행해서 출력해 보면,

DataSet ds = new DataSet("test");
ds.Tables.Add(my);

Console.WriteLine(ds.GetXml());

다음과 같은 결과를 얻습니다.

<test>
  <Table1>
    <ID>5</ID>
    <name>test1</name>
    <stock>500</stock>
    <ID2>5</ID2>
    <name2>test12</name2>
    <stock2>510</stock2>
  </Table1>
  <Table1>
    <ID>6</ID>
    <name>test2</name>
    <stock>600</stock>
    <ID2>6</ID2>
    <name2>test22</name2>
    <stock2>610</stock2>
  </Table1>
  <Table1>
    <ID>7</ID>
    <name>test3</name>
    <stock>700</stock>
    <ID2>7</ID2>
    <name2>test32</name2>
    <stock2>710</stock2>
  </Table1>
</test>

그런데, 여기서 제가 하고 싶은 말은... Linq는 단순한 메서드 호출의 나열에 불과하다는 것입니다. 일례로, 위의 Linq 쿼리에서 select 문을 개선해 볼까요?

var result = from dataRows1 in table1.AsEnumerable()
             join dataRows2 in table2.AsEnumerable()
             on dataRows1.Field<string>("ID") equals dataRows2.Field<string>("ID2")
             select dtResult.LoadDataRow(dataRows1.ItemArray.Concat(dataRows2.ItemArray).ToArray<object>(), false);

가령, 다음과 같이 메서드로 바꿔줄 수 있습니다.

var result = from dataRows1 in table1.AsEnumerable()
             join dataRows2 in table2.AsEnumerable()
             on dataRows1.Field<string>("ID") equals dataRows2.Field<string>("ID2")
             select MergeRows(dtResult, dataRows1, dataRows2);


private static DataRow MergeRows(DataTable table, DataRow dataRows1, DataRow dataRows2)
{
    List<object> list = new List<object>();

    list.AddRange(dataRows1.ItemArray);
    list.AddRange(dataRows2.ItemArray);

    return table.LoadDataRow(list.ToArray(), false);
}

또는, table 정의를 바꾸고 그에 맞게 데이터를 채우는 것도 가능합니다. 가령, 다음은 ID2 컬럼을 제외하고 있습니다.

DataTable dtResult = new DataTable();
dtResult.Columns.Add("ID", typeof(string));
dtResult.Columns.Add("name", typeof(string));
dtResult.Columns.Add("stock", typeof(int));
dtResult.Columns.Add("name2", typeof(string));
dtResult.Columns.Add("stock2", typeof(int));

var result = from dataRows1 in table1.AsEnumerable()
             join dataRows2 in table2.AsEnumerable()
             on dataRows1.Field<string>("ID") equals dataRows2.Field<string>("ID2")
             select MergeRows2(dtResult, dataRows1, dataRows2);

private static DataRow MergeRows2(DataTable table, DataRow dataRows1, DataRow dataRows2)
{
    List<object> list = new List<object>();

    list.AddRange(dataRows1.ItemArray);
    list.AddRange(dataRows2.ItemArray.Skip(1));

    return table.LoadDataRow(list.ToArray(), false);
}

/*
<test>
  <Table1>
    <ID>5</ID>
    <name>test1</name>
    <stock>500</stock>
    <name2>test12</name2>
    <stock2>510</stock2>
  </Table1>
  <Table1>
    <ID>6</ID>
    <name>test2</name>
    <stock>600</stock>
    <name2>test22</name2>
    <stock2>610</stock2>
  </Table1>
  <Table1>
    <ID>7</ID>
    <name>test3</name>
    <stock>700</stock>
    <name2>test32</name2>
    <stock2>710</stock2>
  </Table1>
</test>
*/

이 정도면... 대충 감이 오시죠? ^^

(첨부 파일은 위의 예제 코드를 포함합니다.)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 2/20/2016]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2016-02-22 02시55분
[강준] 답변 감사합니다.
그런데 제가 원했던건 dtResult 가 없는상태에서 결과를 도출할수 없는가 였습니다.^^
A 테이블과 B 테이블의 컬럼을 알 수 없고 키만 같고 결과로는 키와 함께 다른 컬럼들도 같이해서
결과를 보고 싶었던것입니다.^^
일단 이런 방법도 있는 걸 알게 되었네요
[guest]
2016-02-22 03시03분
제가 MergeRows2를 작성한 것은 그에 대한 다양한 요구 사양을 만족시킬 수 있다는 것을 알려드린 것입니다. dtResult를 굳이 안 받아도 전달받은 DataRow 2개의 컬럼 값을 런타임시에 알아내서 dtResult를 임의로 구성해 처리할 수 있습니다. (물론, 매번 구성하면 성능 손실이 심각하니 static 유형의 캐시 관리를 추가해야 할 것입니다.)

그러니까, 이 글에서는 Linq를 제약된 환경으로 인식하지 말고 예전의 메서드 호출과 동일하게 보라는 것입니다.

즉 Linq를 생각하지 말고, 2개의 DataRow가 조합된 데이터들을 어떻게 DataTable에 넣을 수 있을까요?.... 와 같은 문제입니다.
정성태
2016-02-22 04시10분
[강준] 네 말씀하신데로 런타임시에는 알수가 있어서 그렇게 처리를 하긴 했습니다.^^
암튼 linq 내에서 메서드 호출이 가능하다는걸 알았네요
(람다식을 이용하면 먼가 제약이 있던데..)
답변 감사합니다~

[guest]

... [166]  167  168  169  170  171  172  173  174  175  176  177  178  179  180  ...
NoWriterDateCnt.TitleFile(s)
894정성태7/25/201026377오류 유형: 100. Could not find the Database Engine startup handle. [1]
893정성태7/25/201027554오류 유형: 99. .NET 4.0 설치된 윈도우 7에서 SQL Server 2008 R2 설치 오류
892정성태7/9/201029237오류 유형: 98. 영문 윈도우에 한글 SQL Server 2008 R2 설치할 때 오류 [4]
891정성태7/8/201025144오류 유형: 97. MsiGetProductInfo failed to retrieve ProductVersion for package with Product Code = '{...}'. Error code: 1605. [2]
889정성태7/5/201026809.NET Framework: 179. Dictionary.Get(A) 대신 Dictionary.Get(A.GetHashCode())를 사용해서는 안 되는 이유 [1]
888정성태6/30/201024621오류 유형: 96. Hyper-V 연결 오류 - A connection will not be made because credentials may not be sent to the remote computer
887정성태6/23/201034492개발 환경 구성: 79. Hyper-V의 가상 머신에서 소리 재생 방법 [2]
886정성태6/23/201022662제니퍼 .NET: 14. ASMX, WCF 호출 모니터링 및 누수 확인
885정성태6/20/201024266개발 환경 구성: 78. COM+ 서버에서 COM+ 서버를 호출하는 방법
884정성태6/20/201027181제니퍼 .NET: 13. COM+ 서버 모니터링 [2]
883정성태6/18/201029104개발 환경 구성: 77. Appinit_Dlls로 구현한 환경 변수 설정 DLL [5]파일 다운로드1
882정성태6/17/201031878개발 환경 구성: 76. JKS(Java Key Store)에 저장된 인증서를 ActiveX 코드 서명에 사용하는 방법 [1]
881정성태6/14/201021296제니퍼 .NET: 12. COM+ 호출 모니터링 및 누수 확인
879정성태6/10/201023918제니퍼 .NET: 11. 소켓 모니터링 기능으로 본 ASP.NET의 소켓 풀링 기능 [1]
878정성태6/6/201023761제니퍼 .NET: 10. 소켓 모니터링 기능으로 본 WCF의 WSDualHttpBinding 성능 부하
877정성태5/31/201020472제니퍼 .NET: 9. 성능 관리 퀴즈 세 번째 문제 (닷넷 개발자 컨퍼런스)
876정성태5/31/201019911제니퍼 .NET: 8. 성능 관리 퀴즈 두 번째 문제 (닷넷 개발자 컨퍼런스) [2]
875정성태5/30/201021671제니퍼 .NET: 7. 성능 관리 퀴즈 첫 번째 문제 (닷넷 개발자 컨퍼런스)
873정성태5/19/201028508제니퍼 .NET: 6. 제니퍼를 위한 방화벽 설정
872정성태5/15/201027806제니퍼 .NET: 5. 제니퍼 서버 - NT 서비스로 구동시키는 방법
871정성태5/13/201034395VC++: 40. MSBuild를 이용한 VC++ 프로젝트 빌드파일 다운로드1
870정성태5/12/201025412제니퍼 .NET: 4. 닷넷 APM 솔루션 - 제니퍼 닷넷의 기능 요약 [2]
869정성태11/8/201926850오류 유형 : 95. WCF 인증서 설정 관련 오류 정리 [4]
865정성태5/5/201029148개발 환경 구성: 75. 인증서의 개인키를 담은 물리 파일 위치 알아내는 방법파일 다운로드1
864정성태5/4/201032926.NET Framework: 178. WCF - 사용자 정의 인증 구현 예제 [4]파일 다운로드1
863정성태5/4/201058889개발 환경 구성: 74. 인증서 관련(CER, PVK, SPC, PFX) 파일 만드는 방법 [1]파일 다운로드1
... [166]  167  168  169  170  171  172  173  174  175  176  177  178  179  180  ...