2009년 8월 31일 월요일

스레드로부터 안전한 방식으로 Windows Forms 컨트롤 호출

스레드로부터 안전한 방식으로 Windows Forms 컨트롤 호출


 프로그램이 조금 복잡해 지면 스레드를 사용하게 된다. WinForm 으로 멀티 스레드를 사용하다가 스레드가 컨트롤에 접근 하려 하면 예외가 발생한다. (역시 C# 컴파일러는 냉정하다...)



예외 : InvalidOperationException
설명 : "control name 컨트롤이 자신이 만들어진 스레드가 아닌 스레드에서 액세스되었습니다."

이 예외는 디버깅 할 때 발생하며, Run-time 때는 발생하지 않는다. (프로그램을 강제 종료 시키지도 않더라.)

CheckForIllegalCrossThreadCalls 프로퍼티 값을 false 로 설정하면 위 예외를 발생시키지 않게 할 수 있지만 안정적인 프로그램 작성을 위해 위 예외 처리 및, 예외가 일어 나지 않게 해주자.


테스트 한 코드는 다음과 같다.
#region 스레드메소드

// 스레드 메소드
public void ThreadProc()
{
            while (ThreadRun)
            {
                QueryCount++;
                QueryDB(QueryCount);                    
            }

            MessageBox.Show("스레드 작업 종료");
}


delegate void SetTextBox(int _count);

// 스레드에서 호출하는 메소드
private void QueryDB(int _count)
{
            var query = from c in TestDB.tbNodes
                              select c;

            RecordCount = query.Count();

            // textBoxQueryCount.Text = _count.ToString();
            // textBoxRecordCount.Text = RecordCount.ToString();
            // Thread.Sleep(RepeateCount * 1000);


            if (textBoxQueryCount.InvokeRequired)
            {
                SetTextBox dele = new SetTextBox(QueryDB);
                this.Invoke(dele, new object[] { _count });
            }
            else
                textBoxQueryCount.Text = _count.ToString();

            if (textBoxRecordCount.InvokeRequired)
            {
                SetTextBox dele = new SetTextBox(QueryDB);
                this.Invoke(dele, new object[] { _count });
            }
            else
                textBoxRecordCount.Text = RecordCount.ToString();

            Thread.Sleep(RepeateCount * 1000);
}
#endregion




부모 스레드에서 ThreadProc() 메소드를 작업자 스레드로 생성한다.
ThreadProc() 메소드는 ThreadRun 프로퍼티 값이 true 이면 계속 실행 하고, false 이면 스레드를 종료한다.

ThreadProc() 메소드를 실행하는 자식 스레드는 QueryDB() 메소드를 실행 하는데 이 안에서 컨트롤로 접근을
하게 된다.

컨트롤의 InvokeRequired 프로퍼티는 호출자가 컨트롤이 만들어진 스레드와 다른 스레드에 있기 때문에 메서드를 통해 컨트롤을 호출하는 경우 해당 호출자가 호출 메서드를 호출해야 하는지 여부를 나타내는 값을 가져온다.
컨트롤의 Handle 이 호출 스레드와 다른 스레드에서 만들어져 호출 메서드를 통해 해당 컨트롤을 호출해야 하는 경우 true이고, 그렇지 않으면 false이다.
cf) MSDN InvokeRequired : http://msdn.microsoft.com/ko-kr/library/system.windows.forms.control.invokerequired.aspx

InvokeRequired 값이 true 일 때는 delegate 를 이용하여 해당 컨트롤에 접근하는 메소드를 다시 호출해 주고,
false 일 때는 직접 호출해 준다.









참조)
1. MSDN
http://msdn.microsoft.com/ko-kr/library/ms171728%28VS.80%29.aspx





2009년 8월 30일 일요일

Nullable 형식 사용(?, ??)


Nullable 형식 사용(C# 프로그래밍 가이드)




 C# 3.0 부터 시작해서 그런지 가끔 엄한 문법이 보인다. 요즘 코드를 보다 보면 ?, ?? 가 가끔 보인데..
Nullable 형식 이라고 하는데 C# 2.0 때 문법 이라고 한다. 무엇인지는 알고 있어야 겠다.

MSDN
http://msdn.microsoft.com/ko-kr/library/2cf62fcy(VS.80).aspx#Mtps_DropDownFilterText












C# 프로그래밍 가이드


Nullable 형식 사용(C# 프로그래밍 가이드)






nullable 형식을 사용하면 내부 형식의 값을 모두 나타낼 수 있을 뿐만 아니라 null 값을 추가로 나타낼 수 있습니다. nullable 형식은 다음과 같은 두 가지 방법 중 하나로 선언됩니다.

System.Nullable<T> variable

- 또는 -

T? variable

T는 nullable 형식의 내부 형식입니다. Tstruct를 비롯한 임의의 값 형식이 될 수 있지만 참조 형식은 될 수 없습니다.

nullable 형식이 필요한 경우를 예로 들면, true와 false라는 두 가지 값만 갖는 일반적인 부울 변수를 생각해 볼 수 있습니다. 이런 변수에는 "정의되지 않은 상태"를 의미하는 값이 없습니다. 여러 프로그래밍 응용 프로그램에서 변수는 정의되지 않은 상태로 있을 수 있으며, 데이터베이스 상호 작용이 가장 대표적인 예입니다. 예를 들어, 데이터베이스의 필드는 true나 false 값을 포함할 수 있지만 값을 전혀 포함하지 않을 수 있습니다. 마찬가지로, 참조 형식이 초기화되지 않았음을 나타내기 위해 이를 null로 설정할 수 있습니다.

이러한 차이를 처리하기 위해서는 상태 정보를 저장하기 위한 추가 변수 사용, 특수 값 사용 등 별도의 프로그래밍 작업이 필요할 수 있습니다. C#에서 nullable 형식 한정자를 사용하면 정의되지 않은 값을 나타내는 값 형식 변수를 만들 수 있습니다.






nullable 형식의 기준으로는 모든 값 형식을 사용할 수 있습니다. 예를 들면 다음과 같습니다.



int? i = 10;
double? d1 = 3.14;
bool? flag = null;
char? letter = 'a';
int?[] arr = new int?[10];







nullable 형식의 각 인스턴스에는 읽기 전용인 공용 속성이 두 개 있습니다.



  • HasValue

    HasValuebool 형식이며, 변수에 null이 아닌 값이 포함되어 있으면 true로 설정됩니다.


  • Value

    Value는 내부 형식과 동일한 형식입니다. HasValuetrue이면 Value에는 의미 있는 값이 포함됩니다. HasValuefalse인 경우에 Value에 액세스하려고 하면 InvalidOperationException이 throw됩니다.

이 예제에서는 HasValue 멤버를 사용하여 변수의 값을 표시하려고 하기 전에 변수에 값이 포함되어 있는지 테스트합니다.



int? x = 10;
if (x.HasValue)
{
    System.Console.WriteLine(x.Value);
}
else
{
    System.Console.WriteLine("Undefined");
}


값은 다음과 같은 방법으로도 테스트할 수 있습니다.



int? y = 10;
if (y != null)
{
    System.Console.WriteLine(y.Value);
}
else
{
    System.Console.WriteLine("Undefined");
}







nullable 형식은 Value 속성을 사용하거나 명시적으로 캐스팅하여 일반 형식으로 캐스팅할 수 있습니다. 예를 들면 다음과 같습니다.



int? n = null;

//int m1 = n;      // Will not compile.
int m2 = (int)n;   // Compiles, but will create an exception if x is null.
int m3 = n.Value;  // Compiles, but will create an exception if x is null.


두 데이터 형식 사이에 사용자 정의 변환이 정의되어 있는 경우에는 이러한 데이터 형식의 null 허용 버전에 대해서도 동일한 변환을 사용할 수 있습니다.






nullable 형식의 변수는 다음과 같이 null 키워드를 사용하여 null로 설정할 수 있습니다.



int? n1 = null;


일반 형식에서 nullable 형식으로의 변환은 암시적입니다.



int? n2;
n2 = 10;  // Implicit conversion.







값 형식에 사용되는 미리 정의된 단항 및 이항 연산자와 모든 사용자 정의 연산자는 nullable 형식에도 사용할 수 있습니다. 피연산자가 null이면 이러한 연산자는 null 값을 생성합니다. 그렇지 않으면 연산자는 포함된 값을 사용하여 결과를 계산합니다. 예를 들면 다음과 같습니다.



int? a = 10;
int? b = null;

a++;         // Increment by 1, now a is 11.
a = a * 10;  // Multiply by 10, now a is 110.
a = a + b;   // Add b, now a is null.


nullable 형식과 비교를 수행하는 경우 nullable 형식 중 하나가 null이면 비교 결과는 항상 false입니다. 따라서 비교 결과가 false라고 해서 그 반대 경우가 true라고 단정할 수는 없습니다. 예를 들면 다음과 같습니다.



int? num1 = 10;
int? num2 = null;
if (num1 >= num2)
{
    System.Console.WriteLine("num1 is greater than or equal to num1");
}
else
{
    // num1 is NOT less than num2
}


위의 else 문에서 내린 결론은 유효하지 않습니다. num2null이므로 값을 포함하지 않기 때문입니다.

모두 null인 두 nullable 형식을 비교할 경우 결과는 true입니다.






?? 연산자는 null이 허용되지 않는 형식에 nullable 형식을 대입할 때 반환되는 기본값을 정의합니다.



int? c = null;

// d = c, unless c is null, in which case d = -1.
int d = c ?? -1;


이 연산자는 여러 개의 nullable 형식과 함께 사용할 수도 있습니다. 예를 들면 다음과 같습니다.



int? e = null;
int? f = null;

// g = e or f, unless e and f are both null, in which case g = -1.
int g = e ?? f ?? -1;







bool? nullable 형식에는 true, falsenull의 세 가지 값이 포함될 수 있습니다. 따라서 if, for 또는 while 같은 조건문에서는 이를 사용할 수 없습니다. 예를 들어, 다음 코드는 컴파일되지 않고 컴파일러 오류 CS0266이 발생합니다.



bool? b = null;
if (b) // Error CS0266.
{
}


이 코드는 null이 조건문의 컨텍스트에서 무엇을 의미하는지 명확하지 않기 때문에 컴파일되지 않습니다. 조건문에 사용하기 위해 nullable 부울을 bool로 명시적으로 캐스팅할 수 있지만 개체의 값이 있고 이 값이 null이면 InvalidOperationException이 throw됩니다. 따라서 bool로 캐스팅하기 전에 HasValue 속성을 확인하는 것이 중요합니다.

nullable 부울은 SQL에 사용되는 부울 변수 형식과 비슷합니다. &| 연산자로 생성되는 결과가 값이 세 개인 SQL 부울 형식과 일관성을 유지할 수 있도록 다음과 같은 미리 정의된 연산자가 제공됩니다.

bool? operator &(bool? x, bool? y)

bool? operator |(bool? x, bool? y)

다음 표에서는 이러한 연산자의 결과를 보여 줍니다.
























































X y x&y x|y

True


true


True


true


True


false


False


true


True


null


Null


true


False


true


False


true


False


false


False


false


False


null


False


null


Null


true


Null


true


Null


false


False


null


Null


null


Null


null





2009년 8월 23일 일요일

[MS SQL Server] Login failed for user ’sa’ because the account iscurrently locked out.

Login failed for user ’sa’ because the account is currently locked out. The system administrator can unlock it.



 LINQ to SQL 을 이용하여 DB를 사용하려 하다가 "SqlException" 이 일어났다. 예외 메시지는 다음과 같다.

" Login failed for user ’six605’ because the account is currently locked out. The system administrator can unlock it."

해당 계정이 잠겼다는 소리인데.. administrator 계정이 해당 계정의 잠금을 풀어줄 수 있다는 소리다. (아직 DB 잘 모르는 상태...)

우선 왜 해당 계정이 잠겼는지 이해가 안갔는데 Windows Server OS 에는 로컬 보안 정책암호 정책계정 잠금 정책이 있다. 해당 계정이 잠길 수 있는 경우는 2가지 이다.


1. 암호 정책 에 의한 계정 잠금

리스트에 여러 항목이 있는데 그 중에서도 "최대 암호 사용 기간" 이라는 것이 있다. 암호 설정 후 여기서 설정한 기간이 지나면 해당 계정이 잠길 수 있다.



2. 계정 잠금 정책 에 의한 계정 잠금


리스트에 보면은 "계정 잠금 임계값" 설정이 있다. 즉, 사용자가 해당 계정으로 로그인 시도 시 설정만큼 로그인 실패 한다면 해당 계정이 잠기게 된다.



cf) 1.
MS Sql Server 에서 계정을 추가하면, Windows 계정에도 추가 되더라...
조금 더 알아봐야 겠지만 SQL Server 의 계정과 Windows 계정이 이원적인게 아니구나!!




cf) 2.
만약 계정이 잠겼다면 다음 SQL 문으로 잠금 해제 시켜줄 수 있다.

use master
go

alter login 로그인계정
with PASSWORD  = '새로운패스워드' unlock
go



MS SQL Server 에 계정을 생성할 때 Windows 의 계정 정책을 적용하지 않게 할 수 있다. 기본적으로는 Windows 의 계정 정책이 적용된다. MSSMS 를 열어보자.



해당 계정의 속성을 보면 "Enforce password policy" 와 "Enforce password expiration" 이 체크되어 있다. 암호 정책과 암호 만기를 적용시키겠다는 것인데, 이것을 체크 해제 시켜주면된다.

교육 Review - LINQ to SQL : LINQ for Connected Database

LINQ to SQL : LINQ for Connected Database







 In-memory collection, xml files, dats set 을 이용한 방법으로 똑같이 SQL Server DB를 이용할 수 있다. LINQ to SQL 은 LINQ project 의 한 부분으로서 Data Base 와 관련된 Object 에 대하여 쿼리, 관리할 수 있다.  LINQ to SQL 은 Data Base Table 과 여러분의 응용프로그램의 domain specific object model 사이의 불일치를 제거하였다. 여러분은 framework 가 여러분의 object 를 탐색하고, 업데이트 하는 동안 자유롭게 object 와 같은 데이터로 작업할 수 있다.

주어진 database 의 객체 모델을 만들기 위하여 클래스는 database entities 에 mapping 되어야 한다. object mapping 을 만드는 방법은 3가지가 있다.
1. 존재하는 객체에 attribute 를 더한다.
2. 자동으로 object 와 mappings 를 생성하기 위하여 지원되는 designer 를 사용한다.
3. command line SQLMetal tool 을 사용한다.





< 예제 만들기 >

1. Adding LINQ to SQL Support
LINQ to SQL 지원을 받기위해선 System.Data.Linq 어셈블리를 참조 시켜야 한다.



코딩의 편의를 위해 다음과 같이 using 선언문을 추가 시킨다.
using System.Data.Linq;
using System.Data.Linq.Mapping; 





2. 사용하려는 DataBase 로의 연결을 생성한다.

서버 탐색기(Server Explorer) 을 연다. 서버 탐색기 트리에 보면 데이터 연결(Data Connections) 노드가 있다. 이곳에 마우스 오른쪽 버튼을 누르면 팝업 메뉴가 나오는데 여기서 연결 추가(Add Connection) 을 선택해 주자.



서버 이름 항목에 콤보 박스를 누르면 연결 할 수 있는 서버 이름이 쫙~ 뜬다. 사용하려는 DB Server 를 선택해 준다.
서버 이름으로 연결 할 수도 있고, 서버의 IP 주소를 직접 입력해도 되는데 IP 주소를 직접 입력하는게 더 정확하다. 해당 서버의 인증 모드를 선택해 주고, SQL Server 인증 사용 이라면 사용자 이름과 암호를 적어준다. 특히, "데이터베이스에 연결" 에서 "데이터베이스 이름 선택 또는 입력" 란에 사용하고자 하는 DB 이름을 적어 주어야 한다. 적지 않는다면 Default 로 master DB 에 연결된다.



성공적으로 연결 되었다면 서버 탐색기에 해당 DB가 보인다.



3. SQL Designer 사용하기

SQL Designer 는 여러분이 Query를 원하는 DataBase 로 부터 table 의 metadata 를 보거나, 설정할 수 있게 해준다. SQL Designer 의 Command Line 버전은 SqlMetal 이라는 tool 이 있다. SqlMetal 은 Vistual Studio 와 .NET Framework 의 구성요소로서 설치된 위치는 %ProgramFiles%\Microsoft SDKs\Windows\v6.0A\bin 이다.


솔루션 탐색기(Solution Explorer) 에서 " 추가 → 클래스 " 를 선택한다.



"데이터" 항목에서 "LINQ to SQL 클래스" 를 선택한다.



이것이 " SQL Designer " 이다.



솔루션 탐색기에 몇가지 변화가 생기는데
DataClasses1.dbml 파일이 프로젝트에 추가 된다. 또한 그 하위에 DataClasses1.dbml.layout
   DataClasses1.designer.cs 파일이 추가 된다.
- SQL Designer 에 원하는 Table 을 드래그 앤 드롭 하면 Object Releation Mapping(ORM) 이 생성된다. ORM
  은 Database 의 해당 Table 과 해당 Table 클래스 사이를 연결시켜 주며 SQL Designer 가 생성시켜 주고,
  DataClasses1.designer.cs 파일에 위치하게 된다. 이 객체는 entity class 라고 불리며, 연결시킨 Table 을 마치
  프로젝트 안의 객체를 사용 하듯이 data 와 field 에 접근할 수 있도록 해준다.
- 또한 DataClasses1.desinger.cs 파일안에 DataContext 객체가 생성된다. 이 클래스는 연결시킨 Table 의 data
   와 field 에 쉽게 접근할 수 있게 해주며, 자동적으로 database 에 접근할 수 있게 해준다.
- app.config 파일이 프로젝트에 추가된다. 이 파일은 해당 database 를 위한 자동적으로 생성된 ConnectionString
   이 포함되어 있다.

위와 같은 것들은 2가지 장점을 제공해 준다.
- database 에 자동적으로 연결할 수 있게 도와준다.
- 이것은 마치 프로젝트의 포함된 객체인 것 처럼 database table 에 접근할 수 있게 도와준다.




서버 탐색기에서 사용하려고하는 DB Table 을 마우스 왼쪽 버튼으로 SQL Designer 로 드래그 앤 드롭을 한다.
해당 테이블의 디자인을 볼 수 있다.





4. LINQ 사용 예
DataClasses1DataContext db = new DataClasses1DataContext();
db.Log = Console.Out;
var query = from c in db.tbOrgChart
                  where c.DEPTH == 3
                  select c;
            
dataGridView1.DataSource = query.ToList();






5. 출력 예









cf) Connect to a SQL Database and Use the LINQ to SQL Designer
http://blogs.msdn.com/charlie/archive/2007/11/19/connect-to-a-sql-database-and-use-the-sql-designer.aspx

2009년 8월 22일 토요일

교육 Review - LINQ to DataSet : LINQ for DataSet Objects

LINQ to DataSet : LINQ for DataSet Objects






 DataSet 은 ADO.NET programming model 의 key element 이다. ADO.NET 에서 데이터베이스는 DataSet 클래스로 표현한다. DataSet 은 이름 그대로 데이터의 집합이며 테이블과 관계, 그 외 관리에 필요한 여러 가지 정보들로 구성된다. DataSet 은 ADO.NET의 주요 특징이라 할 수 있는 비연결 구조의 핵심이다. DataSet 은 메모리 내에 구현된 완벽한 데이터베이스이며 서버의 DB 전체에 대한 사본을 DataSet 객체 하나로 생성할 수 있다. 따라서 서버와 연결되지 않은 상태에서도 데이터를 검색하고 조작할 수 있는 것이다.

LINQ to DataSet 역시 In-Memory Collection 과 XML 문서를 쿼리한 방법을 동일하게 적용할 수 있다.






※ 실습 준비

1. SQL 2000 Samples DBDB 테스트 할려면 DB가 있어야 하는데... 이건 MS에서 제공하는 무료 DB 이다.
http://www.microsoft.com/Downloads/details.aspx?FamilyID=06616212-0356-46a0-8da2-eebc53a68034&displaylang=en
cfile27.uf.1646B93C50AEC26C20F509.msi


설치시 특별하게 설정할 건 없다. 설치된 디렉토리의 경로는 C:\SQL Server 2000 Sample Databases 이다.


2. SQL Server2005 SSMSEE 설치
MS 다운로드 페이지의 설명을 빌리자면
" SSMSE(Microsoft SQL Server Management Studio Express)는 SQL Server 2005 Express Edition 및 SQL Server 2005 Express Edition with Advanced Services를 관리할 수 있는 사용이 용이한 무료 그래픽 관리 도구입니다. "
http://www.microsoft.com/downloads/details.aspx?FamilyID=6053c6f8-82c8-479c-b25b-9aca13141c9e&DisplayLang=ko



역시 특별히 설정해줄것은 없다. 역시 무료라...





< 프로그램 로드 시 DataSet 객체 생성 >
DataSet dataSet;
dataSet = new DataSet("Customers");




< DataSet 의 데이터 출력 >
SqlConnection con = new SqlConnection();
con.ConnectionString = @"Data Source=.\sqlexpress;Initial Catalog=Northwind;Integrated Security=True";

SqlDataAdapter adt = new SqlDataAdapter("select * from Customers", con);
adt.Fill(dataSet);
dataGridView1.DataSource = dataSet.Tables[0];







< LINQ 를 사용한 데이터 출력 >
var query = from ord in dataSet.Tables[0].AsEnumerable()
                  where ord.Field("City") == textBox1.Text
                  select ord;
          
dataGridView1.DataSource = query.AsDataView();







< 소스 코드 >
cfile24.uf.0313B64050AEC26E0EFB5B.zip






이미지 출처 : FreeM Consulting Group.
http://www.msstudy.co.kr

2009년 8월 20일 목요일

교육 Review - LINQ to XML : LINQ for XML documents

LINQ to XML : LINQ for XML documents






LINQ to XML 은 새로운 XML DOM 이다. 새로운  XML DOM 은 standard query operators 를 이용하고, XML document 와 XML 조각을 만들기 위한 간단한 방법을 제공해 준다.

※ LINQ to XML 에서 알아야 할 것은 다음과 같다.
1. XDocument object 안에서 XML document 를 읽는 방법
2. object 로 부터 element 들을 쿼리하는 방법
3. scratch 로 부터 document 와 element 를 생성하는 방법

Namespace
LINQ to XML 은 System.Xml.Linq 를 참조해야 한다.
using System.Xml.Linq





< 예제 >

테스트할 XML 파일

 
 
 
 
 
 
 
 




XML 파일로 부터 읽은 데이터를 저장할 클래스를 정의 한다.
namespace LINQtoXML
{
    public class Customer
    {
        public string CustomerID { get; set; }
        public string City { get; set; }

        public override string ToString()
        {
            return CustomerID + "\t" + City;
        }
    }
}




XML 파일을 읽어 출력


var results = CreateXMLCustomers();
dataGridView1.DataSource = results.ToList();


코드를 보면 알 수 있듯이 LINQ 는 Generic Collection 을 사용한다. XDocument 클래스를 사용하여 XML 파일을 읽는다. Load 메소드의 인수로 XML 파일의 경로를 주면 되는데, 디폴트로는 현재 실행 파일의 위치이다. XML 파일의 내용을 제네릭 클래스에 넣어야 하기 때문에 XML 파일에서 City 와 CustomerID 값을 사용하여 새로운 Customer 객체를 생성해서 제네릭 클래스에 넣어 주었다.







City 가 London 인 사람만 출력
var results = from c in CreateXMLCustomers()
                   where c.City == "London"
                   select c;
dataGridView1.DataSource = results.ToList();





 여기서 중요한 점은 LINQ to Object 를 사용한 예제와 비교했을 때 쿼리문은 동일하다는 것이다.


< 소스 코드 >
cfile25.uf.15189F3850AEC25D27DF14.zip





위의 방법은 XML 데이터를 사용자 정의 클래스를 생성하여 컬렉션으로 만든 다음에 LINQ 를 이용한 것이다. 이 방법 외에 사용자 정의 클래스를 만들지 않고 직접 XML 문서를 쿼리하고, 또한 결과를 XML 문서를 만드는 방법이 있다. 아래 소스를 참조.

/**
@brief XDocument, XElement을 이용한 XML 직접 쿼리
@note  Load 메소드는 XDocument 클래스 타입을 리턴하며, XML 데이터 내용을 그대로 갖는다.
@note  XDoment 객체를 LINQ 쿼리 결과 XElement<> 제네릭 컬렉션 객체를 얻게 된다.
           하나의 XElement 는 하나의 레코드를 표현한다.
@note  XML 을 쿼리할 때, XML 데이터를 담을 클래스를 미리 생성하여, 그 클래스를 쿼리
           할 수도 있고, 이와 같이 직접 XML 데이터를 쿼리할 수도 있다.
*/        
private void XMLQuery()
{
    var doc = XDocument.Load("Customers.xml");
    var results = from c in doc.Descendants("Customer")
                       where c.Attribute("City").Value == "London"
                       select c;

     // dataGridView 에 출력
     DataTable tblCustomers = new DataTable("tblCustomers");
     dataGridView1.DataSource = tblCustomers;

     DataColumn col = new DataColumn("CustomerID", typeof(string));
     col.MaxLength = 10;
     col.AllowDBNull = false;
     col.Unique = true;
     tblCustomers.Columns.Add(col);
     tblCustomers.PrimaryKey = new DataColumn[] { col };

     col = new DataColumn("City", typeof(string));
     col.AllowDBNull = false;
     tblCustomers.Columns.Add(col);

     DataRow row;
     foreach (var contact in results)
     {
         row = tblCustomers.NewRow();
         row["CustomerID"] = contact.Attribute("CustomerID").Value;
         row["City"] = contact.Attribute("City").Value;
         tblCustomers.Rows.Add(row);
      }

      // 쿼리 결과로 새로운 XML 파일 생성
      XElement transformedResults = 
                    new XElement("Londoners", from customer in results
                                  select new XElement("Contact",
                                               new XAttribute("ID", customer.Attribute("CustomerID").Value),
                                               new XElement("Name", customer.Attribute("ContactName").Value),
                                               new XElement("City", customer.Attribute("City").Value)));
     transformedResults.Save("Output.xml");
}


< 소스 코드 >
cfile1.uf.1123A33A50AEC25E1F5944.zip





2009년 8월 19일 수요일

교육 Review - LINQ to Object : LINQ for In-Memory Collections

 LINQ to Object : LINQ for In-Memory Collections






LINQ를 사용하여 In-Memory Collection 에 대하여 데이터를 다룰 수 있다.






< 예제 >

제네릭 컬렉션 타입으로 사용할 클래스를 정의 한다. Customer 클래스로 다음과 같다.
namespace LINQOverView
{
    public class Customer
    {
        public string CustomerID { get; set; }
        public string City { get; set; }

        public override string ToString()
        {
            return CustomerID + "\t" + City;
        }
    }
}


Customer 클래스 타입을 사용하여 제네릭 컬렉션을 생성한다.
public IEnumerable CreateCustomers()
{
    return new List
    {
        new Customer { CustomerID = "ALFKI", City = "Berlin"    },
        new Customer { CustomerID = "BONAP", City = "Marseille" },
        new Customer { CustomerID = "CONSH", City = "London"    },
        new Customer { CustomerID = "EASTC", City = "London"    },
        new Customer { CustomerID = "FRANS", City = "Torino"    },
        new Customer { CustomerID = "LONEP", City = "Portland"  },
        new Customer { CustomerID = "NORTS", City = "London"    },
        new Customer { CustomerID = "THEBI", City = "Portland"  }
    };
}



제네릭 컬렉션 객체로 부터 모든 데이터를 읽어서 출력한다.
var results = CreateCustomers();
dataGridView1.DataSource = results.ToList();







제네릭 컬렉션 객체 데이터 중에서 City 가 London 인 사람만 출력한다.
var results = from c in CreateCustomers()
                   where c.City == "London"
                   select c;

dataGridView1.DataSource = results.ToList();







< About >
우선 관리할 데이터 정보를 담고 있는 클래스를 만들었다. 고객의 ID와  사는 도시 정보를 담고 있는  Customer 클래스 이다. 나중에 출력용으로 ToString 메소드를 재정의 하여 고객의 ID 와 도시 정보를 string 으로 출력하게 하였다.

Main 함수에서 ObjectQuery 메소드를 호출하게 되는데, ObjectQuery 메소드는 객체(즉, Collection)를 쿼리하는 메소드 이다. CreateCustomers 메소드는 고객들 각각을 Customer 객체로 생성하고, List<Customer> 제네릭 컬렉션에 담아 리턴한다. 여기서 유용하게 쓰인 문법은 "컬렉션 Initializer" 이다. 제네릭 컬렉션은 모두 IEnumerable<>를 상속받으로로 IEnumerable<> 타입으로 리턴하였다.

사용된 LINQ는 select 문으로 CreateCustomers 메소드가 리턴한 컬렉션에 대하여 도시 정보가 "London" 인 정보만 컬렉션으로 뽑았다. 뽑은 컬렉션을 순환하여 출력 하였다.
순환문은 다음과 같이 작성하여도 된다.

IEnumerator ie =  ht.Values.GetEnumerator();
while(ie.MoveNext())
{
    Button b = (Button)ie.Current;
    b.BackColor = Color.Blue;
}



< 소스 코드 >

cfile25.uf.17760F3850AEC25A2AE535.zip






이미지 출처 : FreeM Consulting Group.
http://www.msstudy.co.kr

2009년 8월 18일 화요일

교육 Review - C# 3.0 과 LINQ

C# 3.0 과 LINQ



 

C# 3.0 은 C# 2.0 의 성능을 향상 시켰다. LINQ 기술을 사용할 수 있는 문법을 지원하며, C# 2.0 의 기술을 C# 3.0 에서 그대로 사용할 수 있다.








C# 3.0 에 많은 부분이 추가되었다. 그 중에서도 위와 같은 특징들은 LINQ 를 잘 활용하기 위해 추가되었다고 할 수 있다. 위와 같은 기술들이 기존 기술들 보다는 LINQ 를 사용할 때 많이 사용되어 진다.








LINQ 의 쿼리는 대상이 컬렉션으로만 만들 수 있다면 어떤 대상이라도 가능 하다. 즉, 모든 컬렉션에 대해 쿼리 작업을 진행한다. 따라서 대상이 무엇이든 동일한 쿼리문을 사용한다.  프로그래밍 언어와 통합 했다는 뜻은 기존에는 쿼리문을 문자열 형태로 날리기 때문에 컴파일 타임때는 쿼리문의 잘잘못을 따질 수 없지만, LINQ 는 쿼리문 자체가 언어의 문법이므로 컴파일 타임에 잘못된 쿼리문을 판별할 수 있다. 따라서 개발 효율성이 높다. 쿼리문 작성 시 Visual Studio 인텔리센스가 도움을 주기 때문에 쉽게 작성할 수 있다.

1. LINQ APIs 는 Visual Studio 의 IntelliSense 의 도움을 받는다.
2. 쿼리문을 문자열로 작성할 필요 없이, 직접 LINQ 쿼리문을 이용하며, 이는 컴파일 타임에 검사된다.








.NET LINQ 를 이용할 수 있는 언어는 현재(2009년 08월) C#3.0, VB.NET 9.0 뿐이다. 앞으로 더 늘어날 것이라고 한다. 이런 언어들을 통하여 LINQ 를 사용하는데 쿼리 대상에 따라 크게는 3가지로 나눌 수 있다.

Object 를 대상을로 하는 LINQ to Object
RDB를 대상으로 하는 LINQ to ADO.NET (LINQ to DataSets, LINQ to SQL, LINQ to Entities)
XML을 대상으로 하는 LINQto XML

분류를 나눠 놓기는 했지만 LINQ 사용법은 동일하다.









자료 출처 : FreeM Consulting Group.
http://www.msstudy.co.kr

교육 Review - Generic Collection, Anonymous Type var, Func

Generic Collection


사용자 정의 클래서 Customer 로 컬렉션 객체를 생성과 동시에 초기화 하는 예제.
컬렉션 객체를 DataGridView 에 출력할 때 ToList 메소드를 사용하지 않아도 된다.

// 컬렉션 객체 생성과 동시에 초기화
//    - collection initializer 사용
List myCustomer = new List()
{
    new Customer() {CustomerName="홍길동", Age = 10, EmailAddress = "se"},
    new Customer() {CustomerName="홍길동2", Age = 20, EmailAddress = "se2"},
    new Customer() {CustomerName="홍길동3", Age = 30, EmailAddress = "se3"},
    new Customer() {CustomerName="홍길동4", Age = 40, EmailAddress = "se4"},
    new Customer() {CustomerName="홍길동5", Age = 50, EmailAddress = "se5"},
    new Customer() {CustomerName="홍길동6", Age = 60, EmailAddress = "se6"},
    new Customer() {CustomerName="홍길동7", Age = 70, EmailAddress = "se7"}
};

// 얻은 리스트(컬렉션)를 Grid 에 출력
//   - 컬렉션은 ToList()를 사용하지 않아도 된다.
dataGridView1.DataSource = myCustomer;









Anonymous Type (역명타입) var


익명 타입은 선언과 동시에 초기화 해줘야 한다. Java 에도 익명 타입이 존재 한다. .NET 의 익명 타입이  Java 의 익명 타입과 다른점은 익명 타입이 처음에 한번 임의 타입으로 결정 되면 더이상 다른 타입으로 변경 불가능 하다.

// anonymouse type : var 는 선언과 동시에 초기화 해야 한다.
// java 의 var 와 차이점
//   - 한번 선언된 var 는 고정된다.
var num = 10;

// 예제2
List myCustomer = new List()
{
    new Customer() {CustomerName="홍길동",  Age = 10, EmailAddress = "se",  Title="사원"},
    new Customer() {CustomerName="홍길동2", Age = 20, EmailAddress = "se2", Title="사원"},
    new Customer() {CustomerName="홍길동3", Age = 30, EmailAddress = "se3", Title="사원"},
    new Customer() {CustomerName="홍길동4", Age = 40, EmailAddress = "se4", Title="사원"},
    new Customer() {CustomerName="홍길동5", Age = 50, EmailAddress = "se5", Title="사원"},
    new Customer() {CustomerName="홍길동6", Age = 60, EmailAddress = "se6", Title="사원"},
    new Customer() {CustomerName="홍길동7", Age = 70, EmailAddress = "se7", Title="사원"}
};

// c는 컬렉션의 타입에 의존
// C# 3.0 에 추가된 문법은 같이 쓰인다.
//    - Sum 은 Extention Method
int num2 = myCustomer.Sum(c => c.Age);
MessageBox.Show(num2.ToString());

// LINQ 의 사용
// in 뒤에는 컬렉션(하나의 타입을 담고 있는)이 온다. 그렇지 않다면 컴파일 에러
// select new 는 특정 컬럼만 빼오는 것
// 필드는 링큐로 받았을 때 표현이 안된다.
var query = from cus in myCustomer
                  where cus.Age > 20
                  // select cus ;
                 select new { cus.CustomerName };
dataGridView1.DataSource = query.ToList();










Func


Func 는 일종의 델리게이트 이다.
델리게이트 처럼 미리 메소드의 시그내처대를 선언해 주지 않아도 된다.

// Func : 델리게이트 이다.
private void button3_Click(object sender, EventArgs e)
{
    // 생성
    Func myF = new Func(MyMehtod);

    // 호출
    int num = myF(10);
    MessageBox.Show(num.ToString());

    // 호출2
    // 람다 식은 func 안에서만 사용 가능하다.
    Func myF2 = new Func(p => p + 1);
    int num2 = myF2(10);
    MessageBox.Show(num2.ToString());
}

// func로 연결할 메소드
public int MyMehtod(int i)
{
    return i + 100;
}




cfile3.uf.154BDF4650AEC255011514.zip


교육 Review - Extention Method (확장 메소드)

Extention Method (확장 메소드)




확장 메소드는 C# 3.0 에 추가된 문법으로 기존 클래스에 수정없이 메소드를 추가 시키는 문법이다. 이를 이용하면 기존의 BCL 에 사용자가 임의의 메소드를 추가 시킬 수 있다.

규칙!!
static 클래스를 정의하여 추가 하고 싶은 메소드를 static 메소드로 추가한다.


< 예제 >

버튼 객체(Button)에 Greeting 이라는 새로운 메소드를 추가하는 예제 이다.

namespace ExtentionMethodDemo_6
{
    public static class MyClass
    {
        public static void Greeting(this Button str)
        {
            Console.WriteLine("Hello");
        }

        // 오버로딩 가능, 인자 개수 제한 없음
        public static void Greeting(this Button btn, string message)
        {
            MessageBox.Show(message);
        }

        // 오버로딩 가능, 인자 개수 제한 없음
        public static void Greeting(this Button btn, int a)
        {
            MessageBox.Show(a.ToString());
        }
    }
}

MyClass 라는 static 클래스를 추가하고, Greeting 이라는 새로운 매소드를 작성 하였다. 매소드의 첫 번째 인자는 추가 대상이 되는 객체이다. 여기서는 버튼 객체에 추가 하려고 하므로 this Button 이라고 작성해 준다. this 를 써주는 것은 문법! 그냥 외워라!!. 두 번째 인자부터가 매서드의 인자가 된다. 여러개의 매서드를 넣을 수도 있다. 또한 확장 메서드는 Overloading 을 지원하기 때문에 다른 인자로 하여 작성할 수 있다.





메인 코드로 와서 Button 객체인 button1뒤에 . 을 찍어보면 사용 가능한 멤버가 보이는데 확장 메서드로 작성한 Greeting 메서드가 보인다. Greeting 앞에 빨간색 상자와 아래로 향하는 화살표가 보이는데 이것이 확장 메서드를 나타내는 아이콘이다.







Greeting 메소드를 오버로딩 하였기 때문에 위와 같이 오버로딩된 메소드 목록을 볼 수 있다.



< 예제 소스 >
cfile4.uf.111D533750AEC25416C29F.zip


교육 Review - UnitTest

UnitTest




 .NET 응용프로그램을 만들때는 아직 UnitTest 가 익숙하지 않다고 한다. Java 같은 경우에는 UnitTest 가 많이 사용된다고 하는데...  UI에 의존적이지 않은 UnitTest! 간단하게 클래스 라이브러리(DLL) UnitTest 하는 예제이다.


< Class1Test.cs >

두 개의 정수값을 받아 그 합을 리턴하되, 합이 100 보다 작으면 100을 더해서 리턴하는 메소드 이다.

namespace UnitTestDemo_4
{
    public class Class1
    {
        public int AddTo(int i, int j)
        {
            int num = i + j;

            if (num < 100)
                num += 100;

            return num;
        }
    }
}




< 단위 테스트 코드 만들기 >

메소드 안에 마우스 커서를 위치 시킨 후 마우스 오른쪽 버튼을 누르면 "단위 테스트 만들기" 라는 팝업 메뉴가 있다. 이 메뉴를 선택해 주자.







테스트 하려는 메소드(AddTo)를 체크 한다.
출력 프로젝트 설정에는 "새 Visual C# 테스트 프로젝트 만들기.." 를 선택한다.





새 테스트 프로젝트 이름을 입력해 준다. 디폴트로 생성된 이름을 그냥 사용하였다.





솔루션 탐색기를 보면 위에서 입력한 프로젝트 이름으로 새로운 프로젝트가 추가 되었다. 테스트 하기 위해선 시작 프로젝트를 새로 생긴 프로젝트(TestProject1) 으로 설정하자. 클래스 라이브러리 자체만으로 실행 할 수는 없으니까.





새로 생긴 프로젝트의 "Class1Test.cs" 파일에 보면 메소드 이름이 "테스트할 메소드 이름 Test"(AddToTest) 메소드가 작성되어 있다. 테스트할 메소드가 받는 두 개의 인수가 명시되어 있는데 이 곳에 직접 입력값을 작성해 준다. expected 변수는 예상되는 리턴값이다. 이 값 역시 예측하여 작성해 준다. 마지막 라인에 Assert.Inconclusive 메소드는 실행 시 에러를 리턴하므로 주석 처리 해주자.





입력값에 대하여 예상된 결과 값이 맞다면 위와 같이 "성공" 이란 결과를 볼 수 있다. 입력값이 Built-In 타입이 아니라 사용자 정의형 이라면 그에 맞게 조금더 수정을 해주면 된다.



< 소스 코드 >
cfile2.uf.1133BD3750AEC2521CE6F7.zip