( 어제 다 쓰려고 했는데, c2.net 번역 게시물들을 읽다가 시간이 Over되는 바람에 다 적지 못했습니다. )
4. 데이터 접근용 Class (계속)
앞의 Post에서 실제적인 데이터 접근용 클래스의 틀을 만든 거라면, 지금 부터는 그 안에서 실제적인 동작과, 그 활용 방법을 구현하는 작업을 한다.
a. Select
가장 기초적인 구현입니다. 저장된 데이터가 있어야 겠지만, 대부분의 DB 관련 프로그래밍을 하면, 이 Select 에서 시작해서 Select로 끝나는게 대부분이라 생각한다.
앞의 내용까지 따라갔다면, GuestBookDataSource 라는 이 클래스가 있을 것이고, 그 클래스 안에 아래와 같은 Method를 추가한다.
public IEnumerable<GuestBookDataEntry> Select()
{
var result = from g in this.context.GuestBookDataEntry
where g.PartitionKey == "GuestBookRawData"
select g;
return result;
}
이 내용은 그 Hands on Lab의 내용을 그대로 차용한 것이다.
사실 이 문구를 보고서 심각하게 LINQ 부터 시작해야 할 까? 고민을 많이 했다. 아니 C# 안에서 form, select, where를 쓰는 건 이거 솔직히 반칙이다. MS의 수석 엔진니어들에게 욕이라도 퍼붑고 싶어졌다. 그러나 이미 구현되어 있고, 알게 모르게 .NET 개발자들은 이를 새로운 기술로 받아들여 사용 중이니, 불만은 내 속에서만 삭히고 결국 문법을 가만히 보게 되었다.
먼저 Return Value.
아마 DataTable 이라는 개체를 통해 Select 명령어를 쓰신 분은 약간 부담이 없을듯 싶다.
DataTable.Select()의 결과는 DataRow의 배열 값인데, 저 IEnumerable<GuestBookDataEntry> 라는 것은, GuestBookDataEntry의 배열과 같은 형태라 보면 된다. 단지, 배열보다 더 많은 기능과 활용도가 있다는게 그 차이. 그냥 배열 형태를 좀 멋지게 그리고 유연하게 Return 하는 구나 하고 넘어가면 된다.
자, 이제 부터 진정한 산맥.
var.
무슨 JavaScript도 아니고 var 라니. 그러나 이것도 엄연한 C#의 Keyword. 즉 외부에서 온 값 중 그 형태를 정의할 수 없는 기묘한 값인 경우 이 var를 사용한다. 물론 단항이면 object 한방이면 모든 데이터형 감쌀 수 있지만, 저게 배열일지, 단항일지는 모른다. 그래서 var를 사용한다. var 형태로 정의된 변수는 그 데이터가 처음 들어가는 순간 그 형이 결정된다.
- var strVal = “aaaa”; // strVal은 이 순간 문자열로써 동작해버린다.
- var intVal = 55; // intVal은 이 순간 int (정수형)으로써 동작해 버린다.
- var dblVal = 5.555; // dblVal은 이 순간 dobule 로써 동작해 버린다.
참… 알기 쉬운 내용이지만, 순간 쓰긴 참 뭐시기한 내용이다. 그래서 var는 초기화 자체를 할 때 아주 고민 지대로다. 그래서 위와 같은 메소드를 만들때, 그냥 var result = …; 라고 적어버렸다.
다음은 from .. ..
앞서 만든 클래스 내용 중 GuestBookContext 라는 것이 있다. 그 안에 아무 생각없이 만든 프로퍼티인 GuestBookDataEntry 라는게 있다. 아마 Return 값이 IQueryable<GuestBookDataEntry> 인데, 바로 이 프로퍼티를 통해서 마치 DB의 Table 처럼 쿼리 할 수 있는 것이다. 아마 내부적으로는 Select … from .. 의 from 처럼 DB 테이블에서 값을 긁어오기 위한 무언가의 작업을 수행하는 것이다.
여튼 이 from 뒤에 쓰일 수 있는 것인 TableServiceContext를 상속받은 클래스에서 IQueryable<T>의 Return 값을 돌려주는 프로퍼티를 써야 된다는 사실만 기억하면 된다.
아 여기서 Table의 문법은 다음과 같다.
<변수명> in <context>.<Property>
즉 아래와 같이 볼 수 있다.
g in this.context.LottoSvcDataEntry
그리고 where .. ..
뭐 from 나올때 이미 감 다 잡으셨지만, 맞다. select form where 이다. 단지 그 순서가 미묘하게 다를 뿐.
SQL의 where 절 쓰듯이 쓰면 된다. 단지 비교 연산자가 C#에서 사용되는 비교 연산자의 차이일 뿐이다.
즉 = –> == 로 AND는 && 로 쓰면 된다.
마지막으로 select
select 뒤에 필드명 적게 되어 있는데, 이 필드를 무작정 적는게 아니고, 앞서 Table 선언할 때 사용한 변수를 이용하여 사용한다. 즉 from <변수명> in <IQueryable 프로퍼티> 의 형태에서 있는 <변수명> 이거다.
위의 예제에서는 g 라는 변수를 썼으니 여기서는 g 라고 썼다. 만을 그냥 그 변수명을 쓰면 “*”의 의미가 된다.
아직 실험은 안해봤지만, 특정 필드의 값을 빼려면, <변수명>.<필드명> 하면 될 거라 생각된다. 즉 이름 빼오기 할 때, g.Name 하면 되지 않을까 싶다.나중에 한번 해보려고 한다.
여튼 이와 같이 select 문을 만들어 var의 변수에 때려 박으면 IEnumerable<T> 형태로 값이 돌아간다.
값이 있든 없던 위의 형태로 들어가게 된다.
b.Insert
사실 이 부분 부터 해야 select 테스트도 되지만, 사실 SQL의 핵은 Select 다 보니, Select를 먼저 설명하게 되었다. 애석하게도(?) Insert는 위의 형태 처럼 C# 같지도 않은 문법으로 처리하지 않는다. 즉 아주 평범하게 (?) Insert를 한다.
먼저 Insert 할 데이터 기본형인 Entitiy 클래스를 new 해서 만든다. 그리고 그 데이터에 값을 넣고, AddObject를 통해 값을 밀어 넣는다. 마지막으로 Save 한다. 이게 전부다.
즉 위의 예제로 본다면 아래와 같다.
public void AddDataEntry(string Name, string Email, string HomePageUrl)
{
GuestBookDataEntry newItem = new GuestBookDataEntry ();newItem.Name = Name;
newItem.Email = Email;
newItem.HomePage = HomePageUrl;
newItem.writtenDate = DateTime.Now;this.context.AddObject("GuestBookDataEntry ", newItem);
this.context.SaveChanges();
}
즉 newItem 이라는 변수에 새로운 데이터를 넣을 공간을 만들고, 그 안에 데이터를 채우고 난 뒤, AddObject 함수를 통해 값을 밀어 넣는다. 이 때, AddObject 라는 함수 첫번째 인자가 문자열을 넣게 되어 있는데, 이 부분에 Entity에 해당하는 클래스 이름을 넣는다. 즉 위에서는 GuestBookDataEntry 이니, 이를 넣으면 된다.
(내부적으로 어셈블리에 대한 reflect 작업을 할 때 사용되는 것 같다. 만일 저 문자열이 틀리면 알아 먹기 힘든 오류가 나기 때문에 주의해서 넣도록 한다. )
자 일단 변경작업을 수행했으니, 마지막으로 SaveChanges 함수를 불러 저장하도록 한다.
c.Update
이것 역시 Insert와 유사하다. 해당하는 Entity의 개체를 꺼내고 그 개체의 내용을 변경한 뒤, 변경용 메소드인 UpdateObject 를 호출하고, 마지막으로 SaveChange 한번 해주면 된다.
뭐 역시 이 또한 코드로 보는게 제일 빠를 듯.
public void ChangeHomePageUrl(string Name, string HomePageUrl)
{
var result = from g in this.context.GuestBookDataEntry
where g.PartitionKey == "GuestBookRawData" && g.Name == Name
select g;if(result.Count<GuestBookRawData>() == 1)
{
GuestBookDataEntry curEntry = result.ElementAt<GuestBookRawData>(0);
curEntry.HomePage = HomePageUrl;
this.context.UpdateObject(curEntry);
this.context.SaveChanges();
}
}
보면 특정 Entity의 값을 가져와서 그 값을 변경한 뒤, UpdateObject를 부르고 다시 SaveChange 하는 것을 볼 수 있다. (굵게 칠해진 부분만 참조)
기존 값을 불러오는데는 앞서 사용한 select를 사용했다. 단, 조건이 하나 더 붙는데, Name이 같은 이름을 가진 Name 인경우에 해당하는 것이다. ( 물론 Name이 둘 이상이면 처리하지 않는 문제점은 그대로 안고 가지만… ) 일단, 무조건 이름이 1개라는 가정에서 해당하는 이름이 적은 내용을 가져온 뒤, 최종적으로 꺼내와서 그 안의 값을 변경했다. 여기서는 HomePage가 그 값을 의미한다.
그리고 나서 UpdateObject를 호출한다. AddObject와는 다르게 이미 존재하는 Entity이기 때문에, 굳이 클래스명 따위는 넘길 필요가 없을 것이다. 업데이트가 완료되면 마지막으로 Save를 하는 작업을 하게 된다.
d.Delete
이제 Action의 마지막 Delete 이다. Update와 모든 동작이 동일하며, 단지, UpdateObject를 쓰는게 아니라, DeleteObject를 쓰면 된다. 특정 이름의 항목을 지우는 로직이다.
public void DeleteBook(string Name)
{
var result = from g in this.context.GuestBookDataEntry
where g.PartitionKey == "GuestBookRawData" && g.Name == Name
select g;if(result.Count<GuestBookRawData>() > 0)
{
foreach(GuestBookDataEntity entry in result)
{
this.context.DeleteObject(curEntry);
}
this.context.SaveChanges();
}
}
Select로 각 Entity 들을 빼내고 그 내용을 DeleteObject를 호출 한 뒤, 맨 마지막에 SaveChanges()를 호출한다.
5. 데이터 활용.
자 이제 만들어진 DataSource를 가지고 데이터 조작을 해보도록 한다.
사용자와 직접적인 I/F를 담당하는 것은 Web_Role 부분. 기본적인 Web-Role을 만들었다면, 별도 Web Project가 한개 열렸을 것이다. 이 Web-Role용 프로젝트에서 참조를 하나 추가한다. 추가할 참조는 바로 이 DataSource가 담긴 프로젝트. 일단 참조 관계가 만들어져야 Web-Role에서 사용 가능하다.
먼저 기본적으로 생성된 Default.aspx 의 코드에서 Select를 호출해서 사용해 본다.
DataSource로도 바로 이용가능하기 때문에, Hands on Lab을 보면, DataGrid를 이용하여 한 큐에 작성한 결과물을 보여준다. 이 방법도 좋지만, 좀 무식하게 만들어 봐야 실제로 데이터를 꺼내고 하는게 눈에 보일 것 같다.
먼저 TextBox를 Web-Form에 추가한다.
그리고 난뒤 그 TextBox의 이름은 txtResult로 한다. 또, TextMode를 MultiLine으로 하고 Rows를 10 정도로 하자. 그러면 TextArea가 되니까, 결과물 보기에 그냥이다.
이제 다음과 같은 코드를 Page_Load 메소드 안에 추가한다.
GuestBookDataSource ds = new GuestBookDataSource();
IEnumerable<GuestBookDataEntry> result = ds.Select();
System.Text.StringBuilder sbResult = new System.Text.StringBuilder();
foreach (GuestBookDataEntry entry in result)
{
sbResult.Append(entry.Name);
sbResult.Append("(");
sbResult.Append(entry.Email);
sbResult.Append(" / ");
sbResult.Append(entry.HomePage);
sbResult.Append(")");
sbResult.Append(entry.writtenDate.ToString());
sbResult.AppendLine();
}
txtResult.Text = sbResult.ToString();
데이터를 꺼내는 방법이 보이지 않는가? 그냥 GuestBookDataSource를 생성하고, 그 결과 값을 이용하여 메소드를 호출하기만 하면 된다. 그리고 그 결과값을 나열하면 된다.
Insert나, Delete, Update도 마찬가지로 GuestBookDataSource를 생성한 뒤 불러 주면 된다.
6. 정리.
데이터 처리 관련해서 올려서 정리해봤다. 사실 LINQ에 대한 기본 지식이 거의 전무하기 때문에, 이 내용에 대해 정확한 동작 방법이나, 올바른 방법이나, 효율적인 처리에 대해서는 거의 알지 못한다.
단지, Hands on Lab을 따라하다가 보니, 이렇게 짜는 구나 정도이다.
필자 처럼 구현에 성급하신 분은 한번 위의 방법대로 구현해 보면 될 것이다.
( 현재 Lotto 당첨 번호 관련된 서비스를 한번 만들고 있다. 별로 로직은 어렵지 않은데, 은근히 Azure 자체의 벽이 높아서 당황했다. 그 중간의 결과를 여기에 기록으로 남기고 있다. )