'분류 전체보기' 카테고리의 글 목록 (2 Page) :: iopeni - Think of C#



너무 욕심이 과한 건지 모르겠습니다. 사실 엔터프라이즈 라이브러리의 한 파트인 프리즘을 공부 하기엔 제 실력이 너무 허접할 지도 모릅니다. 


하지만... 안할 수 없기에 시작 했습니다. 아무래도 이젠 WinForm 은 버려야 할 듯 합니다. 다음 달이면 Windows 10이 출시 됩니다. 사실 너무 늦게 공부를 시작 하는 듯 합니다. 


국내에선 사실 제대로 된 번역서 한권 없고, 제대로 된 예제 코드 하나 없고, 공부 하기 쉬운 것도 아니고 암튼 그렇습니다. 몇몇 회사에서 프리즘을 사용하여 솔루션을 개발 하고 있다고는 하나 성숙도는 사실 낮습니다. 제가 이런걸 평가 할 수 있는 실력도 아니지만 사실 그렇습니다. 그 만큼 프리즘을 잘 알고 잘 하는 개발자는 드뭅니다. 선구자는 아니어도 세상에 처지지는 말자 라는 것이 저의 작은 소망 입니다만 늘 세상을 따라잡기는 벅차 군요~!!!


일단 프리즘을 다운 로드 받을 수 있는 사이트를 남깁니다. 전 그냥 Nuget으로 다운 받아 버리는 주의라.. 필요 없긴 합니다만... 그래도 프리즘 개발자들과 소통을 할 수는 있습니다.


http://compositewpf.codeplex.com/


프리즘을 설치 하고 제일 먼저 해야 하는 일은 Bootstrapper를 작성해야 합니다. 이 부트스트래퍼가 뭘 하고자 하는 놈이지 알아야 할 겁니다. 


자세한 내용은 MSDN 을 참고 하시길 바랍니다.

https://msdn.microsoft.com/en-us/library/ff921139.aspx


MSDN 가라사대~


"부트 스트랩은 프리즘 라이브러리를 사용하여 구축 된 응용 프로그램의 초기화를 담당하는 클래스입니다. 부트 스트랩을 사용하여 프리즘 라이브러리 구성 요소가 응용 프로그램에까지 연결되는 방법을보다 상세하게 제어 할 수 있습니다." 라고 정의 하고 있습니다. 


위 그림을 보면 첫 단계는 컨테이너 설정 입니다. 뭔가 담길 저장 공간을 설정 하는 군요. 그 이후 레전 맵핑 이라는 것을 합니다. 그리고~~ 대망의 화면을 생성 하는 군요. 그 다음에 각각의 구성 요소들 즉 모듈을 초기화 합니다.


Prism은 부트스트래퍼로 다양한 외부 라이브러리 또는 프리즘에 포함된 MEFBootstrapper등을 사용 할 수도 있습니다만, 저는 프리즘에 포함 되어 있는 UnityBootstrapper를 사용하고자 합니다. 


UnityBootstrapper의 원형을 살펴 보면


UnityBootstrapper는 Bootstrapper를 상속 받았군요. Bootstrapper의 원형도 살펴 보아야 할 듯 합니다.


원형만 바라 보면 이걸 어떻게 하라는 것인지 좀 막연합니다. 이런 코드를 보고 아하~~ 하고 뭔가 눈치를 챌 수 있으면 좋으련만... 전 아직 멀었나 봅니다. 그래도 원형을 살펴 봤으니 억측이라도 해 봐야 겠죠.


원형을 가만히 들여다 보시면 아직 구현 되지 않은 항목이 딱 하나 보입니다. 바로 Bootstrapper 클래스의 CreateShell 메소드가 구현되지 않았습니다. 


이건 확실하게 사용자가 만들어야 하는 클래스가 맞겠군요. 더군다나.. Shell 이라는 건 WPF의 MainPage를 이야기 하는 것일 테니 만들어진 메인 화면을 CreateShell 메소드를 오버라이드 하여 재정의 하도록 하죠.


그런데 말이죠.. 여기 Bootstrapper에 보면 멤버로 IUnityContainer 항목이 있습니다. UnityContainer는 Dependency Injection(의존성 주입) 컨테이너 입니다. 즉 여기에 메인 화면을 넣어 주면 될 듯한 분위기 입니다.


코드 확인 하도록 하죠.


햐~ 어렵습니다. 이게 MainWindow Shell = new MainWindow(); 와 같은 문장이라고 해야 겠죠?
그 다음입니다.. 생성을 했다면 초기화를 해야 겠죠.



일단 이까지가 윈도우를 띄우는 루틴 입니다. 이거 참... 그냥 윈도우 띄우는 것보다 한참 복잡하고 어렵네요 코딩량도 장난 아니구요. 그런데 이걸 왜 쓸까요? 이걸 왜 써야 하는지는 천천히 차근 차근 하다 보면 알아 질껍니다. 명확한 이유는 이거죠. 프리즘은 마이크로소프트의 대표적 의존성 주입 패턴의 결정체 입니다. 


쉘을 생성하고 호출 하여 화면에 띄우기는 했는데... 그 다음은 화면에 뭔가 표시 해야 겠죠. 여기서 나오는 방법이 이제 Region 이라는 것과  Module 이라는 것입니다. 


MSDN 가라사대~~


"모듈은 독립적으로 개발, 테스트 및 (옵션) 배포 할 수있는 기능 패키지입니다. 많은 상황에서는 모듈이 개발되어 다른 팀에 의해 유지됩니다. 전형적인 프리즘 응용 프로그램은 여러 모듈에서 구축되어 있습니다. 모듈은 (예를 들어, 프로파일 관리) 특정 비즈니스 기능을 설명하며, 이러한 기능을 구현하는 데 필요한 모든 뷰, 서비스 및 데이터 모델을 캡슐화하는 데 사용할 수 있습니다. 모듈은 여러 응용 프로그램에서 재사용 할 수있다 (예를 들어, 로깅 및 예외 관리 서비스를위한) 일반적인 응용 프로그램 인프라 나 서비스를 캡슐화하는 데 사용할 수 있습니다."


라고 정의 되어 있습니다. 


Bootstrapper의 원형을 다시 한번 확인 해 보면 대충 눈에 보이는 것이 ConfigureModuleCatalog()라는 원형을 볼 수 있습니다. 반환값은 void 이군요.


일단 Module 클래스를 하나 만들어 보겠습니다. 저 ConfigureModuleCatalog 메소드도 뭔가 넣어야 될 클래스가 필요 할 테니 말이죠.


이렇게 작성을 하고 Bootstrapper의 모듈카탈로그에 등록 하도록 하죠. 추후 Region 맵핑을 하기 위한 키로 "MainRegion" 이라는 스트링으로 등록 합니다.


이 코드를 보시면 ModuleCatalog는 Generic Collection 이라는것 정도는 설명 드리지 않아도 아시겠죠? 

자 이제 까지 하고 보면 참 이상한게 보입니다.코드 어디에서도 ModuleA를 생성 호출 하는 녀석이 없습니다. 이게 바로 Region의 마술 입니다. 일단 우리는 앞에서 MainWindow를 초기화 하고 Show 하였습니다.


그 MainWindow에 xmlns:cal="http://www.codeplex.com/prism" xml namespace를 추가 하도록 합니다. 그리고 Region 맵핑 하도록 하겠습니다. 전체 xaml 코드를 보도록 하죠.


MainRegion을 RegionManager가 모듈 카탈로그를 뒤져서 타입을 찾고 생성하고 초기화 하여 화면에 표시 하는 일을 수행 합니다.

 

정말 어렵습니다. 지금까지 작성한 코드를 보면 단 한군대에서도 명시적으로 new를 이용하여 클래스를 생성하고 사용한 적이 없습니다.


여러분이 감 잡으셨을 지도 모르겠지만 new를 쓰지 않았다는 것은 정말 커다란 의미를 내포 하고 있습니다. 이것은 다시 말해 명시적으로 하위 클래스를 강하게 결합하지 않고 있다는 것 입니다. 결국 하위 클래스는 존재 하여도 그만 존재 하지 않아도 그만 이라는 소리 인데, 이것이 바로 마틴 파울러가 처음 이야기 하였던 제어 역행화(IoC) 이론이며, 어플리케이션을 더욱 견고 하게 만드는 방법 이며...어쩌구 저쩌구.... 


어찌 되었든 프리즘 라이브러리가 어디선가는 new를 이용하여 객체를 생성하고 반환할 것입니다. 그것이 우리 눈에 보이지 않아 정말 어렵게 느껴지고, 개발자가 하지 않은 일을 라이브러리가 순서에 맞춰 대신 해주고 있기 때문에 연결 고리가 쉽게 와 닿지도 않습니다. 정말 어렵습니다. 그러나 멀찍이 서서 조금만 쉽게 바라보면 명확하게 각각의 영역이 분리 되어 있습니다. 개발자는 조금 더 비지니스에 집중 할 수 있도록 만들고 있습니다.


이것이 프리즘의 매력 입니다. 이제 시작 입니다. 해야지 해야지 했던 프리즘 공부를 이제 시작 하려고 합니다. 오늘 부터 말이죠....


장문의 글 읽느라고 고생 많으셨습니다.


그럼 전 이만~~~~~ 


 

Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,



뜬금이 없다.. 랄까? 갑자기 웬 API??랄까? 이젠 쓸 일도 없는 녀석을 왜??? 랄까? 암튼 좀 그렇다.

작금의 상황은 이런 SendMessage보다... 더 좋은 모델들이 많이 있다. 굳이 SendMessage를 쓰는 것은... ㅜ,.ㅜ;; 좀 절망 이라고 할까.... 물론 가볍고 빠르다는 장점으로 인해 이 SendMessage를 사용하는 분들도 많이 있다.


어찌되었든.. 이 SendMessage라는 녀석을 이해 하려고 하면 기본 지식 부터 정리 하는 것이 좋을 듯 하다. 


옛날 DOS 시절을 생각 하면 그 땐 너무 행복한 시절이었다. 이런 SendMessage같은 녀석들이 없었으니 말이다. 하긴 그땐 인터럽트 때문에 어려웠다.


우리가 사용하고 있는 윈도우은 멀티 태스킹 환경이라는 것은 뭐 설명하지 않아도 다들 알고 있는 내용일 것이다. 모르는 사람들을 위한 팁으로 멀티 태스킹 환경이라는 것은 동시에 여러개의 프로그램을 실행 할 수 있는 환경 이라는 소리다. 


여러개의 프로그램이 동시에 실행 된다면 각각의 프로그램은 메모리 영역을 할당 받게 되는데 이는 보호되고 있어서 해당 프로그램 이외에 다른 프로그램은 메모리 영역에 접근 할 수 없다. (옛날 윈도우 3.1 또는 윈도우 95 시절에 윈도우가 무수히 블루 스크린을 띄웠는데 이게 이 메모리 영역의 침범으로 인한 오류로 컴퓨터를 재부팅 해야 하는 시절도 있었다. 지금은 거의 없어진 증상이지만...)


이렇게 여러개의 프로그램이 실행되어 있는 상황에서 각각의 프로그램들이 특정 데이타를 공유 할 필요성이 대두 되었고 여러개의 프로그램이 데이타를 공유 할 수 있는 방법을 통칭 IPC(Inter-process Communication)라고 한다.


그러하기에 IPC라는 것은 여러가지 방법을 가지게 될 것이다. 프로세서간 정보만 공유 하면 되는 것이기에 소켓 또는 파일 또는 레지스트리등을 사용할 수도 있을 것이다. 정말 많은 방법이 있을 것이므로 우리는 이 많은 방법들 중에 어떤 방법을 선택 해야 하며, 이때 고려 해야 할 요소들이 있는데 대충 다음과 같다.


1. Network을 고려 해야 하는가?

   1-1 통신 하려고 하는 대상의 OS가 같은가?

   1-2 통신 하려고 하는 대상의 OS종류는 한가지 인가?

2. 통신의 속도가 중요하게 고려 되어야 하는가?

3. 반복적 정보 공유 인가?

4. 통신하고자 하는 정보의 사이즈는 큰가? 작은가?


여러가지 고려 요소를 생각 하여 방법이 정해 지면 추 후 방법을 바꾸고자 할 경우 무척 어려운 상황이 발생 되므로 처음 선정 할때 부터 주의를 요해야 한다.


그럼 우리가 사용하는 Windows 환경에서의 메시지 전송 방법엔 어떤 방법들이 있는지 보자. 


1. 메지시 : 사용자 정의 메시지로 정보 전달 한다.

2. 클립보드 : 중앙 저장소를 이용한 대량의 정보 교환

3. DDE : Do Data Exchange를 이용한 지속적 정보 교환

4. 파일 맵핑 : 파일 맵핑을 이용한 메모리 공유

5. 메일 슬롯 : 메일 슬롯 사용

6. 파이프 : 파이프 사용

7. 윈속 : 소켓 사용

8. RPC, RMI : Remote Procedure Call, Remote Method Innovation 을 이용한 방법

9. COM, DCOM, COM+, WebService, WCF 등을 이용한 방법.


뭐 이 포스트에서 이야기 되지 않은 다른 방법들도 많을 것이다. 사실 IPC라는 것이 데이타를 공유만 하면 되는 것이기에 그 목적에만 맞으면 IPC라고 할 수 있을 것이다.


오늘의 포스팅 목적은 사실 위 많은 IPC 중에 메시지에 해당하는 내용을 살펴 볼 것이다.


메시지는 사실 정보를 교환 할 수 있는 방법중에 가장 간단하고 가장 가볍고 가장 빠른 방법이다. 이 메시지를 이용하기 위하여 우리는 OS에 대한 어느 정도 기초적 지식을 습득해야 한다. 


가장 처음 이해 해야 되는 내용은 Message Queue 이다.


일단 힘들게 그림 그린 메시지의 흐름도 부터 확인 하자.




메시지 큐는 여러분이 키보드를 이용하여 입력 하거나 마우스를 가지고 아이콘을 클릭 하거나 하는 일련의 동작들이 담기게 되는 일종의 자료 구조 이다. 이 메시지 큐는 무한 루프를 돌면서 사용자의 입력을 기다리게 된다.


메시지루프의 형태는 다음과 같다.



GetMessage 함수는 메시지 큐로 부터 메시지를 가지고 온다. DispatchMessage는 WndProc 함수가 메시지 구조체에 들어 있는 윈도우 핸들을 이용할 수 있도록 메시지 데이터를 WndProc함수로 보낸다. 결국 우리는 메시지를 가로채기 위해 WndProc함수를 Override 하여 재정의 사용한다. 


TranslateMessage()는 DispatchMessage 전 키보드 또는 입력 메시지를 전 처리 할 수 있다. 이 함수는 PreProcessMessage로 보낸다. PreProcessMessage를 Override 하여 재정의 사용한다. 매개 변수에는  WM_KEYDOWM, WM_SYSKEYDOWN, WM_CHAR 및 WM_SYSCHAR 값을 사용할 수 있다. 


다음 그림을 통해 메시지 루프를 조금 더 자세히 들여다 보자. 


이 그림에서 주의 깊게 보아야 할 항목은 PostMessage는 시스템 메시지 큐에 담긴다는 것이다. 이 때문에 SendMessage와 PostMessage가 몇가지 다른 특성을 보이게 된다. 


첫번째 차이점은 PortMessage는 비 동기 이다. (메시지 큐로부터의 응답을 기다리지 않는다.) SendMessage는 동기 이다. (메시지 큐로부터의 응답을 기다린다.)


이쯤 공부 했으니.. 이제 샘플을 보자. 


위의 샘플은 WM_COPYDATA 메시지를 이용하였다 이것은 메시지 전송을 할 경우 간단한 문자 메시지를 교환 하더라도 아주 복잡한 방법이 동원 되어야 하기 때문에... (이 예제는 다음 사이트에서 볼 수 있다. http://www.pinvoke.net/default.aspx/user32/SendMessage.html ) 윈도우에 추가된 메시지 이다.


두번째 차이점이 여기에 있다. PostMessage는 시스템 메시지큐에 들어가기 때문에 이 WM_COPYDATA 메시지를 사용할 수 없다.


추가적인 사항으로 SendMessage를 이용한 Broad-Casting 이다. 말 그대로 브로드 캐스팅이면... 여러개의 프로그램에 동시에 메시지를 날릴 때 이용 하는 방법 이란 것을 알 수 있을 것이다. 



위 예제 에서 주의 깊게 보아야 할 항목은 RegisterWindowMessage 이다. MSDN - https://msdn.microsoft.com/ko-kr/library/vstudio/ms644947(v=vs.110).aspx에서 확인 할 수 있다.


참고로 약간의 설명을 하자면 다음과 같다. RegisterWindowMessage는 응용 프로그램에서 정의한 메시지를 등록하고 그 메시지가 중복되지 않도록 해서 이미 등록된 다른 메시지와 충돌이 일어나지 않도록 한다. 


두 가지 별개의 프로그램에서 메시지를 주고 받으면서 의사 교환을 할 때 일반적인 (WM_USER, WM_USER + 1 등) 메시지 번호를 사용하는 것은 안전하지 않다. 


갑자기 정리 하게된 내용이 너무 많다... 아 배고파~~~~~ 


Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,

이전 포스팅에서 하드 코딩 된 맵핑 정보에 대한 글을 쓴 적이 있습니다.


마이크로소프트도 했는데... 나라고 못 할쏘냐~~~~ 


나의 어플리케이션에서도 나만의 네임스페이스를 맵핑 하고 싶을 수 있습니다.


이때 사용 할 수 있는 방법을 정리 합니다.


프로젝트의 Properties 항목의 AssembleyInfo.cs 파일을 Open 해서...


제일 하단에 이렇게 추가 합니다.


이제 코드에서 사용 할 수 있습니다.


xmlns:mc="http://iopeni.com/compressor"


으로 말이죠...!!!!!!!!!

Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,

double 형이 가지고 있는 자리수 제한의 문제로 인하여 동일 한 수를 동일 한 수로 비교 하지 못할 경우....에 사용 가능한 짧은 코드 샘플....

Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,

PowerMockup

Favorite/Computing 2015. 3. 24. 15:24

개발자 로서... 가끔은 기획부서가 제안해야 할 화면을 먼저 제안하고 컨폼 받아야 할 경우가 있다.


상상의 나래를 펼쳐 가며 화면을 디자인 하고 컨폼을 받기 위하여, 디자인 한다는 것은 개발자로서는 정말 하기 싫은 일일 것이다. 먹고 살기 위해서 어쩔 수 없이 해야 하긴 하지만.... ^^ 뭐 아무튼 이런 잡이 생기면 보다 빨리 보다 잘 하고 싶은게 모든 사람이 가진 욕망 일지도 모르겠다.


예전 이런 디자인을 위한 보조 도구로 Balsamik Mockup이란 프로그램을 회사 기획팀에서 사용하는 것을 보고 이런 프로그램도 있구나 깜놀 했던 기억이 있다.


이후 무료로 제공되는 Mockup 프로그램이 없나 찾다가... 어느날 갑자기 PowerMockup이란 프로그램을 알게 되었다.


이건 Power Point Plug-In으로 사용할 수 있는 목업 프로그램이다. ㅎㅎ 저엉말 딱 찾던 거다...


일단... 


http://www.powermockup.com/ 


위 사이트에 접속 하여 보면.... 트라이얼을 설치 하여 볼 수 있다.







설치 정말 쉽다.


이렇게 설치 하고 보면..... 


파워포인트를 실행 하였을 때.... 다음과 같은 화면을 볼 수 있다..




이거 참 쉽다.


나는 이걸 원했다.


그런데 말이다.. 이거 유료다...저기 보면 스텐실에 회색으로 되어 있는 부분이 트라이얼이라 막혀서 그렇다.


이거 결제 해야 하나? 하고 고민할 즈음...... $59.95 라는 만만치 않은 가격... 


블로거나.... 기자 또는 비영리 단체의 회원일 경우 이 프로그램을 공짜로 제공해 주겠다는 글을 보고야 말았다.


그래서 지금 이 글을 쓰고 있다.


블로거는 이 프로그램을 홍보 하여 주면 라이센스를 제공하여 주겠다는 문구......아. 말로 설명 하느니.... 해당 사이트로 직접 직행 하여 보시길....


http://www.powermockup.com/order/free-license


나에게 피가 되고 살이 될 수 있는 화면 설계 보조 도구를 득템 했다...

Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,

어느날 아는 동생으로 부터... (나 보다 훨 스킬이 좋은 개발자다.) ODP.Net을 이용하여 Merge 쿼리를 작성 하면... 반환값이 언제나 무조건... -1이 나온다는 제보를 받았다.


물론 동생은 이걸 해결 하기 위하여 정말 열심히 검색을 했고 결국 얻어 낼 수 있는 정보는 오라클의 버그라는 것이었다.


이거 참... 나보다 훨 스킬이 좋은 동생이기에 이 이야기를 의심할 수 조차 없었다. 그 동생이 그렇다면 그런것이기에...


그럼 이걸 어떻게 해결 해야 할까? 오라클이 이런 버그를 만들어 놓고.. 패치는 하지 않은 걸까? 정말 열심히 찾았다... 그런데... 발견 할 수 있는 모든 글은.... 몇개 되지도 않았고 전부 다들 이걸 어떻게 해결 해야 할지에 대한 문의 와.. 간단하게 PL/SQL을 이용하여 sql%rowcount 를 활용하라는 내용이 전부 였다.


해결 방법 : 닭치고.... 

System.Data.OracleClient

을 쓰면 된다......


이렇게 이야기 하면... 정말 속 편하겠다... 근데 이눔의 호기심이 젠장..... 그냥 넘길 수 없었다.


일단 System.Data.OracleClient에 속한 ExecuteNonQuery 는 잘 동작하니까..... 마이크로소프트는 뭐라고 하는지 MSDN을 봤다...


https://msdn.microsoft.com/ko-kr/library/vstudio/system.data.oracleclient.oraclecommand.executenonquery(v=vs.100).aspx


물론 여기서 중요한 이야기는 


UPDATE, INSERT 및 DELETE 문의 경우, 반환 값은 해당 명령의 영향을 받는 행의 수입니다. CREATE TABLE 및 DROP TABLE 문의 경우 반환 값은 0입니다. 다른 형식의 문의 경우에는 반환 값이 -1입니다.


이 이야기가 되시겠다...


자 그렇다면.... ODP.Net은 그럼 Merge문을 다른 형식의 문장으로 보고 있다는 말인가? 이런.. 퐝당한..... 


Oracle.DataAccess.dll을 .Net Reflector로 까 봤다...


---- 중략 ----

if ((errCode != -1) && (((this.m_pOpoSqlValCtx.CommandType == 4) || 

                           (this.m_pOpoSqlValCtx.CommandType == 2)) || 

                           (this.m_pOpoSqlValCtx.CommandType == 3)))

{

    this.m_rowsAffected = this.m_pOpoSqlValCtx.RowsAffected;

    

    if ((this.m_arrayBindCount > 1) && (null != this.m_pOpoSqlValCtx.RowsAffectedForArrayBind))

    {

        this.m_rowsAffectedPerBind = new long[this.m_pOpoSqlValCtx.NoOfRowsInRowsAffectedForArrayBind];

        for (num7 = 0; num7 < this.m_pOpoSqlValCtx.NoOfRowsInRowsAffectedForArrayBind; num7++)

        {

           this.m_rowsAffectedPerBind[num7] = this.m_pOpoSqlValCtx.RowsAffectedForArrayBind[num7];

        }

     }

 }

 else

 {

     this.m_rowsAffected = -1;

 }


---- 중략 ----


보셨는가? 명령어 타입이.. 딱 3가지... 4, 2, 3이 아니면 무조건... 반환값은 -1 이다. 뭐 이쯤 되면 대충 짐작으로도 저건.. Insert, Delete, Update 쯤 될꺼란 짐작 될것이다.


아 퐝당~~~~


그럼 System.Data.OracleClient.dll 에 있는 코드는 어떻게 되지????? 그냥 넘길 수 없다. 

역시나 .Net Reflector 로 까 봤다.. 


---- 중략 ----


int num = -1;

            try

            {

                try

                {

                    ArrayList resultParameterOrdinals = new ArrayList();

                    statementHandle = this.GetStatementHandle();

                    this.Execute(statementHandle, CommandBehavior.Default, needRowid, out rowidDescriptor, out resultParameterOrdinals);

                    if (resultParameterOrdinals != null)

                    {

                        num = 0;

                        foreach (int num2 in resultParameterOrdinals)

                        {

                            OracleParameter parameter = this._parameterCollection[num2];

                            if (OracleType.Cursor != parameter.OracleType)

                            {

                                num += (int) parameter.Value;

                            }

                        }

                        return num;

                    }

                    if (OCI.STMT.OCI_STMT_SELECT != this._statementType)

                    {

                        statementHandle.GetAttribute(OCI.ATTR.OCI_ATTR_ROW_COUNT, out num, this.ErrorHandle);

                    }

                    return num;

                }

                finally

                {

                    if (statementHandle != null)

                    {

                        this.ReleaseStatementHandle(statementHandle);

                    }

                }

            }

            catch

            {

                throw;

            }

            return num;


---- 중략 ----


뭐 대충 보면... 처음에 -1로 초기화 하고 실행 후 파라미터 반복하며,,,, 조건이 맞으면 하나씩 카운트 한 다음에 반환하는 것을 볼 수 있다.. 


Oracle 프로그램 졸라 잘 만드는지 알았더니.. 이거 참... 실망이다...  이런... 멍청한 것들...


그럼 이걸 어떻게 해결 하면 되는 건지 아니 궁금하다 하면 좀 그럴것이다... 뭐 결국 업데이트 갯수가 몇개가 되었다 보다 적용 된게 있느냐 없느냐가 관건이겠으므로 나는 다음과 같은 방법을 생각 했다..



using (OracleConnection conn = new OracleConnection(oradb))

            {

                conn.Open();

                OracleCommand cmd = new OracleCommand();

                cmd.Connection = conn;

                cmd.CommandText = @"BEGIN

                                       MERGE INTO employee

                                            USING DUAL

                                               ON (companyid = 1235)

                                       WHEN MATCHED

                                       THEN

                                          UPDATE SET empname = '홍길동'

                                       WHEN NOT MATCHED

                                       THEN

                                          INSERT     (companyid, empname, phone)

                                              VALUES ('1111', 'choi', '111111');


                                       IF SQL%ROWCOUNT = 0

                                       THEN

                                          RAISE_APPLICATION_ERROR(-20801, '에러 났어요.');

                                       END IF;

                                    END;

                                    ";

                

                cmd.CommandType = CommandType.Text;


                try

                {

                    int dr = cmd.ExecuteNonQuery();

                }

                catch (OracleException ex)

                {

                    if (ex.Number == 20801)

                    {

                        System.Console.WriteLine(ex.Message);

                    }

                    else

                    {

                        throw;

                    }

                }

            }


뭐 대충 이렇게 말이다....... 만약 하나의 로우도 변경 되는 것이 없다면 오라클이 오류를 뱉을 것이다... 젠장 나 못해먹겠어... 라고 말이지......



BEGIN

   MERGE INTO employee

        USING DUAL

           ON (companyid = 1235)

   WHEN MATCHED

   THEN

      UPDATE SET empname = '홍길동'

   WHEN NOT MATCHED

   THEN

      INSERT     (companyid, empname, phone)

          VALUES ('1111', 'choi', '111111');


   IF SQL%ROWCOUNT > 0

   THEN

      RAISE_APPLICATION_ERROR(-20801, SQL%ROWCOUNT);

   END IF;

END;


이렇게 하면 뭐... 적용된 갯수를 Exeception으로 잡아 낼 수도 있겠다... 에효... ㅜ,.ㅜ;;; 이런 꼼수는 싫은 데.....


오라클은 당췌 이 버그는 언제 수정해 줄껀지..... 알고는 있으려나?????????

Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,

Visual Studio 2013 에는 우리가 알고 있는 Off-Line용 배포 프로젝트가 빠져 있습니다.

Microsoft는 Flexsa Soft와의 계약을 통하여 제한 버전인 Install Shield LE를 제공 하고 있습니다.


정품 Visual Studio를 사용하신다면 이메일을 등록 하고 다운로드 받으면 시리얼넘버가 메일로 발송 됩니다.


그런데 말이죠... Install Shield가 쉬운듯 하면서도 뭔가 좀 고급기능을 사용하고자 하면 제한 버전이기 때문에 지원이 안되는 건지... 이것 저것 좀 곤란한 상황을 많이 만나게 되기도 합니다.


예를 들자면.... 윈도우 서비스 프로그램 배포라던지... 또는 외부 확장 라이브러리의 실행 설치본 같은 것들을 추가 하고자 하면 살짝 좀 난감해 집니다.


이런 상황을 만나고 나니 Microsoft에서 Visual Studio에 기본 제공해 주던 설치 프로젝트가 그리워졌고.... Visual Studio 에서도 사용할 수 있는 방법을 찾기 시작 했습니다.


역시 Microsoft는 우리를 버리지 않았습니다.


있더군요......


https://visualstudiogallery.msdn.microsoft.com/9abe329c-9bba-44a1-be59-0fbf6151054d?SRC=Featured


이곳에 들어가셔서 다운로드 받은 후 설치를 하고 보면 


Visual Studio 2010에서 보던 바로 그 설치 프로젝트 템플릿을 볼 수 있습니다.. 아래 처럼 말이죠.





VSI_bundle.exe


위 클릭 하시면 이곳에서도 다운로드 받으실 수 있습니다. 암튼...... 그렇습니다.



Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,

아무 것도 아닌(?) 팁이라고 해야 할까요?


사실 아무 것도 아닌 아주 간단한 팁입니다. C# 1에는 아주 특수한 타입이 하나 있습니다.


System.Type 을 상속 받은 값 형식의 자료형들은 null을 대입 할 수 없습니다.


그런데 이게 좀 곤란한 상황이 발생 할 때가 있습니다. 정의 할 수 없는 상태의 값들이 바로 그렇습니다. 사람이 살아 있는데 사망일을 기록하여야 한다면 DB에는 아마도 null을 대입하게 될 것입니다.


이 null을 어플리케이션 레이어로 들고 나와서 DateTime 타입의 변수에 저장 하려고 하면 값 형식 이기 때문에 대입 할 수 없고 이 상황은 오류를 발생 시킵니다.


이 문제를 해결 하기 위하여 Nullable 이라는 아주 특수한 자료형이 있습니다. 


Nullable<DateTime> a = null; 


과 같이 DateTime 자료형에 null을 대입 할 수 있도록 하는 아주 특수한 자료형이죠!


이 자료형이 C# 2 로 오면서 좀 더 사용하기 쉬워졌습니다.


DateTime? a = null;


과 같이 말이죠 Type 에 ?을 적어 줌으로 인해서 null을 대입 할 수 있습니다.


오늘 이야기를 하려고 하는 것은 Nullable 자료형을 이야기 하려고 하는 것은 아닙니다.


널인 값을 널이 대입될 수 없는 값 형식 자료형에 대입 하려고 하면 오류가 리턴 될 것이고, 이 변수가 널인지 아닌지 우리는 if else 문 또는 bool ? true : false;의 3항 연산자를 사용하게 됩니다.


참 이런 문장을 쓸 때 마다 답답합니다. 특히나 DB에서 들고 나와야 하는 필드가 무지 많은 상황이고 쿼리에 NVL을 이용한 널 처리를 하지 않았다면 또는 DateTime에 아주 특수한 의미의 MagicValue 라고 불리우는 개발자 나름대로의 규칙 즉 지금은 사용하지 않을 어떤 값을 특수한 목적의 상태를 가리키는 값으로 활용 아직 죽지 않은 사람이라면 사망일에 1년 1월 1일 과 같은 값을 대입하고 어플리케이션에서 1년 1월 1일 인 사람은 살아 있다고 판단하게 하는 ....


뭐 이런 상황이라면 각 필드 별로 많은 조건 판단을 하게 됩니다. 


이런 상황에서 만약 널이 아니면 값을 그대로 대입하고 널이라면 다른 값을 대입하라 라는 삼항연산자의 조건을 조금 더 쉽게 정리 할 수 있는 식이 있다면 얼마나 좋을까요?


Microsoft는 기대를 저버리지 않았습니다.


바로 널 병합 연산자라고 불리우는 ?? 가 바로 그것 입니다.


아래 와 같은 문법이죠..


int? a = null;

int b = a ?? 5;


와 같이 쓰는 거죠. 이 녀석은 중첩 될 수 있습니다.


int b = null ?? null ?? null ?? ........ ?? 5;


처럼 말이죠.


이 널 병합 연산자는 저에게 매마른 대지에 단비 처럼.. 저의 복잡하고 읽기 힘든 코드를 깔끔하게 정리 하게 해준..... 정말 고마운 녀석입니다.

Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,

요즈음 들어 이 DI Pattern에 상당한 관심을 갖게 되었습니다.


이 포스트의 내용은 상당히 주관적인 내용들이기 때문에 제가 잘못 공부 하여 적는 내용들이 있을 지도 모르겠습니다.


잘못된 부분이 있다면 아낌없는 지적을..... 부탁 드립니다.


일단 Dependency Injection 이란게 무엇인지 알아야 겠죠..


전통적인(???) 이라고 해야 할지 모르겠으나, 오래된 프로그래 개발 방법들을 보면 제어를 담당하고 있는 핵심 Core는 해당 프로그램의 모든 Type을 알고 있어야 했습니다.


예를 들자면 아주 오래된 이야기 입니다만 Windows 이전이 DOS 라는 OS를 사용할 당시엔 화면에 무엇인가 출력 하기 위하여 각종 그래픽 카드들의 드라이버를 어플리케이션이 구현 하여, 프로그램에 탑재 해야 했습니다. 더불어 프린터로 출력 하기 위해서는 어플리케이션에서 각종 프린터 드라이버를 탑재 해야 했으며, 음악을 플레이 하기 위해서 각종 사운드 카드 드라이버를 프로그램에 탑재 하여야 했습니다.


그런데 Windows 라는 OS가 발표 되면서 사실 프로그램 개발자는 각종 디바이스 드라이버를 신경 쓰지 않아도 되는 시대가 도래 했죠. 


모든 디바이스 드라이버는 윈도우가 탑재 하였고 그게 어떤 디바이스 드라이버든지 응용 프로그램에서는 신경 쓰지 않고 화면에 출력 또는 프로그램에 출력, 음악 연주를 하는 세상이 Windows를 통해서 도래 하였습니다.


이런 시대가 도래 함으로서 개발자는 상당히 많은 부분의 부담을 덜어도 되었는데 하나의 출력물을 출력 하기 위하여 HP의 프린터인지 Epson의 프린터인지 LG 프린터인지 신경 쓰지 않아도 되는 정말 편리한 세상이 된 것이죠. 


이런 형태를 하나의 Dependency Injection(의존성 주입) 패턴이라고 봐도 무방 합니다.


즉 어플리케이션은 OS에 어떤 프린터가 설치 되어 있는지 몰라도 되는 상황이 된 것이죠 결국 출력 하기 위해서는 Print 라는 명령문만 날려주면 출력이 이루어지는 세상 말이죠. 


이 DI Pattern 을 구현하고 있는 프레임웍들이 상당히 많은데 사실 이런 용어를 제일 먼저 이야기 한 분은 이 시대의 개발 선구자 중 한명인 마틴 파울러 라는 분입니다.


마틴 파울러가 Inverse of Controls(IoC) 제어 역행화 방법론을 제일 먼저 소개 하였으며, 이것이 많은 개발자들에게 영감을 주어 정말 많은 프레임웍이 세상에 등장 하게 되었죠. 


많은 사람들에게 DI 와 IoC가 같은 레벨의 방법론으로 인지 되고 있으나 사실 DI에 비하여 IoC가 조금 더 추상적인 내용 입니다.


DI 패턴을 공부 하기 위하여, 우리가 제일 먼저 알고 있어야 하는 것은 OOP의 Interface 입니다.


눈치 빠른 분들께선 뭐 당연한 이야기네 라고 말씀 하시겠지만. Interface라는 것이 어떤 타입이든 관계 없이 해당 타입의 메소드 만 존재 한다면 타입 캐스팅이 필요 없이 사용할 수 있는 메커니즘 때문 이라는건 누구나 주지 하고 있는 사실 일 것입니다.


뭐 이쯤해서 서론은 마치기로 하고 몇가지 패턴들에 대해서 이야기 해 보도록 하겠습니다.


GoF의 디자인 패턴 중 Factory 패턴을 보게 되면 이 패턴이 이야기 하고 있는 것은 여러개의 동일한 행위를 하는 여러 타입의 객체를 중앙 집중화 할 수 있는 방법입니다.


회사의 규모에 따라 하나의 회사에는 여러명의 사원이 존재 합니다.


여러명의 사원은 각기 다른 업무를 수행하게 되겠죠.. 영업, 개발, 연구, 경원지원 등등등..... 과 같이 말이죠.


만약 한명의 개인의 급여를 계산 하는 프로그램이 있다고 가정 한다면 급여를 계산 하는 메인 프로세서는 이 사람의 부서, 직책, 직위, 시간외 업무 수당, 실적, 보너스 .. 등등 많은 것들을 고려 하고 있어야 할 것입니다. 


급여 계산을 수행 하고자 하는 메인 프로그램이 모든 사원에게 기본급을 계산 하는 룰은 같다고 가정 하고 각각의 부서별로 다른 급여 계산을 하는 비지니스 모델이라고 한다면, Employee 라는 추상 클래스로서 기본급을 계산 하는 Type을 생성 하고 이 Employee 라는 클래스를 상속 받는 부서별 Class를 작성하여 비지니스 로직을 분리 하고 이 객체를 좀 더 쉽게 생성 중앙 집중화 할 수 있도록 하는 Factory Pattern 을 사용하게 될 것입니다.


컨트롤러의 입장에서 바라 본다면 이 패턴의 문제점은 다음과 같을 것입니다.

어찌 되었든 컨트롤러 입장에서 바라 볼 때, 컨트롤러는 급여 계산을 하기 위하여 어떤 부서가 있는지 명시적 타입을 알고 있어야 할 것입니다.


이것은 DI 패턴이라고 할 수 없죠. 그래서 제시된 방법론이 ServiceLocator 라는 패턴 입니다.


이 ServiceLocator의 핵심은 다음과 같은 코드 입니다.


public class ServiceLocator
{

    private IDictionary<string, object> registeredTypes = new Dictionary<string, object>();

    public void Register<T>(string ServiceName, T obj)
    { 
        registeredTypes.Add(ServiceName, obj);
    }

    public T Locate<T>(string ServiceName)
    {
        return (T)registeredTypes[ServiceName];
    }
}

이 코드를 보면 Dictionary의 Key를 string 타입으로 지정 한 것을 볼 수 있습니다. 즉 각각의 부서명을 이용하여 실제 Object를 취득 하게 됨을 알 수 있습니다.


결국 메인 컨트롤러는 명시적 타입을 알지 못해도 되는 효과를 얻기는 했으나.....


두번째 정말 커다란 문제가 있죠.


만약 회사가 발전하게 되고 회사의 부서가 점점 늘어 나게 될 경우 메인 컨트롤러는 각각의 부서를 Register 하기 위한 코드가 부담 스러워 지게 될 것입니다.


역시 이 또한 Factory 패턴과 별반 다를게 없어 보입니다.


이것을 개선 하기 위하여 작성된 IoC 컨테이너의 핵심 코드를 보도록 하겠습니다.



public class IoCContainer
{

    private IDictionary<Type, object> registeredTypes = new Dictionary<Type, object>();

    public void ADD<T>(T instance)
    { 
        registeredTypes.Add(typeof(T), instance);
    }

    public T GetInstance<T>()
    {
        return (T)registeredTypes[typeof(T)];
    }
}

ServiceLocator 패턴의 핵심 코드와 별반 다르지 않습니다.


그런데 말이죠... 이 코드의 유용함이란.. 정말 이루 말 할 수 없겠네요.  이것으로 DI Container 의 기초 정리는 마무리....... 합니다.

Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,

객체 소거에 대한 단상....


얼마 전 아는 분이 몇가지 기술 적 자문을 구해 오셔서 그분 회사의 코드를 보던 중 질문 하신 범위 밖의 코드가 아주 심각한 문제가 있음을 발견 했습니다. 보고도 이야기 하지 않았습니다만..... 이야기 한다고 해서 그분이 이해 하시기엔 상당한 에로 사항이 있을꺼라는 ..... 제가 나쁜 놈이겠죠?


엔더스 헤일스버그라는 이 시대의 용자 께서는 .Net 을 설계 하시며, 절대 어플리케이션이 죽어서는 안된다 라는 명제를 가지고 정말 많은 고민을 하시었고(정말 고민을 했는지는 저도 모릅니다. 그냥 추측일 뿐입니다.)  생성된 객체가 소거 되었을 때, 누군가 소거된 객체를 호출 할 경우 어플리케이션의 종료를 막을 수 없고, 이 문제를 해결 하기 위하여, 가베지 컬렉터를 사용하여, 1세대, 2세대등...  사용자가 소거를 하여도 즉시 소거 되지 않고 가베지 컬렉터가 정말 사용하지 않는 객체를 소거 할 수 있도록 하는 시스템을 설계 하셨습니다.


아시는 분의 회사 코드를 보면 다음과 같은 황당한 코드가 담겨 있습니다.


private LIst<object> = new List<Object>();


public void funcion()

{

list.Clear();

list.ADD(new userControl);


//Todo....

}


그리고 화면에서 function을 호출 합니다.


결국 메소드가 한번 호출 될 때 마다 List를 비우고 다시 객체를 생성하여, 리스트에 담아 두게 되는데, 담겨 있는 객체를 소거 하는 문장은 어디에서도 찾아 볼 수 없습니다.결국 만든 유저 컨트롤 또 만들고, 또 만들고, 또 만들고........


엔더스 헤일스버그라는 이 시대의 용자가 설계한 마인드로 볼 때 결국 위와 같은 코드로 생성된  UserControl은 프로그램이 종료 될 때 까지 절대 소거 되지 않습니다. 


즉 Memory Leak 이 발생 한 거죠. 


만약 이 유저 컨트롤이 정말 큰 데이타를 취급 하게 된다면.... 이 프로그램이 메모리 부족 오류를 던지며, "나 일 못하겠다... 써글..." 이라고 이야기 하는 것은 시간 문제 입니다.


하나의 프로세스당 제한된 스레드 갯수... 하나의 스레드당 제한된 메모리 량을 생각 해 보면..... 사용 컴퓨터의 용량에 따라 어느 정도 가변적 이겠으나, 아주 위험한 코드죠 위와 같은 코드는.....


그래서 컬렉션에 있는 객체를 어떻게 소거 하여야 할까? 라는 문제를 생각해 보면 상당히 많은 방법들이 존재 합니다.


아주 무식한 방법으로 컬렉션에 담겨 있는 Controls를 Foreach로 순환 하며, 명시적 타입 캐스팅을 통한 타입 캐스팅 이후 각 타입이 가지고 있는 Dispose()를 호출 할 수도 있으나, 만약 저 List에 담기는 타입이 늘어 나면 늘어 날 수록 이 방법은 절대적 부담으로 작용 합니다. 


리스트에 담겨 있는 타입을 타입 캐스팅 해서 그 타입이 소거 대상 타입이라면 소거 하라는 메소드로 이관 하고 그 이관된 각 객체가 정말 이 시점에서 소거 하여야 하는 가를 검증 하는 잡이 발생 하게 되면... 이건 정말... 가관 입니다.


욕 나오는 코드를 작성 하게 되죠... 아마도 많은 초급 개발자 분들 께서는 이렇게 난해한 코드를 작성 하리라 생각 합니다.


그래서 살짝 힌트 아닌 힌트를 정리 합니다.


컬렉션에는 아주 유용한 메소드가 하나 있습니다.


ofType 메소드가 바로 그것이죠... 


이거에 확장 메소르를 하나 연결 하면 아주 쉬운 정리가 이루어 집니다. 다음 처럼 말이죠


//확장 메소드.

public static void ForEach<TItem>(this IEnumerable<TItem> sequence, Action<TItem> action)

        {

            // ToDo... 유효한 제거 인지에 해당하는 검증 작업 수행

                  검증하자....

// ---------------------------------------


            foreach(var item in sequence)

            {

                action(item);

            }

        }


// 컨트롤 클래스에서의 사용 이후 리스트 삭제 시점..

collection.OfType<IDisposable>().ForEach(ex => ex.Dispose());

collection.Clear();



짧은 코드로 원하는 잡을 수행 할 수 있다는 ..... ^^ 유용한 코드 입니다.





'Functional World > C#' 카테고리의 다른 글

라운드 트립 서식...  (0) 2015.05.06
널 병합 연산자.....  (0) 2014.12.15
윈도우 8.1 에서의 장치 독립적 코드 작성을 위한 DPI 설정 방법  (0) 2014.06.22
Form.ActiveForm  (0) 2013.11.07
바이트 배열 취득  (0) 2013.10.01
Posted by 프로그래머란 카페인을 코드로 변환하는 기계다
,