728x90
얼마전에 올렸던 포스팅에서 OpenXML을 이용하여 대용량 XML 형태 데이터를 MS-SQL 데이터베이스에 효과적으로 넣는 방법에 대해서 공유를 했었다. 하지만 OpenXML 을 이용하는 방법은 조금 까다로운 감이 없지 않고 구문이 복잡해져서 복잡한 XML 데이터를 핸들링 할때는 불편한게 사실이다.

오늘 소개해 드리는 방법은 이보다 더 간단한 방식으로 SQL Server 2005, 2008 등에서 XML 에 대한 자체적인 지원을 시작하면서 사용 가능해진 방법이다. OpenXML 을 이용하는 방법보다 단순하게 쿼리를 만들 수 있는 장점이 있는 반면 OpenXML 이 상당히 구체적으로 노드와 어트리뷰트, 값을 지정해서 뽑아내는 것보다 명확하지 않아 보일 수 있는 단점도 있다.
DECLARE @x XML

SET @x = N'
  
    1
    2
    3
  
'

SELECT 
	x.y.value('@name[1]', 'VARCHAR(20)'),
	x.y.value('.', 'INT')
FROM @x.nodes('my_data/item_list/item') as x(y)
변수를 XML 형태로 선언을 하고 파싱을 하고자 하는 XML 데이터를 이 변수에 넣어주는 것에서부터 시작해 보자. 데이터는 객체의 nodes 라는 속성을 통해서 읽어내고자 하는 XML 의 노드를 지정하여 데이터 소스로 사용하게 된다. Select 문에서 읽어들인 값에 대해 .value 속성을 통해 이름과 자료형을 기술해주면 데이터가 파싱되어 결과로 출력된다.


- NoPD -
728x90
728x90
WCF 를 이용한 통신채널을 구성할때, 일반적인 방법으로 인터페이스를 선언하고 웹 참조 혹은 DLL 참조, Svcutil 로 생성된 레퍼런스 정보를 사용할 때는 별 문제가 없다. 하지만 WCF 3.5 부터 제공되기 시작한 REST 형태의 호출 지원을 사용하는 경우에는 파라메터의 데이터 형태에 따라 BodyStyle 속성을 지정해야 하는 경우가 빈번하다.

예> IService.cs

[ServiceContract]

public interface IService

{

    [OperationContract]

    [WebInvoke(UriTemplate = "Counter", Method = "POST", BodyStyle = WebMessageBodyStyle.Wrapped)]

    int Counter(CounterList counterValues, int tryCount); 

 
그런데 문제는 서비스의 인터페이스 선언에 이렇게 WebInvoke Attirbute 와 BodyStyle 을 지정했음에도 클라이언트에서 서비스를 호출할 때 BodyStyle 이 Wrapped 로 지정되지 않았다는 에러가 발생할 때가 간혹 있다는 점이다. 특히 svcutil 을 이용해서 매뉴얼하게 레퍼런스 클래스를 만드는 경우에 이런 일이 많이 발생한다. (웹참조로 추가하는 경우에도 발생한다는 보고가 있다)

예> 클라이어트에서 서비스 호출시 에러 메세지

'IService' 계약의 'Counter' 작업에서 래퍼 요소 없이 직렬화할 여러 개의 요청 본문 매개 변수를 지정합니다. 최대 하나의 본문 매개 변수가 래퍼 요소 없이 직렬화될 수 있습니다. 추가 본문 매개 변수를 제거하거나 WebGetAttribute/WebInvokeAttribute의 BodyStyle 속성을 Wrapped로 설정하십시오.


이런 경우 서비스쪽을 자꾸 살피게 되는데 원인은 서비스가 아니라 레퍼런스 클래스의 생성에 있기 때문에 트러블슈팅이 쉽지 않다. svcutil 명령을 이용해서 만든 레퍼런스 클래스를 확인해 보면 WebInvoke 로 지정한 내용이 전혀 들어가있지 않은 걸 쉽게 발견할 수 있다. 해당 부분에 동일한 선언을 추가해주면 에러를 가볍게 제거할 수 있다.

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]

[System.ServiceModel.ServiceContractAttribute(ConfigurationName="IService")]

public interface IService

{

[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IService/Counter", ReplyAction="http://tempuri.org/IService/CounterResponse")]

    [WebInvoke(UriTemplate = "Counter", Method = "POST", BodyStyle = WebMessageBodyStyle.Wrapped)]

    int Counter(Counters counterValues, int tryCount);


비동기 형태로 호출하기 위해 만든 레퍼런스 클래스의 경우에도 Beginxxx 와 같은 비동기 메소드를 선언하는 부분에는 지정해줄 필요가 없다. 원래 함수의 속성으로만 지정하면 문제는 해결된다. 혹시 비슷한 어려움을 겪는 사람들을 위해서 공유해둔다.

- NoPD -



 

 
728x90
728x90
데이터베이스 서버나 개발시 로컬 인스턴스 환경에서 유용하게 사용될 수 있는 것이 벌크 인서트(Bulk Insert) 기능이다. 샘플 데이터 라던가 코드성 데이터들을 한번에 쉽게 테이블에 넣을 수 있는 방법으로 많이 애용되고 있다. 그런데 벌크 인서트는 로컬의 파일 시스템에 저장된 텍스트 파일을 이용하는 방식이기 때문에 리모트에서 동작하는 ASP.NET 어플리케이션과 같은 클라이언트는 사용할 수 없는 기술이다.

그렇다면 대용량의 데이터를 한번에 넣을 수 있는 좋은 방법은 없을까? 수백건의 데이터를 한번에 테이블에 넣는 것과 같은 작업을 insert 문을 이용해 행의 갯수만큼 돌린다면 트랜잭션의 관리를 포함하여 도저히 감당하기 힘든 느린 속도를 경험하게 될 것이다. 이럴 때 유용하게 사용할 수 있는 것이 바로 OpenXML 을 이용한 대용량 데이터의 삽입이 아닐까 싶다.

원리는 간단하다. 한번에 넣어야 하는 데이터를 XML 형태로 구성해서 데이터베이스 서버에 전달하고 이를 서버측 스토어드 프로시저가 OpenXML 을 이용하여 DTD 에 맞추어 데이터를 추출하고 테이블에 넣는 방법이다. 서버측 자원을 사용하게 되는 단점이 있지만 insert 문을 행의 갯수만큼 반복하는 것보다는 훨씬 효과적인 방법이라고 생각된다.
functio foo()
{

}


SET TEXTSIZE 100000


DECLARE @docHandle int

DECLARE @data varchar(max)


SET @data = '

<Logs Hostname="MyServer">

   <Log C="/Central Process Unit/Process Time Limit is never comes along with me yeah1" I="/Item Name Can Be many Things1" V="0.306316345601545" D="2011-05-04T01:01:01" />

</Logs>

'


EXEC sp_xml_preparedocument @docHandle OUTPUT, @data


SELECT *

  FROM OPENXML(@docHandle, '/Logs/Log', 2)   -- 0,1 : Attibute-Centric , 2 : Element-Centric

  WITH (Hostname varchar(50) '../@Hostname',

        CounterCategory varchar(100) '@C',

        CounterItem varchar(100) '@I',

        VounterValue decimal(13,1) '@V',

        CheckDate datetime '@D')


대용량 데이터의 저장을 위해서 XML 을 구성하면 필연적으로 그 용량이 커지게 된다. MS-SQL 의 varchar 타입은 최대 8000 바이트까지 밖에 허용하지 않고 DECLARE 로 선언 가능한 내부 변수에 text 타입은 빠져있다. 따라서 SET TEXTSIZE 구문을 통해서 max 값을 충분히 늘려준뒤 varchar(max)를 사용하면 해결할 수 있다.

XML 로 구성하는 데이터의 형식은 자유이다. DTD 를 별도로 만들지 않아도 되고 형식만 잘 맞추어주면 된다. OpenXML 은 노드를 구조를 지정하고 애트리뷰트 단위(Attribute) 혹은 엘레멘트 단위(Element)로 지정할 수 있다. 디렉토리 이동시와 마찬가지로 ../ 와 같은 구문을 이용하면 XML 의 모든 요소에 엑세스가 가능하다.



- NoPD - 
728x90
728x90
프로젝트를 진행하다가 급하게 SMTP 서버를 구축하는 경우가 많습니다. 하지만 SMTP 서버로 사용할 자원이 없거나 다양한 이슈로 운영이 힘든 경우에는 외부의 메일서버를 사용하는 경우가 많습니다. 가장 대표적인 것이 구글에서 제공하는 SMTP 서버입니다. Gmail 계정만 있으면 사용할 수 있고 SSL 통신등도 지원하기 때문에 무척 사랑받고 있습니다.

그런데 Gmail 계정을 이용해서 smtp.gmail.com 을 사용하는 경우 문제가 하나 있습니다. 메일 헤더에 발신자(From)를 아무리 설정해도 인증을 받는 구글 계정으로 발신자가 표시되는 문제점이 바로 그것입니다. 한참동안 구글링을 하면서 해결 방법을 찾아보다가 너무 가까운 곳에서 해답을 찾았습니다. 같은 고민 하시는 개발자 분들을 위해 정리해 봤습니다.


Gmail 에 로그인 하신 다음 설정으로 먼저 들어갑니다. 설정의 여러가지 탭들 중 Accounts and Import 탭으로 이동해서 Send mail as 항목을 수정하는 것이 바로 해결책입니다. 기본적으로 gmail 계정이 등록되어 있는데 하단의 Send mail from another address 를 눌러 사용하는 이메일 계정을 추가해 주면 됩니다.

이메일을 추가한 다음 실제로 살아있는 정상 계정인지 확인을 위해 Verification 메일로 확인 코드를 전달해 줍니다. 해당 코드를 입력함으로써 이메일 추가가 완료됩니다. 이후 smtp.gmail.com 서버를 통해 메일 발송시에 새로 설정한 메일이 발신자로 기록되게 됩니다. 우측에 make default 버튼을 눌러서 꼭 default 로 설정하는 것 잊지 마시구요.

- NoPD -
 
728x90

+ Recent posts