티스토리 툴바


개발을 하다보면 엑셀시트 달랑 던져주고 데이타를 업로드 해달라는 요구를 많이 받게된다.
엑셀을 읽어 원하는 데이타를 가져오는것은 그렇게 어렵지 않지만
제일 중요한건 업로드를 하는데 걸리는 시간을 줄이는 것이다.
업로드 시간이 몇 분이상 넘어가게 된다면 사용자는 슬슬 짜증을 내기 시작한다.
만약 Sql DB서버를 사용하고 있다면 SqlBulkCopy를 추천한다.
20분 이상 걸리던 시간을 1분대로 줄여서 매우 만족스럽게 처리했던 경험이 있다.
예제와 함께 세번의 포스팅으로 나눠서 알아보고자 한다.
참고로 .NET Framework 버전 2.0이상에서만 사용가능합니다.
네임스페이스는using System.Data.SqlClient;

※ 예제는 엑셀파일을 읽어서 가져온 데이타를 SqlDB의 특정 테이블에 SqlBulkCopy를 하는 내용입니다.

샘플파일을 다운 받아 소스를 봐주시길 권장합니다. ^^


첫번째 시간으로
1. 클래스 정의 및 멤버
    1) 코드 패턴소개
        - 소스 데이타의 오버로드 소개
    2) 간단한 예제
를 알아보자!!!!

1.클래스 정의 및 멤버
■ 클래스 정의
SqlBulkCopy??
조금은 생소할 수 도있겠지만 MSDN을 찾아보면~~

Microsoft SQL Server에는 단일 서버에서 또는 여러 서버 간에 테이블의 데이터를 다른 테이블로 이동할 때 많이 사용하는 bcp라는 명령 프롬프트 유틸리티가 포함되어 있습니다. SqlBulkCopy 클래스를 사용하면 비슷한 기능을 제공하는 관리되는 코드 솔루션을 작성할 수 있습니다. 또한 INSERT 문 등을 사용하여 SQL Server 테이블로 데이터를 로드하는 방법도 있지만 SqlBulkCopy를 사용하면 다른 방법에 비해 성능이 향상됩니다.
SqlBulkCopy 클래스는 SQL Server 테이블에 데이터를 쓸 때에만 사용됩니다. 그러나 데이터를 DataTable 인스턴스에 로드하거나 IDataReader 인스턴스로 데이터를 읽을 수 있는 경우 SQL Server를 포함한 모든 데이터 소스를 사용할 수 있습니다.
라고 정의 되어있죠..

말그대로 Sql DB에서 Bulk로 데이타를 Copy를 하는 클래스입니다.
('.' ;) 좀 직관적인 해석이였습니다. ㅎㅎ

//DB의 특정테이블에 Datable를 데이타를 BulkCopy하는 예제

            //1.BulkCopy할 소스 데이타를 가져온다.
            DataTable dtBulk = (DataTable)this.dgvSource.DataSource;

            this.prgCount.Value = 0;
            this.prgCount.Maximum = dtBulk.Rows.Count;

            using (SqlBulkCopy oBulk = new SqlBulkCopy(this.GetConnectionString()))
            {
                //2.대상 테이블
                oBulk.DestinationTableName = "dbo.CustomerTest";

                //이벤트를 처리할 행의 수
                oBulk.NotifyAfter = 1;
                //데이타가 Copy될때 이벤트 발생
                oBulk.SqlRowsCopied += new SqlRowsCopiedEventHandler(oBulk_SqlRowsCopied);

                //3.지정된 데이타를 DestinationTableName에 카피한다.
                oBulk.WriteToServer(dtBulk);
               
                oBulk.Close();
            }   


1. 카피할 데이타를 가지고 옵니다. ( Datarow[], DataTable, IDataReader )
2. 데이타를 부어줄 테이블명(DestinationTableName)을 설정합니다.
3. WriteToServer() 메소드로 데이타를 DestinationTableName에 부어줍니다.
4. 끝

위와 같은 순서로 몇줄 안되는 코드라인으로 데이타를 카피할수 있습니다.

■ 멤버
BatchSize 
벌크 카피를 할때 한번에 전송할 로우의 사이즈를 설정할 수 있습니다.
(Ex: 10 한번에 10개의 행을 모아서 한번에 전송

BulkCopyTimeout
타임아웃을 설정하여 일정시간안에 끝나지 않을 경우 에러를 발생할 수 있다.

ColumnMappings
SqlBulkCopyColumnMapping 항목의 콜렉션을 사용하여
DestinationTableName의 테이블 컬럼명과 소스 데이타의 컬럼명을 맵핑시켜서 데이타를
넣을 수 있다.

DestinationTableName
데이타를 부을 실제 DB의 테이블명

NotifyAfter
SqlRowsCopied 이벤트를 발생시킬 행의 수
(Ex: NotifyAfter = 3; 으로 설정할 경우 3건의 데이타가 대상 테이블로 카피된후 알림 이벤트가
발생되게 된다.)

Close
SqlBulkCopy 인스턴스를 닫는다.

WriteToServer
DestinationTableName 테이블에 데이타를 복사합니다.

첫번째 시간으로 간략한 클래스 정의와 멤버를 알아봤는데요.
다음 포스팅에서는 SqlBulkCopy가 지원하는 트랜잭션의 종류와 SqlBulkCopyOptions에 대해서
알아보도록 하겠습니다.

Posted by MotionCoder

spring 자바버전 Reference Documentation http://openframework.or.kr/framework_reference/spring/ver2.x/html/

spring 닷넷버전  Reference Documentation
http://www.springframework.net/doc-latest/reference/html/

저작자 표시
Posted by pinkicon
2009/01/16 10:47


Spring은 이미 오래전부터 자바진영에서 각광받고 있는 프레임웤으로서, 닷넷버전으로도 공개가 되었습니다.
그것이 바로, Spring.NET인데요,  
Spring.NET은 닷넷기반의 엔터프라이즈 어플리케이션을 구축하기 위한 어플리케이션 프레임웤  이라고 정의할 수 있습니다. 



Spring.NET의 각 모듈의 구성을 보시면, 하단 우측의 AOP 기반으로, ASP.NET,  Web Serivce, ORMapping, DataAccess, NUnit, Remoting, COM+ 등의 다양한 코어 모듈 기반의 프레임웤입니다.

AOP(Aspected Oriented Programming) 란  관점 지향 프로그래밍이라고 해석되는데요,
이는, 프로젝트를 진행할때,  개발자가 고민해야하는 비지니스 로직을 핵심관심사라 보고, 그외의 인증/권한/예외처리/로깅/트랜젝션관리 등에 대한 것을 횡단관심사로 보아, 관심의 분리를 일컷습니다.
이는, 개발자는 핵심관심사에 치중하며, 횡단관심사는 프레임웤의 각 컨테이너가 제공을 해주게 됩니다. 
그리고 각 컨테이너는 IOC(Inversion Of Control) 형태로 구성되어있는데요, 
기존에 개발방법론에서는 작성되어있는 프레임웤은 Library로서의 기능이 강했습니다.
개발자는  비지니스 로직에 집중하면서,  횡단 관심사에 대한 호출이 필요한 경우에는 작성되어있는 프레잌웤 라이브러리를 호출해서 사용하였었지요.
그러나, IOC 형태로 구성된 컨테이너 들은  프레잌웤 코드가 전체 어플리케이션의 처리흐름을 제어하며, 특정한 이벤트가 발생할 시마다 다형성을 통해, 어플리케이션이 확장한 메소드를 호출함으로서, 결과적으로는 제어가 프레임웤에서 어플리케이션으로 거꾸로 흐르게 되는 방법을 말합니다.
그리고 객체가 참조하고 있는 다른 객체를 스스로가 new로 create하지 않고, 인터페이스를 통해서 Assing받아 사용하는 방식을 따르고 있습니다.

[예제1] 
App.config
    <objects xmlns="http://www.springframework.net" >
       <object id="MyHello" type="DI.Hello, DI"></object>
    </objects>

Interface
    interface IHello
    {
        string sayHello(string str);
    }

Class
    class Hello : IHello
    {
        public string sayHello(string str)
        {
            return str;
        }
    }

MainApp
        static void Main(string[] args)
        {
            IApplicationContext ctx = ContextRegistry.GetContext();  //app.config에 설정된 object를 가져옵니다.
            IHello hello = (IHello) ctx.GetObject("MyHello"); //  MyHello object를 노출된 interface 를 통한 Assign
            string str = "안녕";
            string result = hello.sayHello(str);  
            Console.WriteLine(result);
        }

MainApp에서 Hello 클래스의 객체를 직접 create하지 않고, IHello 인터페이스를 도입함으로서, MainApp와 Hello와의 종속성이 제거되었습니다.  여기서 Hello가 컴퍼넌트 역할을 하게됩니다.
configuration에 사용하고자 하는 컴퍼넌트를 명시하고,  MainApp에서는  해당 컴퍼넌트를 사용할수 있도록 interface를  제공한 것입니다. 
위와 같은 프로그래밍 모델을  IOC, DI 라고 부릅니다.


Spring.NET에서는 AutoProxy기능이 지원되는데요,  이는 특정 메소드가 호출될때, configuration에 define된 룰에 따라, 메소드호출을 intercept하여, 흐름의 제어를 할수 있게 되는것입니다.

[예제2]
 

Configuration
<!--AutoProxy 설정-->
      <object id="MyServiceCommand" type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop">
        <property name="ObjectNames">
          <list>
            <value>K*</value>
          </list>
        </property>
        <property name="InterceptorNames">
          <list>
            <value>Interceptor</value>
          </list>
        </property>
      </object>
  <!-- Intercept시 호출된 클래스정보-->
  <object id="Interceptor" type="ConsoleApplication2.Interceptor, ConsoleApplication2"/>
  
 <!--Object-->
      <object id="English" type="ConsoleApplication2.HelloWorldSpeaker, ConsoleApplication2">
        <property name="Language" value="English"/>
      </object>
     <object id="Korean" type="ConsoleApplication2.HelloWorldSpeaker, ConsoleApplication2">
        <property name="Language" value="Korean"/>
      </object>
  
interface
    public interface IHelloWorldSpeaker
    {
        void SayHello();
    }

Class 
   public class HelloWorldSpeaker : IHelloWorldSpeaker
    {
       public void SayHello()
        {
            switch (language)
            {
                case Language.English:
                    Console.WriteLine("영어");
                    break;
                case Language.Korean:
                    Console.WriteLine("한국어");
                    break;
            }
        }
    }

Interceptor 
 public class Interceptor : IMethodInterceptor
    {
        public object Invoke(IMethodInvocation invocation)
        {
            Console.WriteLine("실행전");
            object rval = invocation.Proceed();
            Console.WriteLine("실행후");
            return rval;
        }
    }

또한 before/After/Around 등의 Advice를  configuration에 세팅하게되면, 메소드의 실행전후에 특정 흐름을 끼워넣을수도 있게 됩니다.

Spring.NET에서는 Quart.NET을 이용한  스케줄러 구현을 지원합니다.일반적으로, 기존에 우리 스케줄러를 작성하기 위해서는 쓰레드개념을 생각 안할 수가 없는데요,
이때, Threadpool을 관리함에 있어서 여러가지 고민해야될것들이 많았습니다
Pool의 count , 객체 소멸시점, 특정 시간에만 동작하기 위한 Timer .. 외에도 여러가지 존재할것 같습니다.
그렇다면, Spring.NET에서는 이 모든것들에 대해서 어떻게 구현하게 될까요..
이제부터 직접 스케줄러를 구현해 보도록 하겠습니다.

1. configuration  
2. scheduler
3. trigger
4. jobDetail

configration을 작성해야 하는데요, 이는 app.config에 작성할 수도 있고, 따로 xml파일로 뺼수도 있습니다.
configuration에는 스케줄에 해당하는 object와, 스케줄에 대한 반복적인 주기를 뜻하는 트리거, 그리고 마지막으로 각 트리거가 수행하는 Job에 대해서 명시해야합니다. 

[예제3]
 
Scheduler
<object id="quartzSchedulerFactory"  type="Spring.Scheduling.Quartz.SchedulerFactoryObject, Spring.Scheduling.Quartz">
    <property name="SchedulerName" value="quartz1"/>
    <property name="triggers">
      <list>
        <ref object="CronTrigger" />
        <ref object="SimpleTrigger" />
       </list>
    </property>
  </object>

Trigger  
<object id="SimpleTrigger"  type="Spring.Scheduling.Quartz.SimpleTriggerObject, Spring.Scheduling.Quartz">
    <!-- see the example of method invoking job above -->
    <property name="JobDetail" ref="SchedulerJob" />
    <!-- 10 seconds -->
    <property name="StartDelay" value="10s" />
    <!-- repeat every 10 seconds -->
    <property name="RepeatInterval" value="10s" />
  </object>
  <object id="CronTrigger" type="Spring.Scheduling.Quartz.CronTriggerObject, Spring.Scheduling.Quartz">
    <property name="JobDetail" ref="SchedulerJob" />
    <!-- 5초마다의 interval Fire -->
    <property name="CronExpressionString" value="0/5 * * * * ?" />
  </object>

Job
<object name="SchedulerJob" type="Spring.Scheduling.Quartz.JobDetailObject, Spring.Scheduling.Quartz">
    <property name="JobType" value="Publisher.SchedulerJob, Publisher" />
    <property name="JobDataAsMap">
      <dictionary>
        <entry key="Timeout" value="5" />
       </dictionary>
    </property>
  </object>

모든 object는 Id(Name)와 Type을 지정해주어야합니다.
그리고, 각 오브젝트마다 가지는 프로퍼티가 조금씩 다른데요,
스케줄에서는 해당 스케줄에 대한 반복적인 주기를 뜻하는 트리거를 지정하게됩니다.
저는 simpleTrigger와 cronTrigger라는 두개의 트리거를 지정했는데요,  몇분에 한번씩. 처럼 간단한 작업주기인경우에는 simpleTrigger를, 일중일 중에서 토/일을 제외한 매일 아침 10시부터 12시까지 20분간격으로. 라는 조금은 복잡해보이는 반복주기인경우에는 crontrigger를 사용하면 됩니다.
그리고 각 트리거에서는 수행할  Job에 대해서 명시를 해야하는데요,
JobType이라는 프로퍼티를 통해, 수행할 Task의 Type을 지정하게됩니다 Type은 어셈블리명, 네이스페이스명으로 지정하시면 됩니다.
위의 configration는 Publisher.SchedulerJob을 하나는 10초마다, 하나는 5초마다 반복 수행하는 스케줄러임을 알수 있게됩니다.
config세팅이 끝나면, 이제 Publisher.SchedulerJob을 작성해 보겠습니다.

[예제4]
 class SchedulerJob : QuartzJobObject
    {
        protected override void ExecuteInternal(JobExecutionContext context)
        {
                 Console.WriteLine("Excute...");
         }
    }


SchedulerJob은 QuartzJobObject를  상속받아, 예약되어있는 ExecuteInternal메소드를 오버라이딩 하면 됩니다
파라메터인 JobExecutionContext타입의 context에는 configration에서 작성한 해당 Job의 정보를 모두 알 수 있습니다.
위와 같이 QuartzJobObject를 상속하여, Job을 수행하는 방식 외에, 직접 TargetMethod를 지정할 수도 있습니다.

스케줄 트리Listener를 등록해 놓으면, 트리거가 Fire될때, 혹은 misFire되거나 실행이 완료되었을때의 이벤트를 Listen할 수가 있습니다.

[예제5]
   
public class TriggerListener : ITriggerListener
    {
       
public void TriggerFired(Trigger trigger, JobExecutionContext context)
        {
            Console.WriteLine("TriggerFired");
        }

         public void TriggerMisfired(Trigger trigger)
        {
            Console.WriteLine("TriggerMisfired");
        }

        public void TriggerComplete(Trigger trigger, JobExecutionContext context,
                                    SchedulerInstruction triggerInstructionCode)
        {
            Console.WriteLine("TriggerComplete");
        }
    }

작성된 스케줄러를 실행시켜보겠습니다.
[실행결과]

5초마다 Crontrigger가, 10초마다 simpleTrigger가 수행되고 있으며, 각 이벤트가 Fire될때마다 Trigger를 Listen하고 있습니다.

지금까지 간략하게나마, Spring.NET을 이용하여 스케줄러를 만들어 보았는데요,,
잘 동작하네요 :)  

읽어주셔서 감사합니다.
Posted by pinkicon
이전버튼 1 2 3 4 5 ... 17 이전버튼