죄다 깨지냐 ㅡㅡ; .NET Enterprise Services 성능Richard Turner, 프로그램 관리자, XML Enterprise Services Larry Buerk, 프로그램 관리자, XML Enterprise Services Dave Driver, 소프트웨어 디자인 엔지니어, XML Enterprise Services Microsoft Corporation 2004년 3월 적용 대상: COM+ 구성 요소 Microsoft .NET Enterprise Services 요약: 다른 활성화 및 호출 패턴에 적용될 때의 원시 COM+ 및 .NET Enterprise Services 구성 요소 성능을 확인합니다. .NET Enterprise Services 구성 요소를 C++의 COM+ 구성 요소처럼 빨리 실행하기 위한 지침과 함께 고성능 .NET Enterprise Service 구성 요소를 만드는 데 도움이 되는 주요 권장 사항을 살펴봅니다(45페이지/인쇄 페이지 기준). 관련 EnterpriseServicesPerf.exe 코드 샘플을 다운로드하십시오. 목차소개 관리되는 코드로 마이그레이션해야 하는 이유 코드 변경 정도 지정 .NET Enterprise Services에 COM+ 연결 .NET Enterprise Services와 COM+ 성능 비교 테스트 결과 및 분석 결론 부록 1: 성능 권장 사항 부록 2: "Indigo" 및 .NET의 동향 부록 3: 분산 트랜잭션이 성능에 미치는 영향 부록 4: 참고 자료 부록 5: 성능 테스트 소스 코드 부록 6: 테스트 결과 소개COM+ 코드를 "원시" Visual C++ 또는 Visual Basic 6에서 관리되는 .NET Enterprise Services 구성 요소로 이동할 것을 고려 중인 개발자는 다음과 같은 고민 사항이 있을 수 있습니다. - 왜 관리되는 코드로 전환해야 하는가?
- 코드를 얼마나 변경해야 하는가?
- Enterprise Services 구성 요소는 어떻게 수행되는가?
- COM+ 및 .NET Enterprise Services가 제시하는 미래는 어떠한가?
이 문서에서는 특히 성능에 대한 질문을 위주로 위 주제를 다룰 것입니다. 이러한 주제에 대한 자세한 내용은 부록 4: 참고 자료에 있는 리소스를 참조하십시오. 이 문서는 COM+ 구성 요소를 개발하고 코드를 .NET Enterprise Services로 마이그레이션할 것을 고려 중인 개발자와 설계자를 대상으로 합니다. 관리되는 코드로 마이그레이션해야 하는 이유개발자가 .NET에서 코드를 개발해야 하는 이유는 여러 가지입니다. 다음은 코드 개발의 몇 가지 이점입니다. - 향상된 개발자 생산성: 개발자들은 종종 .NET을 사용하여 개발할 때 작성해야 하는 "통로 코드"가 훨씬 적기 때문에 응용 프로그램 논리를 작성하는 데 더 많은 신경을 쓸 수 있습니다. 또한 대부분의 개발자는 .NET에서 제공하는 명확하고 일관되게 구성된 풍부한 리소스 라이브러리 덕분에 다른 기술과 비교하여 훨씬 빨리 배울 수 있습니다.
- 향상된 코드 안정성 및 보안: 개발자는 원시 코드보다는 .NET을 사용할 때 안정적이고 보안된 코드를 더욱 쉽게 작성할 수 있습니다. 그 이유는 코드 액세스 보안 및 CLR(공용 언어 런타임) 같은 기능 때문입니다. 이러한 기능은 NET 코드가 다른 실행 중인 코드에 의도하지 않은 영향을 미치는 것을 방지하고 해커가 .NET 코드를 사용하여 환경을 방해하거나 제어할 수 있는 기회를 줄이는 데 도움이 됩니다.
- 향상된 성능 및 확장성: 개발자는 .NET으로 마이그레이션할 때 코드의 성능과 확장성이 향상되는 것을 알 수 있습니다. 모든 .NET 언어가 다중 스레딩 같은 기능을 지원하고 활용하기 때문입니다.
- XCOPY 배포: 대부분의 .NET 응용 프로그램에서는 필요한 파일을 하드 드라이브의 폴더로 복사하고 공유 구성 요소를 운영 체제에 선택적으로 등록하기만 하면 배포가 이루어집니다. 이는 다른 응용 프로그램에서 일반적으로 사용하는 것보다 훨씬 명쾌한 배포 전략입니다.
개발자가 .NET으로 마이그레이션하기를 원하는 10가지 이유에 대해서는 Top 10 Reasons for Developers to Use the .NET Framework 1.1 을 참조하십시오. .NET은 시스템 관리자에게도 이점을 제공합니다. 관리자가 .NET으로 이동하기를 원하는 10가지 이유가 기록된 목록을 보려면 Top 10 Reasons for Systems Administrators to Use the .NET Framework 1.1 을 참조하십시오. 일반 C++ 코드를 관리되는 C++로 마이그레이션하는 방법에 대한 소개는 Managed Extensions for C++ Migration Guide의 Introduction to Wrapping C++ Classes 를 참조하십시오. 코드 변경 정도 지정대부분의 경우 COM+ 코드를 .NET Enterprise Services에 연결하려면 COM+ 구성 요소가 어떠한 언어로 개발되었는지를 비롯한 다수의 요인에 따라 몇 가지 수동 작업이 필요합니다. 예를 들어, Visual Basic 6 개발자는 앞으로 나올 Visual Studio 2005의 도구에서 서브루틴 및 함수의 본문을 수정하지 않고도 클래스 정의 및 메서드 서명을 변환할 수 있는 몇 가지 지원을 받게 됩니다(다른 변환된 메서드에 대한 호출은 제외). C++ 개발자는 COM+ 코드에서 .NET 코드로 변환하는 작업을 대부분 수동으로 수행해야 하지만, 대부분의 통로가 ATL(Active Template Library) 같은 클래스 라이브러리 대신 CLR에 구현되기 때문에 변환된 코드가 좀 더 간결해지는 것을 확인할 수 있습니다. C++ 개발자는 응용 프로그램을 연결할 언어를 선택할 수도 있습니다. 예를 들어, 기존 코드 기반을 최대한 활용하기 위해 관리되는 C++를 선택하거나, 더욱 간결한 코드를 위해 C#을 선택할 수 있습니다. .NET Enterprise Services에 COM+ 구성 요소 연결기존 COM+ 코드를 Visual Basic 6 및 Visual C++ 같은 원시 프로그래밍 언어와 도구로부터 마이그레이션하는 개발자는 .NET 코드로 완벽하게 변환하기 위해 몇 가지 기존 코드를 수정해야 합니다. 여기에 동반되는 작업 양은 사용할 수 있는 기존 코드 기반 및 도구에 따라 다릅니다. 다음 표에는 코드를 .NET으로 마이그레이션할 때 고려할 옵션이 요약되어 있습니다. 변환하기 전의 언어 | 변환한 후의 언어 | 코드 변환 도구 사용 가능 여부 | 필요한 코드 변환 작업 |
---|
Visual Basic 6 | Visual Basic .NET | 예(Visual Studio 2005) | 대부분의 Visual Basic 6 코드는 Visual Basic .NET에 직접 연결됩니다. Visual Studio 2005의 Visual Basic 6 코드 마이그레이션 도구는 대부분의 클래스와 메서드 선언 및 형식을 Visual Basic .NET 구문으로 변환합니다. | Visual C++ | Visual C++ .NET | 아니요 | C++ .NET은 특히 원시 코드와 .NET 사이에서 상호 작동하는 코드를 작성하는 데 유용합니다. | Visual C++ | Visual C# | 아니요 | C# 구문은 많은 면에서 C++와 비슷합니다. 변환을 수행하려면 개발자의 특정 작업이 필요합니다. |
.NET 특성COM+ 구성 요소를 설치한 후에는 구성 요소 서비스 스냅인 도구를 사용하여 수동으로 구성하거나 스크립트 또는 코드를 통해 구성해야 합니다. 예를 들어, 구성 요소에 트랜잭션 지원이 필요하다고 표시하고 각 구성 요소의 AddSale() 메서드가 오류 없이 완료될 경우 트랜잭션을 자동으로 커밋하도록 지정하는 상황을 가정합니다. 그러기 위해서는 구성 요소 및 필요한 메서드의 속성을 COM+ 구성 요소 서비스 관리 콘솔에서 수동으로 구성해야 합니다. - 구성 요소 서비스 관리 콘솔을 열고 올바른 응용 프로그램 및 구성 요소를 탐색합니다.
- 이 구성 요소의 속성을 열고 트랜잭션 탭을 클릭한 다음 필수를 클릭합니다.
- 확인을 클릭하고 구성 요소 탐색기를 통해 ATLPerfTests 개체의 AddSale() 메서드를 찾습니다.
- AddSale() 메서드를 마우스 오른쪽 단추로 클릭하고 메서드의 속성을 연 다음 이 메서드가 반환하면 자동으로 이 개체 비활성화 확인란을 선택합니다.
관리자는 일반적으로 보안 및 ID 설정 같은 배포 지향 설정이나 역할 구성원 및 재활용 같은 런타임 지향 설정을 구성합니다. 개발자는 트랜잭션 지원과 같은 구성 요소의 개발 지향 기능을 구성합니다. 그러나 COM+에서는 개발자가 코드 내에서 구성 요소의 구성 방법을 지정하기가 어렵습니다. Visual Basic 개발자는 구성 요소에 필요한 트랜잭션 지원을 어느 정도 지정할 수 있지만 C++ 개발자는 그렇지 못합니다. COM+ 구성 요소를 안정적이면서 반복적으로 설치하기 위해서는 스크립트, 설치 관리자 응용 프로그램 또는 설치 지침을 작성해야 합니다. .NET은 개발자가 구성 요소의 코드 내에서 구성 요소에 필요한 서비스와 해당 서비스의 구성 방법을 지정할 수 있도록 함으로써 구성 요소 구성을 단순화합니다. 구성 요소가 설치되면 플랫폼에서 구성 설정을 자동으로 구성하지만, 이러한 구성 설정은 설치 후에 변경할 수 있습니다. COM+에서는 설치 후에 구성 설정을 변경할 수 있기 때문에 이러한 속성을 다수 변경할 때는 매우 주의를 기울여야 합니다. 예를 들어, 보안 설정을 변경하면 개체를 인스턴스화하고 개체의 메서드를 호출할 수 있는지의 권한 지정에 영향을 미칠 수 있습니다. 반면에 트랜잭션 지원을 제거하면 구성 요소가 안정적이지 못하고 예측 불가능해질 수 있으며 구성 요소의 데이터가 손실되거나 손상될 수도 있습니다. 개발자는 특성(C# 및 C++에서는 [attribute]와 같이 대괄호 안에 표시되고, Visual Basic .NET에서는 <attribute>와 같이 꺾쇠괄호 안에 표시됨)을 사용하여 어셈블리, 응용 프로그램, 구성 요소 또는 메서드의 요소를 구성합니다. 예를 들어, 아래 코드에서는 SimpleTest 구성 요소에 트랜잭션이 필요하며, AddSale() 메서드가 오류 없이 완료될 경우 트랜잭션이 자동으로 완료된다는 것을 보여줍니다. C# [Transaction(TransactionOption.RequiresNew)] public class SimpleTest: ServicedComponent { ... [AutoComplete] public void AddSale(int orderNumber, int storeID, int titleID, int qty) { ... } ... } Visual Basic .NET<Transaction(TransactionOption.RequiresNew)> _ Public Class VBTestObject : Inherits ServicedComponent ... <AutoComplete> _ Public Function Sum(ByVal number1 As Integer, ByVal number2 As Integer) As Integer ... End Function ... End Class 다음 표에는 Enterprise Services 구성 요소에 적용할 수 있는 주로 사용되는 특성이 들어 있고, 설치 후에 특성을 안전하게 변경할 수 있는지 여부가 표시되어 있습니다. 위 표는 구성 요소 개발자가 기본적으로 소유하는 구성 요소와 관리자가 기본적으로 소유하는 구성 요소도 보여 줍니다. 코드에 미치는 영향을 자세히 알고 있는 경우가 아니라면 개발자의 설정을 변경하지 않는 것이 좋습니다. 개발자가 지정한 보안 관련 특성을 관리자가 변경할 수는 있지만, 이 경우에는 구성 요소 또는 응용 프로그램에 대한 액세스를 충분히 제한하되 너무 과도하게 제한하지는 않도록 주의를 기울여 보안을 구성해야 합니다. 특성을 통해 개발자는 자신의 구성 요소에 대한 구성 요구 사항을 간단하고 효율적으로 지정하면서 설치 후에 관리자가 구성 설정을 변경할 수 있도록 합니다. Enterprise Services가 제공하는 특성에 대한 자세한 내용은 .NET Framework Class Library 를 참조하십시오. .NET Enterprise Services와 COM+ 성능 비교Enterprise Services의 성능을 COM+와 비교하여 측정하기 위해 다음 언어로 구성 요소를 만들었습니다. - Visual C++ .NET 및 ATL COM+
- Visual Basic 6 COM+
- C# 및 .NET Framework 1.1 Enterprise Services
- Visual Basic .NET 및 .NET Framework 1.1 Enterprise Services
각 구성 요소에는 두 개의 공용 메서드가 포함되어 있습니다. - Sum(): 이 간단한 메서드는 두 숫자를 합해 디스크 또는 데이터베이스 액세스 작업을 수행하지 않는 간단한 작업을 시뮬레이트합니다.
- AddSale(): 이 일반적인 메서드는 트랜잭션되며, 테이블에 레코드를 삽입하고 반환 전에 트랜잭션을 완료하는 개인 메서드인 InsertSale()을 호출합니다. 이 메서드는 일반적인 비즈니스 응용 프로그램 작업을 수행하는 "실제" 메서드의 성능 특징을 보여 줍니다.
그 다음에는 각 구성 요소에 대해 다음 테스트를 수행한 테스트 프로그램을 만들었습니다. - 반복적인 만들기/호출/릴리스: 이 테스트는 개체를 만들고 호출하고 릴리스하는 과정을 반복적으로 수행합니다.
- 만들기/반복 호출/릴리스: 이 테스트는 개체를 인스턴스화하여 수천 번 호출한 다음 마지막에 개체를 릴리스합니다.
테스트 프로그램에서는 각 구성 요소에 대해 두 가지 테스트를 모두 실행했으며 결과를 쉼표로 분리된 파일에 작성하는 고해상도 타이머를 사용하여 각 테스트를 수행하는 데 걸린 시간을 측정했습니다. 이 파일의 결과를 Microsoft Excel로 가져와서 분석했습니다. 각 구성 요소에 대한 코드 목록은 부록 5: 성능 테스트 소스 코드를 참조하십시오. 테스트는 다음 표에 표시된 구성으로 설정된 컴퓨터에서 실행되었습니다. | 컴퓨터 1: 서버 컴퓨터 | 컴퓨터 2: 클라이언트 컴퓨터/단일 컴퓨터 |
---|
CPU | Dual Pentium 4 Xeon 3.06GHz | Dual Pentium 4 Xeon 2.8GHz | RAM | 1GB | 1GB | 디스크 | 로컬 SCSI | 로컬 SCSI | 네트워크 | 기가비트 이더넷 | 기가비트 이더넷 | OS 및 .NET | Windows Server™ 2003 .NET Framework 1.1 | Windows Server 2003 .NET Framework 1.1 |
다른 하드웨어에서 테스트 응용 프로그램을 실행할 때 나타나는 특정 결과는 아래에 보고된 결과는 물론, 서로 간에도 다를 수 있습니다. 그러나 각 결과는 여기에 소개된 결과와 비례해야 합니다. 테스트 결과 및 분석다음 절에서는 이전에 논의한 코드에 대해 실행한 성능 테스트의 결과를 분석합니다. 이 결과는 위에 나열된 하드웨어와 소프트웨어에서 테스트를 실행하여 얻은 것입니다. 모든 결과의 목록은 부록 6: 테스트 결과에 포함되어 있습니다. 결과를 보여 주는 다음 차트에서 차트 막대 길이가 길거나 숫자가 클수록 성능이 뛰어난 것입니다. 개체 활성화 및 삭제 성능먼저 C++ 및 Visual Basic 6을 사용하여 개발한 원시 COM+ 구성 요소의 성능을 통해 COM+ 인프라가 어떻게 수행되는지를 살펴보겠습니다. 아래 차트는 개체 만들기, 간단한 메서드 호출 및 개체 릴리스를 반복적으로 수행함으로써 얻은 초당 호출 수를 보여 줍니다. MTS(Microsoft Transaction Server) 1.0의 원래 디자이너는 이와 비슷한 데이터를 보고서 호출을 배달하는 데 필요한 인프라, 즉 프록시, DCOM(또는 프로세스간) 채널, 스텁 및 컨텍스트를 설정함으로써 프로세스 간 및 컴퓨터 간 활성화 시간에 큰 영향을 미칠 수 있다는 것을 깨달았습니다. 이 점이 다음을 수행하는 JIT(Just In Time) 활성화를 디자인하게 된 기본적인 동기였습니다. - 서버 구성 요소가 반환 이전에 SetComplete() 또는 SetAbort()를 호출하여 자체 수명 주기를 제어할 수 있도록 합니다.
- 여러 메서드 호출을 사용하여 DCOM 통로를 설정하는 부담을 줄입니다.
다음 차트는 JIT 활성화를 활용하는 수정된 테스트를 단일 개체를 만들고 두 숫자를 합하는 간단한 메서드를 반복적으로 호출한 다음 마지막에 개체를 릴리스하는 방법으로 실행할 경우 발생하는 상황을 보여 줍니다. 이 결과는 JIT 활성화를 사용할 때 초당 호출 수 면에서 성능이 크게 향상되는 것을 보여 줍니다. JIT 활성화 및 Visual Basic 6을 사용하면 JIT 활성화를 사용하지 않는 C++보다 거의 33배 빠른 결과를 얻을 수 있습니다. JIT가 활성화된 Visual Basic 6에서는 초당 호출 수가 약 8600인 반면 JIT가 활성화되지 않은 Visual C++에서는 초당 호출 수가 약 261입니다. 작업을 수행하는 데 필요한 통로를 설정한 이후 컴퓨터 간 호출을 수행하면 네트워크가 성능에 큰 영향을 미치게 됩니다. 이 경우 Visual Basic 6 및 C++는 성능이 거의 비슷하지만, 그래도 Visual Basic 6가 C++보다 88% 빨리 수행됩니다. Enterprise Services에서는 필요한 통로를 설정하는 추가 작업이 수행됩니다. 특히 개체를 생성하고 릴리스하기 위한 추가 호출이 필요합니다. 따라서 개체에서 아무런 작업을 수행하지 않고 단순히 개체를 만들었다 제거하는 경우를 비교할 때 이러한 추가 왕복 부담이 성능 비교에 큰 영향을 미치게 됩니다. Visual Studio 2005에서는 Enterprise Services가 향상되어 이러한 활성화 왕복 중 하나가 제거되었기 때문에 "활성화/단일 호출/릴리스" 패턴을 사용할 때 .NET Framework 1.1과 비교하여 성능이 20-30% 향상됩니다. 그러나 가능하면 이러한 패턴은 사용하지 않는 것이 좋습니다. JIT 활성화를 사용할 때 C++ 및 Visual Basic 6의 성능이 비슷하다는 것을 고려하면 C# 및 Visual Basic .NET을 사용하는 Enterprise Services도 대략 같은 성능을 보일 것으로 예상할 수 있습니다. 아래 그림은 간단한 메서드를 호출하는 위 테스트를 실행한 결과를 보여 줍니다. 이 데이터는 단순히 두 정수를 합하고, 개체의 컨텍스트에 SetComplete()를 호출하고, 결과를 반환하는 간단한 메서드에 대해 수행한 초당 호출 수를 보여 줍니다. 개체를 활성화하고 릴리스하는 부담은 거의 사라졌지만 버퍼를 마샬링하고 호출 스택으로 변환하는 등의 작업으로 인해 호출을 수행하는 부담은 아직 남아 있습니다. 이처럼 매우 간단한 메서드에서도 프로세스 간 호출의 경우 Enterprise Services는 Visual Basic 6과 성능이 거의 비슷합니다. 컴퓨터간에 호출할 경우에는 모든 언어의 성능이 거의 비슷합니다. 그러나 일반적인 비즈니스 응용 프로그램은 메서드에서 이보다 더 복잡한 작업을 수행합니다. 다음 차트는 일반적인 메서드를 호출하여 분산 트랜잭션 내에서 데이터베이스 연결을 열고 간단한 SQL 문을 실행하는 동일한 응용 프로그램을 네 가지 언어로 작성했을 때 각각의 상대적인 성능을 보여 줍니다. 앞의 결과는 메서드 내에서 많은 작업을 수행할 경우 모든 언어의 결과가 실험 오차 범위 내에서 동등하다는 것을 보여 줍니다. ADO를 사용하는 C++ 및 Visual Basic 6을 통해 작성한 COM+ 원시 응용 프로그램은 Enterprise Services를 사용하는 C# 또는 Visual Basic .NET 응용 프로그램과 동일한 속도로 수행됩니다. 프로세스 간 작업을 실행하거나 컴퓨터 간 작업을 실행하는 경우 성능 면에서는 거의 차이가 없습니다. 결과 요약위 결과는 구성 요소를 최대한 효율적으로 수행하는 데 있어서 JIT 활성화 및 "만들기/반복 호출/릴리스" 패턴이 매우 중요하다는 것을 보여 줍니다. 결론코드를 .NET으로 마이그레이션하는 것이 유리한 몇 가지 주요 이유를 설명했습니다. 다른 활성화 및 호출 패턴에 적용될 때의 원시 COM+ 및 .NET Enterprise Services 구성 요소 성능에 대해서도 논의했습니다. 또한 지침을 따라가면서 .NET Enterprise Services 구성 요소가 C++ COM+ 구성 요소만큼 빨리 실행된다는 것을 보여 주었습니다. 부록 1: 성능 권장 사항에는 고성능 .NET Enterprise Service 구성 요소를 만드는 데 도움이 되는 주요 권장 사항이 나와 있습니다. 여기에 설명된 기술을 일관되게 적용하면 기존 COM+ 코드를 .NET Enterprise Service 구성 요소로 바로 변환하고 아무런 성능 저하 없이 .NET Framework의 사용성, 보안 및 개발자 생산성 등의 이점을 누릴 수 있습니다. 지금 COM+ 구성 요소를 Enterprise Services 구성 요소로 변환해 놓으면 나중에 코드를 "Indigo"로 더욱 쉽게 마이그레이션할 수 있다는 점도 중요합니다. 부록 2: "Indigo" 및 .NET의 동향에서는 이 주제에 대해 간략히 논의합니다. 부록 1: 성능 권장 사항다음 절에서는 높은 수준의 성능을 제공하는 빠른 COM+ 및 Enterprise Services 구성 요소를 만드는 방법에 대한 팁과 안내를 제공합니다. 대부분의 제안은 .NET Enterprise Services 구성 요소와 원시 COM+ 구성 요소에 동일하게 적용됩니다. 해당하는 경우 개체 풀링 및 JIT 활성화 사용위의 테스트 결과가 보여주듯이 메서드 호출이 구성 요소 활성화보다 빠르며 관리되지 않는 구성 요소의 활성화가 Enterprise Services 구성 요소의 활성화보다 빠릅니다. 따라서 구성 요소 기반 응용 프로그램의 속도를 최대한 높이려면 코드에서 구성 요소 활성화 및 삭제의 수를 최소화하는 것이 중요합니다. COM+에서는 개체 활성화를 최소화할 수 있는 두 가지 서비스를 제공합니다. - 첫 번째는 앞에서 설명했듯이 호출자가 개체에 대한 활성 참조를 보관하고 있는 동안 해당 개체를 원활하게 비활성화할 수 있는 COM+ 서비스인 JIT(Just-in-time) 활성화입니다. 클라이언트가 개체에 메서드를 호출하기만 하면 COM+가 개체의 할당을 동적으로 관리하여 요청을 처리합니다.
- 두 번째는 해당 형식의 구성 요소 인스턴스를 요청하는 클라이언트가 바로 사용할 수 있도록 개체를 풀에서 활성 상태로 유지하는 개체 풀링입니다. COM+는 풀을 자동으로 관리하며, 개체 활성화 정보를 처리하고 사용자가 지정한 기준(예: 풀 크기)에 따라 다시 사용합니다.
풀링된 구성 요소 및 JIT 활성화된 구성 요소에 대한 참조를 보관하고 다시 사용함으로써 구성 요소 활성화 및 삭제를 최소화하고 높은 수준의 성능을 얻을 수 있습니다. COM+ JIT 활성화 및 개체 풀링에 대한 자세한 내용은 Platform SDK: COM+ (Component Services) 설명서 를 참조하십시오. 왕복 회피COM+ 구성 요소의 성능을 최적화하려면 호출자와 구성 요소 사이에 수행되는 프로세스 간 또는 컴퓨터 간 호출의 수를 최소화하는 것이 중요합니다. COM+ 구성 요소에서 만들어진 모든 메서드는 프로세스 간 호출 전환은 물론 컴퓨터 간 호출 전환도 초래하며, 매번 전환할 때마다 시간이 걸립니다. 따라서 COM+ 개체에서 만들어지는 메서드 호출을 최소한으로 유지하는 것이 필수적입니다. 그러기 위해 단일 호출에서 최대한 많은 작업을 수행하는 메서드를 사용하여 COM+ 구성 요소를 디자인하는 것도 좋은 방법입니다. 단, 이 경우에는 순수 아키텍처에서 변형된 구성 요소도 디자인해야 합니다. COM+ 서비스 사용 최적화COM+가 중요한 서비스를 다수 제공하기는 하지만 이러한 서비스를 현명하게 사용하는 것이 중요합니다. COM+가 구성 요소에 필요한 서비스를 제공하는 경우에는 대개 이 서비스가 가장 높은 성능을 제공하므로 반드시 사용하도록 하십시오. 그러나 서비스가 필요하지 않은 경우에는 구성 요소가 불필요한 작업을 수행하여 실행 속도도 느려질 수 있으므로 해당 서비스를 사용할 필요가 없습니다. COM 마샬링 가능 매개 변수 사용호출자가 데이터를 전달할 때 사용하는 매개 변수를 Enterprise Services 구성 요소의 메서드가 받아들이는 경우 다음과 같이 COM과 .NET 사이에 쉽게 마샬링할 수 있는 형식을 사용할 것을 매우 강력히 제안합니다. - Boolean
- Byte, SByte
- Char
- DateTime
- Decimal
- Single, Double
- Guid
- Int16, UInt16, Int32, UInt32, Int64, UInt64
- IntPtr, UIntPtr
- String
이러한 형식만 사용하고 다른 복잡한 형식(예: 구조 또는 배열)은 전달하지 않으면 .NET serializer가 호출 처리 스택을 최적화하고 호출을 유선(RPC의 경우) 또는 가상 유선(LRPC의 경우)에 일렬로 직렬화할 수 있습니다. 그러면 호출이 더욱 빨리 실행됩니다. 그러나 메서드에 복잡한 형식이 필요한 경우에는 코드가 일반 DCOM 호출 스택을 통해 호출되므로 추가 처리 과정이 발생합니다. 파이널라이저 사용하지 않기Enterprise Services 구성 요소에서 파이널라이저(C# 및 C++의 ~Classname() 소멸자)를 구현하지 마십시오. 파이널라이제이션은 가비지 수집기의 단일 스레드 작업입니다. Enterprise 구성 요소를 파이널라이제이션할 경우 완료되는 데 상당한 시간이 걸리므로 가비지 수집기 성능이 저하됩니다. 가비지 수집기 엔진이 실행 중인 동안에는 응용 프로그램에서 다른 작업을 수행할 수 없으므로 가비지 수집기가 완료되는 데 오랜 시간이 걸리면 전체 응용 프로그램의 성능이 저하됩니다. 대신 개체에서 Dispose(bool)을 재정의하고 Dispose(true)가 호출될 때 파이널라이제이션 형식 작업을 수행하는 것을 고려해 보십시오. 또한 이러한 종료 코드를 최대한 정돈되고, 안전하고, 간단하게 유지하도록 하십시오. 단일 스레드 COM+ 구성 요소 만들지 않기여러 스레드의 동시 액세스를 지원하지 않는 개체는 STA(단일 스레드 아파트) 기능을 지원하는 것으로 표시되어 있습니다. 여러 스레드가 같은 인스턴스에 동시 액세스할 수 있도록 지원하는 구성 요소는 MTA(다중 스레드 아파트) 인식으로 표시되어 있습니다. .NET Enterprise Services 구성 요소는 항상 STA 및 MTA를 모두 지원하는 것으로 표시되어 있기 때문에 더 이상 이 논의에 포함시키지 않겠습니다. 모든 Visual Basic 6 COM+ 구성 요소는 STA입니다. C++ COM+ 개발자는 구성 요소를 STA, MTA 또는 둘 다로 표시할 수 있습니다. STA COM+ 구성 요소의 잠재적인 문제는 개체가 단일 스레드에서만 실행될 수 있으므로 해당 스레드가 개체의 메서드를 실행하는 유일한 스레드라는 것입니다. 이 직렬화를 통해 개발자는 STA 구성 요소를 더욱 쉽게 작성할 수 있지만, 도메인 간 마샬링이 종종 필요해서 성능이 저하되고 STA에서 한 스레드만 계속 실행됨으로 인해 확장성이 떨어지는 것은 피할 수 없습니다. 가능하면 STA 구성 요소를 만들거나 사용하지 않을 것을 제안합니다. 특히 확장성이 중요한 곳에서는 더욱 그렇습니다. 구성 요소가 다른 COM+ 구성 요소를 호출하는 경우 특히 STA 스레딩을 피해야 합니다. 이러한 호출에는 종종 스레드 전환이 필요한데, 스레드 전환은 해당 아파트에 있는 다른 모든 COM+ 구성 요소를 차단합니다. 설상가상으로 가비지 수집기의 파이널라이저도 개체를 소유한 STA 스레드를 호출할 때 차단됩니다. 그러면 파이널라이제이션 프로세스가 단일 스레드에 직렬화되며, 이는 이전 주제에서 설명했듯이 시스템 성능을 크게 저하시킬 수 있습니다. 부록 2: "Indigo" 및 .NET의 동향Microsoft에서 현재 개발 중인 연결된 응용 프로그램을 위한 새로운 플랫폼, 코드 이름 "Indigo"에 대해 들어 보셨을 것입니다. 그렇다면 "Indigo"란 과연 어떤 제품일까요? "Indigo"는 서비스 지향 연결 응용 프로그램을 위한 Microsoft의 전략적 기술 플랫폼으로서, 로스앤젤레스에서 개최된 2003 PDC(Professional Developers Conference)에서 소개되었습니다. "Indigo"는 다음 기술의 개념, 특징 및 기능을 하나의 기술 스택으로 통합합니다. - COM
- DCOM
- COM+/Enterprise Services
- ASMX/Web Services
- .NET Remoting
- 향상된 웹 서비스
- MSMQ의 요소
"Indigo"는 서비스를 호출자에게 노출하는 데 필요한 프로토콜 및 전송으로부터 서비스의 개념을 추상화하는 다계층 플랫폼입니다. 최대한 많은 시스템과 상호 작동하기 위해 "Indigo"는 HTTP, TCP 및 IPC를 통한 고급 웹 서비스(WS-*)를 완벽하게 지원합니다. 현재 Microsoft는 코드 이름이 "Longhorn"인 Microsoft Windows의 출시 시기에 맞춰 "Indigo"를 배포할 예정입니다. Windows XP 및 Windows Server 2003에 대한 "Indigo" 지원도 함께 배포될 것입니다. 향후 "Indigo"가 발표된다고 하니 .NET Enterprise Services(또는 이 경우 ASMX 및 Remoting) 같은 기존 기술이 오늘날의 연결된 응용 프로그램을 개발하는 데 아직 유효한 것인지 걱정할 수도 있습니다. ASMX & WSE, Enterprise Services, Remoting 및 MSMQ는 오늘날의 엔터프라이즈 수준 솔루션에 가장 적합한 기술입니다. 적절하게 사용하면 "Indigo"가 릴리스되고 널리 사용될 때까지 훌륭한 응용 프로그램 개발 플랫폼을 제공해 줄 것입니다. .NET을 사용하여 새 응용 프로그램을 작성하고 기존 응용 프로그램을 .NET으로 마이그레이션하면 향상된 보안, 안정성, 관리 및 확장성의 혜택을 누릴 수 있습니다. 또한 "Indigo"로 업그레이드하는 것이 원시 코드보다 훨씬 쉬워질 것입니다. 응용 프로그램의 "Indigo" 업그레이드를 준비하는 방법과 기존 기술을 사용하여 "Indigo"와 상호 작동하는 방법에 대한 자세한 내용은 나중에 MSDN을 참조하십시오. "Indigo"에 대한 자세한 소개는 MSDN Magazine 기사, Code Name Indigo: A Guide to Developing and Running Connected Systems with Indigo 를 참조하십시오. 이 기사에 제공된 아키텍처 개요를 통해 이후의 Microsoft 응용 프로그램 플랫폼을 미리 살펴볼 수 있습니다. "Indigo"에 대한 일반적인 내용은 Microsoft "Indigo" Frequently Asked Questions 를 참조하십시오. 부록 3: 분산 트랜잭션이 성능에 미치는 영향위 테스트를 진행하면서 "COM+ 분산 트랜잭션이 이러한 구성 요소에 얼마나 많은 영향을 미칠까?"라는 질문이 생길 수도 있습니다. 이 질문의 답을 찾기 위해 각 구성 요소에 대해 COM+에서 "트랜잭션 필요" 설정을 끄고 테스트를 다시 실행해 보았습니다. 그 결과는 다음 차트에 표시되어 있습니다. 위 차트에서 볼 수 있듯이 COM+ 트랜잭션 지원이 없는 구성 요소의 성능과 트랜잭션이 켜져 있는 구성 요소의 성능은 거의 동일합니다. 이 결과는 테스트에서 COM+ 트랜잭션의 영향을 무시해도 상관없다는 것을 분명하게 보여 줍니다. 부록 4: 참고 자료Upgrading Microsoft Visual Basic 6.0 to Microsoft Visual Basic .NET Visual Basic 6.0 응용 프로그램을 Visual Basic .NET으로 업그레이드하는 내용을 다루며 프로그래밍 팁, 요령 및 단계별 코드 비교 내용을 함께 제공합니다. Programming with Managed Extensions for Microsoft Visual C++ .NET Visual C++ .NET 2003에 맞춰 업데이트된 이 책은 개발자를 위해 컴파일러의 새 기능과 언어에 대한 링커 확장을 심층적이고 전문적으로 다룹니다. .NET Enterprise Services and COM+ 1.5 Architecture Microsoft .NET 및 Enterprise Services가 어떻게 조화를 이루는지 보여 주고 COM+/Enterprise Services 구성 요소를 빌드, 제어, 관리 및 보안하는 방법을 설명합니다. .NET Framework Developer Center의 Performance 페이지 고성능 코드를 작성하는 방법과 발생하는 문제를 진단하는 방법에 대해 심도있게 다루는 여러 링크와 리소스가 제공됩니다. Performance Tips and Tricks in .NET Applications .NET 응용 프로그램이 제대로 수행되도록 하기 위한 팁과 힌트를 모아 놓았습니다. Writing Faster Managed Code: Know What Things Cost .NET 코드에서 다양한 작업이 시스템에 어떤 부담을 주는지 자세히 분석한 내용입니다. Garbage Collector Basics and Performance Hints 가비지 수집기의 작동 방법, 가비지 수집기가 코드에 미치는 영향, 가비지 수집의 영향을 최소화하도록 코드를 작성하는 방법 등에 대해 설명합니다. Performance Considerations for Run-Time Technologies in the .NET Framework 가비지 수집 및 메모리 사용, JIT, 스레딩, .NET Remoting, 보안 등의 주제를 다룹니다. 부록 5: 성능 테스트 소스 코드C++\ATL 구성 요소헤더 파일// ATLPerfTestObj.h : CATLPerfTestObj의 선언 #pragma once #include "ATLPerfTests.h" #include "resource.h" // 주 기호입니다. #include <comsvcs.h> #include <mtxattr.h> // CATLPerfTestObj class ATL_NO_VTABLE CATLPerfTestObj : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CATLPerfTestObj, &CLSID_ATLPerfTestObj>, public IDispatchImpl<IPerfTestObj, &IID_IPerfTestObj, &LIBID_ATLPerfTestsLib, /*wMajor =*/ 1, /*wMinor =*/ 0> { public : CATLPerfTestObj() { } DECLARE_PROTECT_FINAL_CONSTRUCT() HRESULT FinalConstruct() { return S_OK; } void FinalRelease() { } DECLARE_REGISTRY_RESOURCEID(IDR_ATLPERFTESTOBJ) DECLARE_NOT_AGGREGATABLE(CATLPerfTestObj) BEGIN_COM_MAP(CATLPerfTestObj) COM_INTERFACE_ENTRY(IPerfTestObj) COM_INTERFACE_ENTRY(IDispatch) END_COM_MAP() // IPerfTestObj public : STDMETHOD(Sum)(LONG number1, LONG number2, LONG* result); STDMETHOD(AddSale)(LONG orderNumber, LONG storeID, LONG titleID, LONG qty); private : HRESULT InsertSaleRecord(LONG orderNumber, LONG storeID, LONG titleID, LONG qty); }; OBJECT_ENTRY_AUTO(__uuidof(ATLPerfTestObj), CATLPerfTestObj)(참고: 프로그래머 코멘트는 샘플 프로그램 파일에는 영문으로 제공되며 기사에는 설명을 위해 번역문으로 제공됩니다.) 소스 파일// ATLPerfTestObj.cpp : CATLPerfTestObj의 구현 #include "stdafx.h" #include "ATLPerfTestObj.h" #include ".\atlperftestobj.h" #include "atlstr.h" #import "c:\Program Files\Common Files\System\ADO\msado15.dll" rename_namespace("ADO") rename("EOF", "EndOfFile") using namespace ADO; // CATLPerfTestObj // 여기에서 간단한 작업을 수행하여 간단한 메서드를 시뮬레이트합니다. STDMETHODIMP CATLPerfTestObj::Sum(LONG number1, LONG number2, LONG* result) { // 개체 컨텍스트를 가져옵니다. IObjectContext* ctx = NULL; HRESULT hr = CoGetObjectContext(IID_IObjectContext, (LPVOID*)&ctx); if(SUCCEEDED(hr)) { // 계산을 수행합니다. *result = number1 + number2; // 트랜잭션을 커밋합니다. ctx->SetComplete(); ctx->Release(); } return hr; } STDMETHODIMP CATLPerfTestObj::AddSale(LONG orderNumber, LONG storeID, LONG titleID, LONG qty) { // COM+ 개체 컨텍스트를 가져옵니다. IObjectContext* ctx = NULL; HRESULT hr = CoGetObjectContext(IID_IObjectContext, (LPVOID*)&ctx); // COM+ 컨텍스트가 있는지 확인합니다. if(SUCCEEDED(hr)) { // 기본 작업은 중단하는 것입니다. ctx->SetAbort(); // 데이터베이스에 레코드를 삽입합니다. hr = InsertSaleRecord(orderNumber, storeID, titleID, qty); // 삽입 작업의 결과를 확인합니다. if(SUCCEEDED(hr)) { // 모든 것이 올바르면 트랜잭션을 완료로 // 표시합니다. ctx->SetComplete(); } // 컨텍스트 포인터를 정리합니다. ctx->Release(); ctx = NULL; } // 전체 결과를 반환합니다. 삽입 작업이 오류 없이 // 실행되는 경우에만 결과가 S_OK입니다. return hr; } // 데이터베이스에 판매 레코드를 삽입합니다. HRESULT CATLPerfTestObj::InsertSaleRecord(LONG orderNumber, LONG storeID, LONG titleID, LONG qty) { // 기본 결과는 실패를 반환하는 것입니다. HRESULT hr = E_FAIL; try { // 서버에서 실행할 SQL을 포맷합니다. CString str; str.Format("insert into sales (order_no, store_id, \ title_id, order_date, qty) values (%i, %i, %i, \ GetDate(), %i)", orderNumber, storeID, titleID, qty); // 데이터베이스에 대한 연결을 엽니다. _ConnectionPtr cn("ADODB.Connection"); cn->Open("Provider=SQLOLEDB;SERVER=localhost;Integrated \ Security=SSPI;DATABASE=ESPERFTESTDB", "", "", adConnectUnspecified); // 명령을 실행합니다. _variant_t rs; rs = cn->Execute(_bstr_t(str), &rs, adCmdText); hr = S_OK; // 연결을 명시적으로 닫습니다. cn->Close(); } catch(_com_error e) { // 문제가 있으면 오류가 반환됩니다. hr = e.Error(); } return hr; } Visual Basic 6 구성 요소Private Function IPerfTestObj_Sum(ByVal nA As Long, ByVal nB As Long) As Long IPerfTestObj_Sum = nA + nB GetObjectContext.SetComplete End Function Sub IPerfTestObj_AddSale(ByVal OrderNumber As Long, ByVal StoreID As Long, ByVal TitleID As Long, ByVal Qty As Long) ' 기본값은 오류가 throw될 경우 중단하는 것입니다. GetObjectContext.SetAbort Call InsertSaleRecord(OrderNumber, StoreID, TitleID, Qty) ' 실패하지 않았기 때문에 트랜잭션을 완료할 수 있습니다. GetObjectContext.SetComplete End Sub Sub InsertSaleRecord(ByVal OrderNumber As Long, ByVal StoreID As Long, ByVal TitleID As Long, ByVal Qty As Long) Dim command As String connDB.Open ("Provider=SQLOLEDB;SERVER=localhost;Integrated" + _ "Security=SSPI;DATABASE=ESPERFTESTDB") command = "insert into sales (store_id, order_no, order_date, qty, title_id) values (" & StoreID & ", " & OrderNumber & ", GetDate(), " & Qty & ", " & TitleID & ")" connDB.Execute (command) connDB.Close End Sub C# 구성 요소using System; using System.EnterpriseServices; using System.Data.SqlClient; using System.Reflection; using System.Runtime.InteropServices; using perftestsinterop; [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyKeyFile("..\\..\\sign.key")] [assembly: ApplicationName("PerfTest")] [assembly: ApplicationActivation(ActivationOption.Server)] [assembly: ApplicationAccessControl(false)] namespace CSPerfTests { [Guid("0BA5534E-8544-42e2-A909-3265105BEA09")] [Transaction(TransactionOption.Required)] public class CSPerfTestObj : ServicedComponent, IPerfTestObj { public CSPerfTestObj() { } // 여기에서 간단한 작업을 수행하여 간단한 메서드를 // 시뮬레이트합니다. public int Sum(int number1, int number2) { int result = 0; // 계산을 수행합니다. result = number1 + number2; // 트랜잭션을 커밋합니다. ContextUtil.SetComplete(); return result; } // 트랜잭션에 새 판매 항목을 추가합니다. public void AddSale(int orderNumber, int storeID, int titleID, int qty) { try { // 데이터베이스에 레코드를 삽입합니다. InsertSaleRecord(orderNumber, storeID, titleID, qty); // 모든 것이 올바르면 트랜잭션을 완료로 // 표시합니다. ContextUtil.SetComplete(); } catch(Exception) { ContextUtil.SetAbort(); throw; } } // 데이터베이스에 판매 레코
Posted by tornado
출처 : http://network.hanbitbook.co.kr/view.php?bi_id=1090 저자: 한동훈 / traxacun@gmail.com
[지난기사보기] • ASP.NET 가이드 4. 통합 예외처리 • ASP.NET 가이드 3. UI 향상 및 사용자 템플릿 만들기 • ASP.NET 가이드 2. 숫자/문자 입력 텍스트 박스 만들기 • ASP.NET 가이드 1. 자바 스크립트 사용하기
프로젝트 진행시에 겪게되는 어려움 중에 하나는 코드 문서화에 대한 것이다. 거의 모든 회사들은 일정이 빠듯하다고 여기며 코딩이 끝난 구현물을 토대로 문서화를 만들게 된다.
[코딩후 문서화]는 여러가지 문제점을 갖고 있다. 개발자가 구현시에 고려했던 내용들을 문서화하는 시점에서는 잊혀지기 때문에 문서화에서 누락되는 경우가 발생한다. 나중에 문제가 발생한 경우 문서만으로 문제를 해결하지 못하고, 원 코드 제작자에게 문의하여 해결하게 되는 것도 이런 탓이다.
코딩후 문서화가 갖는 다른 문제는 개발 문서, 사용자 설명서, 사용자 시나리오 문서를 구분하지 못한채 문서화되는 경우가 많다는 점이다. 대체로 개발 문서에서 코드만 제거하고 사용자 설명서가 되는 경우도 많다.
여기서는 코딩을 하면서 바로바로 문서화할 수 있는 것을 도와주는 NDoc에 대해 살펴볼 것이다. Visual Studio .NET에는 [도구] → [주석 웹페이지 빌드]를 통해 XML 문서화된 내용을 도움말로 빌드해주는 기능을 갖고 있지만 부족한 부분이 많다. 웹 페이지 뿐만 아니라 윈도우 도움말(.CHM, .HLP)까지 함께 빌드해 줄 수 있는 NDoc 사용법을 살펴볼 것이다.
오픈소스 문서화 도구중에 DoxyGen은 C#의 XML 주석을 이용하지 않고 자체 태그를 사용하고 있기 때문에 .NET 코드 문서화에는 사용하지 않기 때문에 다루지 않는다. 관심있는 분은 DoxyGen 사이트를 참고하기 바란다.
XML 주석 작성하기
.NET에서는 XML 문서화를 지원하고 있으며 사용할 수 있는 태그목록은 다음과 같다. 보다 자세한 사항은 Recommeded Tags for Documentation Comments를 참고하기 바란다.
[표1] XML 문서화 태그 목록 XML 태그 이름에서 알 수 있는 것처럼 대충의 사용 용도는 알 수 있으며 Visual Studio.NET에서는 해당 태그를 입력하면 자동으로 태그의 형식을 입력해준다. Visual Studio .NET이 완성해주는 XML 문서화 태그 입력이 부족하다고 여겨진다면 GhostDoc을 사용하자. 영어로 문서화를 해야하는 경우엔 유용하게 사용할 수 있다. 다음은 BasePage에 필자가 BR 태그를 개행문자로 변환하기 위해 만든 메서드와 그에 대한 주석이다.
[그림1] XML 문서화 사용 예 SUMMARY 태그는 해당 클래스나 메서드의 내용을 요약해서 보여주기 위해 사용한다. REMARKS 태그는 그에 대한 간단한 설명을 덧붙이기 위해 사용하며 EXAMPLE 태그는 예제설명이 시작이라는 것을 나타내며, 예제 코드에 대한 제목 역할을 한다. CODE 태그는 다른 사람이 참고할 수 있는 예제코드를 작성하는 부분이다. PARAM 태그는 각 메서드의 매개변수에 대한 설명이며, RETURNS는 반환값에 대한 주석을 작성할 수 있다. 이렇게 작성된 코드는 컴파일시에 XML 부분만 별도로 작성되는데 이를 위해서는 프로젝트 설정을 해야한다. 명령줄에서 csc.exe를 사용하여 직접 컴파일시에는 /doc 옵션으로 지정할 수 있다.
XML 문서화 옵션 지정
Visual Studio.NET에서 [프로젝트] - [BasePage 속성(P)]를 클릭한다.
[그림2] XML 문서화 설정하기 [XML 문서 파일]에 그림2와 같이 입력한다. 이제 [빌드] - [솔루션 다시 빌드]를 선택하면 전체 프로젝트를 다시 빌드하면서 XML 문서를 생성한다. 개발환경에서 빌드시에는 소스 코드 전체를 빌드하는 대신 수정한 부분만 다시 빌드한다. 이러한 부분 빌드시에는 XML 문서 파일이 갱신되지 않기 때문에 위와 같이 다시 빌드를 해야한다. 이렇게 생성한 XML 문서 파일과 NDoc을 이용해서 문서파일을 자동으로 생성해보자.
NDoc 문서화
NDoc을 받아서 설치했다고 가정하고 NDoc 사용법을 바로 살펴보자.
[그림3] NDoc 시작화면 [Add] 버튼을 클릭한다.
[그림4] 어셈블리와 XML 문서 추가 BasePage.dll과 앞에서 생성한 XML 문서, Mona.Web.UI.BasePage.xml을 추가하고 [OK]를 클릭한다.
다시 NDoc 메인화면으로 돌아오면 화면 중간에 다양한 속성을 설정할 수 있는 부분이 있다. 이 설정을 제대로 해야만 한글이 깨지지 않고 문서화가 된다. NDoc에서 설정할 필요가 있는 속성들만 다음에 나열하였다.
[표2] NDoc 속성 설정 언어 설정을 위해서는 SdkDocLanguage와 LangID를 변경하면 되며, DocumentInherited로 되어 있는 속성들은 기본값이 True인데 False로 변경하였다. 그렇지 않으면 .NET Framework의 기본 멤버들이 모두 문서화되어 여러분이 작성한 메서드보다 프레임워크에서 상속한 메서드에 대한 설명이 더 많아지게 된다. 실제로, .NET Framework 멤버에 대한 설명은 MSDN을 참고해야할 부분이지 여러분이 작성할 문서에서 참고할 것은 아니다.
Internals와 Privates는 클래스내의 내부 데이터나 메서드에 대한 참조용으로 문서화를 하기 위해 True로 하였다. 만약, 다른 업체를 위한 외부용 문서라면 이 부분은 False로 설정하기 바란다.
NDoc 프로젝트도 BasePage 프로젝트 디렉터리와 같은 곳에 저장하고, VS.NET에서도 BasePage.ndoc 파일을 추가한다. ndoc 확장자를 NDoc 프로그램과 연결해두면 언제든지 VS.NET에서 NDoc을 불러서 설정할 수 있다. 여기까지 설정을 모두 마쳤으면 NDoc에서 [Documentation] - [Build]를 선택해서 문서를 빌드한다. 최종 컴파일이 끝나면 doc 디렉터리에 생성된 .CHM 도움말을 볼 수 있으며, NDoc에서는 [Documentation] - [View]를 선택해서 볼 수 있다.
[그림5] BasePage 도움말 [그림5]에서 볼 수 있는 것처럼 Br2Nl 메서드에 대한 설명이 잘 정리되어 나타나는 것을 볼 수 있다. [그림1]과 [그림5]를 비교하면 XML 문서태그의 어떤 부분이 어떤식으로 보여지는지 알 수 있다. NDoc에서 문서화 옵션을 HTML & CHM으로 선택했기 때문에 웹 사이트에 바로 올려놓고 참조할 수 있는 HTML 페이지도 doc 디렉터리에 작성되어 있다. doc\index.html 파일을 열어보면 HTML 형식의 도움말이 어떻게 작성되었는지 살펴볼 수 있다.
NDoc을 이용한 문서화에서 한가지 걸리는 부분이 있는데, 클래스나 메서드 설명에서 공백문자가 ?로 표시되는 부분이다.
이를 위해서는 처음 NDoc을 실행해서 CHM 파일의 원본이 되는 HTML 파일을 만들고, 모든 HTML 파일에서 ?를 공백문자로 변환하고, 다시 NDoc에서 CHM 파일을 작성하는 편법을 이용한다.
? 제거하기
윈도우 환경에서는 mreplace와 같은 유틸리티를 이용하여 해결한다. Visual Studio .NET 2003에도 텍스트 파일의 내용을 한 번에 변경하는 기능을 제공하지만 프로젝트에 포함된 파일에 대해서만 사용할 수 있다. 파일안의 텍스트를 변경하기 위해 엄청나게 많은 수의 HTML 파일을 프로젝트에 등록하는 것은 번거롭다. 따라서 윈도우 환경에서는 mreplace와 같은 유틸리티를 쓰거나 필자와 같이 UNIX 계열 운영체제 명령어에 대한 배경지식이 있는 사람은 Cygwin을 이용할 수 있다. 또는 Mono를 사용하는 분들도 sed를 이용해서 간단하게 변경할 수 있다.
[그림6] mreplace 유틸리티 ?를 공백문자로 변경해야 하는데 HTML 형식으로 변경하기 때문에 변경할 문자열에 를 입력한다. HTML 파일에 대해서만 적용할 것이므로 File mask에는 *.html을 입력한다. [OK]를 클릭하면 모든 파일의 내용이 변경된다. Cygwin이나 UNIX 계열에서 이와 같은 작업을 하려면 다음과 같이 하면 된다.
root@ns2:~/doc# find *.html -print | > while read file > do sed -e 's/\?/\ /g' "$file" > "$file.new" > mv "$file.new" "$file" > done
HTML 파일을 정리했으면 doc 디렉터리로 이동한다. 여기에 보면 Mona.Web.UI.BasePage.hhp 파일이 있다. HTML Help Workshop의 프로젝트 파일이며, NDoc에서 자동으로 생성한 것이다. HTML Help Workshop을 설치했다면 이 파일을 클릭해서 열 수 있다.
[그림7] HTML Help Workshop 실행화면 [File] - [Compile]을 선택해서 CHM 도움말을 빌드하면 ?등이 사라진 깨끗한 도움말을 볼 수 있다.
Br2Nl과 Nl2Br 함수
사용자가 웹에서 여러 줄을 입력할 때 각 줄의 끝에는 개행문자가 붙지만, HTML 웹 페이지를 볼 때 줄 바꿈을 하려면 개행문자가 아니라 BR 태그를 사용해야 한다. BR 태그는 각 줄을 잘라내는 BReak를 뜻한다. 반대로 BR 태그가 들어간 문서를 수정할 때는 BR 태그가 아닌 개행문자로 보여주어야만 화면에 여러줄로 제대로 표시된다.
String.Replace( "\r\n", "" ); String.Replace( "", "\r\n" );
위와 같은 코드를 사용하면 Br2Nl이나 Nl2Br 함수를 만들 수 있다고 생각하기 쉽지만 실제로 웹 페이지는 <BR>, <br>, <BR/>, <BR />, <Br>, <br />, <br />과 같이 얼마든지 다양한 형태로 쓰일 수 있다. 따라서 문자열 형태의 치환이 아닌 정규식을 사용하여 제대로 치환하여야 한다. 마찬가지로 윈도우 환경에서는 개행문자를 \r\n으로 사용하지만 비윈도우 환경에서는 개행문자를 \n으로 사용한다. Linux 환경의 Apache에서 mod_mono를 이용하거나 xsp를 이용하는 경우와 윈도우 IIS 환경에서 동작하는 경우에도 문제없이 변환하려면 \r\n이나 \n 어느 한쪽으로만 변환해서는 안된다. 이런 경우에는 Environment.NewLine 상수를 사용해야 한다.
Replace 함수
Br2Nl과 Nl2Br 함수에서는 Replace를 사용했는데 이는 System.Text.RegularExpressions.RegEx 클래스를 이용해서 만든 함수다.
[그림8] Replace 함수 Replace 함수는 위 코드처럼 모두 5개의 매개변수로 되어 있으며, 마지막 2개는 몇 개나 일치시킬 것인가를 나타내는 count와 정규식 패턴 매치를 시작할 위치를 나타내는 startPos로 되어 있다.
5회까지 걸쳐 작성된 전체 소스는 이곳에서 다운받기 바란다.
참고자료
• HTML Help Workshop MS에서 제공하는 무료 도움말 제작도구이며 CHM 형식의 도움말을 제작하거나 해제할 수 있다.
• 유닉스 파워 풀(UNIX POWER TOOLS), 9.9 찾아낸 파일에 대해 원하는 명령을 실행하는 방법 UNIX 환경에서 파일의 내용을 치환하는 스크립트 작성에 사용했다.
• C# Cookbook, O'Reilly, Recipe 8.5 - Replacing Characters or Words in a String 정규식을 이용한 손쉬운 문자열 치환을 수행하는 Replace 함수는 Recipe 8.5에서 인용했다.
• Ndoc .NET의 XML 문서화를 자동화하기 위한 도구로 Java의 XML 문서화를 자동화하는 JDoc과 같은 이름을 따르는 문서화 도구다.
• DoxyGen C/C++을 위한 문서화 도구로 개발되었고, 현재는 Java, PHP, C#과 같은 다양한 언어들을 지원합니다. C/C++ 문서화 도구가 필요하다면 DoxyGen을 권합니다.
• GhostDoc VS.NET 환경에서 개발언어에 관계없이 XML 문서화를 자동화해주는 도구입니다. 특히, 메서드 이름을 잘 지은 경우에는 그것을 토대로 요약까지 자동으로 작성합니다. 메서드 이름 위에서 오른쪽 클릭해서 선택하면 자동으로 필요한 XML 태그를 작성해줍니다.
• mreplace Modula-2용으로 만든 유틸리티로 여기서는 파일 텍스트의 내용을 일괄적으로 변경하기 위해 사용했습니다. 윈도우 사용자가 사용하기에 가장 간단하고 편리합니다. 마지막에 사용한 설정을 기억하므로 프로젝트 작업시에 반복하는 일은 많지 않습니다.
Posted by tornado
ASP.NET의 Visual SourceSafe 사용에 관한 모든 것Paul Sheriff, Michael Krasowski PDSA, Inc. 2003년 12월 요약: Microsoft Visual SourceSafe를 사용하여 ASP.NET 프로젝트를 관리하는 전체 프로세스를 안내합니다(17페이지/인쇄 페이지 기준). 적용 대상: Microsoft ASP.NET Microsoft Visual SourceSafe 목차소스 코드 컨트롤을 사용해야 하는 이유 격리 모드와 비격리 모드 비교 SourceSafe 데이터베이스 설정 VSS에 ASP.NET 솔루션 추가 VSS로 파일 조작 파일 기록 추적 소프트웨어 버전에 레이블 사용 Visual Studio .NET에서 솔루션 가져오기 결론 일부 개발자들은 소스 코드 컨트롤을 반드시 사용해야 하지만 매우 번거로운 것으로 생각합니다. 하지만 소스 코드 컨트롤은 소프트웨어 개발 프로세스를 지원하는 안전한 업무 관례입니다. 이 문서에서는 실제로 Microsoft Visual SourceSafe를 소스 코드 컨트롤 메커니즘으로 유용하게 사용하는 단계별 방법을 보여 줍니다. 새 SourceSafe 데이터베이스를 만드는 방법, 파일을 체크 인하고 체크 아웃하는 방법, 레이블을 사용하여 릴리스를 만드는 방법을 볼 수 있습니다. 소스 코드 컨트롤을 사용해야 하는 이유단순하게 말하자면 VSS(Visual SourceSafe) 같은 SCM(Software Configuration Management) 제품은 프로젝트를 구성하는 문서의 중앙 라이브러리(데이터베이스)입니다. Visual SourceSafe에는 프로젝트 계획, 사양 설명서, 데이터베이스 개체, 소스 코드 등의 비트 스트림과 프로젝트의 기타 모든 항목을 저장할 수 있습니다. 최상의 방법은 소스 코드뿐만 아니라 모든 프로젝트 항목을 Visual SourceSafe 데이터베이스에 포함시키는 것입니다. 그러면 액세스 및 팀 구성원 간 공유가 용이해지며, 무엇보다도 버전 제어가 용이해집니다. 여느 라이브러리에서처럼 사용할 파일을 "체크 아웃"하는 기능이 필요합니다. 사용자는 파일을 체크 아웃한 후 편집할 수 있습니다. 일반적으로 한 번에 한 명의 사용자만 파일을 체크 아웃하여 편집할 수 있습니다. 언제나 한 명의 사용자만 파일을 체크 아웃할 수 있도록 하는 것이 가장 좋습니다. 간혹, 같은 파일을 여러 사용자가 체크 아웃할 수 있도록 할 것을 권장하는 Visual SourceSafe 사용 시나리오를 소개하는 백서도 있습니다. 이러한 백서에서는 나중에 모든 변경 사항을 함께 병합할 수 있도록 할 것을 권장합니다. 그러나 VSS에 기본 제공되는 도구를 사용하면 쉽게 병합할 수 있을 것 같지만 실제로는 여러 가지 단점이 있습니다. 항목을 다시 체크 인하는 데 시간이 더 오래 걸리고, 병합 프로세스에서 충돌을 수동으로 점검해야 할 경우가 발생할 수 있으며, 데이터베이스, Microsoft Word 문서 등의 이진 항목에는 대개 사용할 수 없습니다. 또한 업데이트된 항목과 업데이트한 사람 및 시간에 대한 정확한 기록을 반영하지 못하는 경우가 있습니다. VSS에서는 라이브러리 관리자가 액세스 제어를 정의할 수 있습니다. 사용자에게는 액세스 ID 및 암호, 그리고 액세스 권한이 주어집니다. 액세스 권한은 읽기 또는 읽기/쓰기 기능처럼 단순할 수도 있고, 기능 권한과 같이 복잡할 수도 있습니다. 예를 들어 파일을 삭제하는 기능도 하나의 기능 권한입니다. 모든 개별 파일(소스 코드, 프로젝트 계획, 요구 사항 등)에 대해 각각의 파일 주기 동안 어떠한 변화가 있었는지 파악하는 것은 매우 중요합니다. VSS는 파일을 만든 시간과 만든 사람, 해당 파일에 대한 각 수정, 해당 파일에 대한 메모 또는 주석, 그리고 해당 문서의 주기를 추적하는 데 도움이 되는 기타 정보 등 모든 작업 기록을 보관합니다. 위에서 언급한 VSS의 기능과 그 외 여러 기능을 통해 잘 관리된 구조적인 방식으로 개발, 빌드 및 유지 관리 프로세스를 효율적으로 관리할 수 있습니다. 이 외에도, VSS를 사용하여 소프트웨어 개발의 생산성을 높일 수 있는 이유에는 여러 가지가 있습니다. 격리 모드와 비격리 모드 비교팀 환경에서 웹 응용 프로그램을 개발할 경우 두 가지 모델 중에서 하나를 선택할 수 있습니다. 첫 번째 방식인 비격리 모델에서는 모든 개발자가 중앙 서버에서 모든 파일을 만들고 수정합니다. 비격리 개발 모델에서는 중앙 공유 컴퓨터에서 하나의 Microsoft IIS(Internet Information Services) 서버를 사용해야 하며, 응용 프로그램의 모든 파일이 해당 서버의 가상 디렉터리에 상주해야 합니다. 모든 개발자가 VSS에서 파일을 체크 아웃하며, 체크 아웃된 파일은 중앙 IIS 서버의 가상 디렉터리로 이동합니다. 두 번째 웹 개발 방식인 격리 모델에서는 각 개발자가 자신의 개발 컴퓨터에서 실행 중인 IIS 안에 가상 디렉터리를 만듭니다. 격리 방식에서는 각 개발자가 중앙 VSS 데이터베이스에서 로컬 컴퓨터로 파일을 가져오거나 체크 아웃합니다. 개발자는 로컬 컴퓨터에서 모든 내용을 편집, 디버그 및 테스트하고 모든 내용이 정상적으로 작동하면 파일을 중앙 위치로 다시 체크 인할 수 있습니다. 체크 인된 파일은 다른 개발자가 가져올 수 있습니다. 두 가지 개발 유형 모두 장단점이 있습니다. 각각의 장점과 단점을 살펴보겠습니다. 비격리 개발의 장점- 개발자의 로컬 컴퓨터에서 IIS를 실행하고 있지 않아도 됩니다.
- 모든 소스 코드가 서로 다른 개발자의 컴퓨터에 흩어져 있는 것이 아니라 한 위치에 모여 있습니다. 소스 코드가 저장된 컴퓨터에 문제가 발생할 경우 변경 내용을 SourceSafe에 체크 인하지 않았으면 해당 변경 내용을 잃을 수도 있습니다.
비격리 개발의 단점- 다른 개발자의 작업에 의도하지 않은 영향을 미치기 쉽습니다.
- 한 개발자가 디버깅을 위해 응용 프로그램을 실행하는 동안에는 프로세스가 잠기므로 다른 개발자들이 응용 프로그램을 디버그할 수 없습니다.
- 여러 개발자가 같은 파일을 작업하는 경우에는 마지막에 체크 인한 파일이 최종 파일이 됩니다.
- 소스 제어 기능이 제한되어 있습니다.
- 한 개발자가 일부 코드를 수정하여 해당 코드가 작동하지 않을 경우 다른 모든 개발자가 더 이상 프로젝트의 해당 부분을 실행할 수 없게 됩니다.
격리 개발의 장점- 다른 개발자를 부주의하게 방해하지 않고 응용 프로그램을 개발 및 디버그할 수 있습니다.
- 다른 개발자에게 영향을 미치지 않고 로컬에서 변경 내용을 테스트할 수 있습니다.
- 소스 코드 컨트롤에 대한 지원이 뛰어납니다.
- 개발자는 네트워크에 연결하지 않아도 프로젝트를 다른 컴퓨터로 이동하거나 가지고 다니면서 사용자를 표시할 수 있습니다.
격리 개발의 단점- 각 개발자가 자신의 로컬 컴퓨터에 IIS를 설정해야 웹 응용 프로그램을 개발할 수 있습니다.
- VSS 라이브러리가 백업 프로세스에 포함되는 경우 각 개발자는 파일이 백업되도록 퇴근하기 전에 모든 파일을 다시 체크 인해야 합니다.
선택할 모델격리 개발 모델을 사용할 것을 권장합니다. 물론 이를 위해서는 각 사용자의 컴퓨터에 IIS가 있어야 하므로 일부 조직에서는 제약이 따를 수 있습니다. 하지만 격리 모델은 가장 유연한 소스 코드 컨트롤을 위한 최상의 모델입니다. 격리 개발을 위한 Visual Studio .NET 설정격리 모델을 사용할 수 있도록 Microsoft Visual Studio .NET에서 올바른 옵션을 설정했는지 확인하십시오. Visual Studio .NET에서 도구 | 옵션 탭으로 이동하고 Microsoft FrontPage Extensions 옵션이 아닌 파일 공유를 클릭합니다. FrontPage Extensions 옵션은 모든 파일이 중앙 서버에 위치한 비격리 방식을 사용할 경우 선택하는 옵션입니다. SourceSafe 데이터베이스 설정지금까지는 Visual SourceSafe의 개요 및 사용 이점에 대해 알아보았습니다. 이제 Visual SourceSafe를 시작하는 방법을 살펴보겠습니다. 첫 번째 단계는 중앙 VSS 데이터베이스의 위치를 찾는 것입니다. 이 데이터베이스는 엄밀한 의미에서 데이터베이스라기 보다는 하드 드라이브의 폴더입니다. 이 폴더는 모든 개발자가 찾을 수 있는 네트워크 공유에 배치되어야 합니다. 개발자가 한 명뿐일 경우에는 개발자의 로컬 하드 드라이브에 배치할 수도 있습니다. VSS 관리 도구를 아직 설치하지 않았으면 VSS CD에서 설치합니다. 사용자 지정 설치를 통해 이 옵션을 선택해야 합니다. CDD에서는 일반적으로 ACMBoot.exe를 실행하여 관리 도구를 설치합니다. 사용자 지정 설치 옵션에서 Administrative Programs 및 Create SourceSafe Database 옵션을 선택해야 합니다. 관리 프로그램을 설치한 후에는 시작 메뉴로 이동하여 프로그램 | Microsoft Visual SourceSafe | Visual SourceSafe 6.0 Admin을 클릭합니다. 새로 시작되는 인터페이스에서 Tools | Create Database...를 클릭합니다 . 그러면 그림 1과 같은 대화 상자가 표시됩니다. 이 새 SourceSafe 데이터베이스를 만들 위치를 "D:\MyVSSDB" 또는 \\SharedDrive\MyVSSDB와 같이 입력합니다.
그림 1. 모든 SourceSafe 파일에 사용할 공유 폴더의 위치를 지정합니다. 데이터베이스를 만들고 나면 그림 2와 같은 대화 상자가 표시됩니다. 이 대화 상자는 이 SourceSafe 데이터베이스를 만들 때 만들어지는 Admin 사용자에 관련 암호가 없다는 것을 나타내는 경고일 뿐입니다. Admin 사용자를 클릭하고 메뉴에서 Users | Change Password...를 클릭하여 Admin 사용자에 암호를 할당하십시오.
그림 2. Admin 사용자에 암호를 할당하여 관리 도구를 보안합니다. 데이터베이스를 만들고 나면 Visual SourceSafe Administrator 도구(그림 3)가 나타납니다. 이 도구에서는 한 번에 하나의 VSS 데이터베이스에만 연결할 수 있습니다. 이 데이터베이스에서 파일을 체크 아웃하고 체크 인할 수 있도록 할 모든 사용자를 이 데이터베이스 안에서 만들어야 합니다. 만드는 각 데이터베이스마다 해당 사용자를 추가해야 합니다. 이 기능은 설정된 사용자만 이 데이터베이스를 사용할 수 있도록 하므로 유용합니다. 그러나 모든 사용자에게 액세스를 허용할 VSS 데이터베이스가 여러 개 있는 경우 각 데이터베이스에서 각 사용자를 개별적으로 설정해야 합니다.
그림 3. Administrator 도구에서는 새 사용자를 만들고, 데이터베이스를 만들고, 데이터베이스를 잠그며, SourceSafe에 대한 기타 시스템 관리 기능을 수행할 수 있습니다. 이제 이 데이터베이스에 자기 자신을 새 사용자로 추가해야 합니다. SourceSafe에서 도메인 로그온 ID(도메인 이름 제외)를 사용자 이름으로 사용하고 있는지 확인하십시오. SourceSafe는 아무런 메시지를 표시하지 않고 도메인 로그온 ID를 사용하여 로그온을 시도합니다. 이 VSS 데이터베이스에 암호를 할당한 경우에는 해당 암호가 도메인 암호와 일치하는지도 확인하십시오. 이 새 데이터베이스의 위치를 기억해야 합니다. 데이터베이스에 사용자를 도메인 이름과 함께 추가한 후에는 사용자가 VSS 클라이언트 유틸리티를 처음 설정할 때 데이터베이스를 찾을 수 있도록 모든 사용자에게 데이터베이스 위치를 알려야 합니다. VSS에 ASP.NET 솔루션 추가새로 만든 VSS 데이터베이스에 프로젝트 및 프로젝트 항목을 추가할 수 있습니다. 두 가지 방법을 통해 이 VSS 데이터베이스를 조작할 수 있습니다. 즉, "다른 프로젝트 항목"을 데이터베이스에 추가하는 데 유용한 VSS Explorer 도구를 사용하거나 Visual Studio .NET 내에서 VSS를 사용할 수 있습니다. 사실 VSS에 새 프로젝트를 추가하는 경우에는 Visual Studio .NET을 사용하여 추가하는 것이 좋습니다. Visual Studio .NET 인터페이스를 사용하면 .SLN 파일에 몇 가지 바인딩 정보가 추가되므로 개발자가 VSS에서 솔루션을 가져올 때 VSS에 자동으로 연결될 수 있습니다. 참고 이 문서의 예제에서는 ASP.NET 사이트에서 다운로드할 수 있는 Microsoft ASP.NET Portal Starter Kit을 사용하여 파일을 체크 인하고 체크 아웃했습니다. 원하는 다른 프로젝트를 사용할 수도 있습니다. ASP.NET Portal Starter Kit을 다운로드하여 설치했다고 가정하고 VSS에 이 솔루션을 추가하는 방법을 설명하겠습니다. Visual Studio .NET에서 솔루션을 열고 그림 4와 같이 메뉴에서 File | Source Control | Add Solution to Source Control...을 선택합니다.
그림 4. Visual Studio .NET에서 기본 제공하는 메뉴를 사용하여 솔루션을 SourceSafe에 추가합니다. 이 메뉴 항목을 선택한 후에는 그림 5와 같은 대화 상자가 표시될 것입니다. 격리 개발 모드를 사용하는 경우 이 대화 상자는 FrontPage Server Extensions 대신 일반 파일 URL을 사용하여 모든 파일을 참조한다는 의미일 뿐입니다. Don't' show this dialog box again (Always allow addition of Web projects using File Share access to source control) 확인란을 클릭하고 Continue를 클릭하십시오.
그림 5. FrontPage Server Extensions에서 파일 공유 액세스 사용으로 전환 이제 VSS 데이터베이스를 설정할 때 만든 로그온 ID와 암호(그림 6 참고)를 입력할 차례입니다. 설정한 ID(예: JohnD)와 암호를 입력합니다. 그런 다음 Browse...를 클릭하여 VSS 데이터베이스를 만든 특정 폴더를 찾습니다. 작업을 마쳤으면 OK를 클릭합니다.
그림 6. VSS 로그온 데이터베이스에서 만들 프로젝트 이름을 입력하는 대화 상자가 나타납니다. 첫 번째 대화 상자인 "Add to SourceSafe Project"(그림 7)는 솔루션 파일이 있는 Visual Studio .NET 프로젝트를 나타냅니다. 그림의 "ASP.NET Portal Starter Kit (VBVS)"와 같이 이 프로젝트의 이름을 입력합니다.
그림 7. 본인이나 다른 개발자가 나중에 쉽게 찾을 수 있도록 프로젝트에 이름을 지정합니다. 솔루션에 있는 모든 개별 Visual Studio .NET 프로젝트를 입력하는 대화 상자가 나타납니다. 그림 8과 같이 VSS는 각 Visual Studio .NET 프로젝트 이름을 "Add to SourceSafe Project" 대화 상자의 입력란에 자동으로 추가합니다. 이 경우 두 번째로 입력되는 내용은 PortalVBVS입니다. "ASP.NET Portal Starter Kit (VBVS)" 폴더를 클릭하여 이 프로젝트를 이 솔루션 아래에 배치해야 합니다.
그림 8. 각각의 새 프로젝트를 VSS에 개별적으로 추가합니다. 솔루션 파일이 프로젝트와 동일한 폴더에 있다는 경고 메시지가 표시됩니다. 의도적으로 같은 폴더에 배치한 것이므로 확인란을 클릭하여 계속 진행합니다. 그림 9와 같은 대화 상자가 나타날 수도 있고 나타나지 않을 수도 있습니다. 이 대화 상자가 나타나면 확인란을 선택하고 OK를 클릭합니다. 이 경우 다른 항목은 프로젝트 파일의 일부분이 아닌 폴더에 있기 때문에 나중에 VSS Explorer 도구를 통해 수동으로 추가해야 합니다. 폴더 안에 있지만 프로젝트 파일로 새로 지정된 문서 또는 .SQL 파일이 이러한 항목에 해당할 수도 있습니다.
그림 9. 프로젝트 파일에서 참조하지 않는 추가 파일이나 폴더가 있으면 VSS에서 이를 사용자에게 알립니다. 솔루션과 프로젝트를 SourceSafe 제어 아래에 배치하고 나면 Visual Studio .NET이 그림 10과 같이 특수 아이콘을 사용하여 파일이 잠겼는지 아니면 체크 인되었는지 표시합니다. 소스 코드 컨트롤 아래의 각 파일 옆에는 자물쇠 아이콘이 표시됩니다. 자신이 체크 아웃한 파일 옆에는 확인 표시가 나타나고, 다른 사용자가 체크 아웃한 파일에는 원형 아이콘이 나타납니다.
그림 10. Visual Studio .NET은 소스 코드 컨트롤 아래에 각 파일의 상태를 표시합니다. VSS 데이터베이스 내의 전체 프로젝트를 보려면 운영 체제 메뉴에서 시작 | 모든 프로그램| Microsoft Visual SourceSafe| Microsoft Visual SourceSafe 6.0을 선택하십시오. 다시 로그온해야 할 수도 있습니다. 로그온 이름과 암호를 입력하십시오. VSS Explorer(그림 11 참고)를 처음 실행하는 경우 데이터베이스를 만든 폴더를 탐색하여 VSS 데이터베이스도 찾아야 합니다.
그림 11. Visual SourceSafe Explorer에서는 전체 프로젝트 및 소스 코드 컨트롤 아래에 배치된 모든 파일을 볼 수 있습니다. VSS로 파일 조작프로젝트 파일을 VSS 데이터베이스에 배치하고 나면 프로젝트의 모든 파일이 디스크에서 읽기 전용으로 설정됩니다. 체크 아웃되지 않은 파일로 솔루션을 실행할 수도 있습니다. 그러나 Visual Studio .NET 내에서 파일을 작업하려면 체크 아웃해야 합니다. 현재의 작업과 비교하면 한 단계가 추가된 것이지만 대신 이전 버전으로 다시 돌아갈 수 있으며 다른 개발자가 수정 중인 파일을 사용자가 동시에 수정할 수 없도록 합니다. 파일 체크 아웃파일 작업을 위해 체크 아웃해야 할 때는 Solution Explorer 창에서 해당 파일을 마우스 오른쪽 단추로 클릭하고 상황에 맞는 메뉴에서 Check Out...을 클릭하기만 하면 됩니다. 예를 들어 Portal Starter Kit에서 Default.aspx 파일을 클릭하고 마우스 오른쪽 단추를 클릭한 다음 Check Out...을 클릭합니다. 그림 12와 같은 대화 상자가 나타납니다. Check Out 단추를 클릭하면 .ASPX 파일뿐만 아니라 .ASPX.resx 및 .ASPX.VB 파일도 체크 아웃됩니다. 이제 해당 파일을 작업할 수 있으며 다른 사용자에게 파일이 체크 아웃된 것으로 표시됩니다.
그림 12. Check Out 대화 상자에서는 프로젝트 파일을 하나 또는 여러 개 가져와서 하드 드라이브에 쓸 수 있는 상태로 설정할 수 있습니다. 파일 체크 인체크 아웃한 파일에서 원하는 내용을 모두 수정한 후에는 SourceSafe에 다시 체크 인해야 합니다. 파일을 체크 인할 경우 두 가지 사항을 염두에 두어야 합니다. 첫째, 프로젝트의 페이지 또는 클래스에서 변경한 내용이 컴파일되는지 확인해야 합니다. 그렇지 않으면 SourceSafe 데이터베이스에서 최신 변경 사항을 가져오는 다른 개발자의 프로젝트에서 오류가 발생하는 곤란한 상황이 발생합니다. 둘째, 매일 일과가 끝나면 파일을 모두 체크 인해야 합니다. 그러면 파일이 단지 하드 드라이브에 저장되는 것이 아니라 다른 위치에 백업됩니다. 따라서 하드 드라이브에 문제가 발생할 경우에도 변경한 내용을 모두 보존할 수 있습니다. 일과 후에도 소스 코드가 아직 컴파일되지 않은 경우에는 문제가 되는 코드에 주석을 달고 체크 인하면 됩니다. 최신 버전 가져오기팀 환경에서 작업하는 경우 프로젝트 내에서 다른 파일을 수정하는 다른 개발자도 있습니다. 특정 시점에서는 VSS 데이터베이스에 있는 모든 최신 변경 사항을 프로젝트와 동기화할 수 있습니다. 그러기 위해서는 Visual Studio .NET Solution Explorer 창에서 프로젝트를 클릭하고 마우스 오른쪽 단추를 클릭한 다음 Get Latest Version (Recursive)를 클릭합니다. 그러면 VSS 데이터베이스로 이동하여 변경된 모든 파일을 검색하고 해당 파일을 프로젝트로 가져옵니다. 이제 로컬 컴퓨터에서 프로젝트를 실행하고 나면 다른 개발자가 변경한 내용을 모두 볼 수 있습니다. 파일 기록 추적특정 시점에서 개발 팀은 "빌드", "버전" 또는 "릴리스"를 만들 수도 있습니다. VSS는 버전 번호를 사용하여 파일 및 프로젝트에 대한 모든 변경 사항을 추적합니다. 따라서 파일 또는 프로젝트의 모든 버전을 검색할 수 있습니다. VSS는 내부 버전 번호, 날짜 및 사용자 정의 레이블의 세 가지 항목을 기준으로 이전 버전을 추적합니다. 버전을 자체적으로 지정하는 경우에는 VSS에서 할당한 내부 버전 번호가 아니라 사용자 정의 레이블을 사용합니다. 버전 번호VSS는 체크 인된 각 파일에 대해 내부 버전 번호를 유지합니다. 파일을 체크 아웃하고 변경한 다음 VSS에 다시 체크 인할 때마다 해당 파일 버전에 대한 새 번호가 만들어집니다. VSS에서 History 대화 상자를 사용하여 파일의 전체 기록을 볼 수 있습니다. History 대화 상자는 Visual Studio .NET 또는 VSS Explorer 도구를 통해 볼 수 있습니다. Visual Studio .NET에서 기록을 볼 파일(예: default.aspx)을 클릭한 다음 Visual Studio .NET 메뉴 시스템에서 File | Source Code Control | History를 클릭합니다. 그러면 그림 13과 같은 대화 상자가 나타납니다. default.aspx 파일을 아직 변경하지 않은 경우에는 첫 번째 버전 이외의 다른 버전이 없습니다. VSS Explorer 도구를 사용하는 경우 Explorer에서 특정 파일을 찾아서 마우스 오른쪽 단추로 클릭한 다음 Show History... 메뉴 항목을 클릭하여 동일한 대화 상자를 표시합니다.
그림 13. VSS Explorer에 파일의 전체 기록이 표시됩니다. 참고 내부 VSS 버전 번호는 단순히 참조용이며 빌드 또는 버전 번호와 직접적인 관련이 없습니다. 빌드 또는 버전 번호에는 레이블(아래 참고)을 사용합니다. 소프트웨어 버전에 레이블 사용SourceSafe에서 파일에 할당하는 내부 버전 번호를 사용하는 대신 소프트웨어 릴리스를 정의하는 자신만의 코드 집합용 "레이블"을 만들 수도 있습니다. 릴리스는 첫 번째 베타 버전, 제품의 첫 번째 버전, 증분 릴리스, 제품의 두 번째 또는 세 번째 릴리스일 수 있습니다. 각 파일에는 자체 내부 버전 번호가 지정되며, 파일 수정 빈도에 따라 이 번호는 전체 프로젝트에서 전혀 일치하지 않게 됩니다. 따라서 내부 버전 번호 대신 자신만의 레이블을 전체 프로젝트에 적용하여 이 레이블을 만든 특정 시점에서 체크 인된 모든 파일을 식별할 수 있습니다. 레이블(최대 31자)을 만들 때 "1.0," "2.01b," "Final Beta" 또는 "Approved for QA" 같은 텍스트를 사용할 수 있습니다. 레이블을 적용한 후에는 기록 대화 상자에서 이 레이블과 연결된 모든 파일을 검색할 수 있습니다. 개별 파일에 레이블을 할당할 수도 있지만 대개 프로젝트 수준에서 레이블을 적용합니다. 프로젝트에 설명 문자열이 있는 레이블을 할당하면 해당 프로젝트의 모든 파일과 하위 프로젝트가 그 레이블을 사용합니다. 개발 주기의 어느 시점에서나 프로젝트에 레이블을 할당할 수 있습니다. 예를 들어 제품의 각 "릴리스"마다 알파, 베타 또는 최종 생산 코드에 관계없이 해당 시점에서 모든 프로젝트 항목에 레이블을 할당할 수 있습니다. 개발을 진행하다 베타 1.0의 소스 코드가 필요할 경우 그냥 가져오면 됩니다. 원하면 원본 파일에 아무런 영향을 미치지 않고 레이블의 이름을 바꿀 수 있습니다. 레이블을 만들려면 VSS Explorer 도구에서 레이블을 할당할 Project 폴더를 클릭하십시오. 메뉴에서 File | Label...을 클릭하면 그림 14와 같은 대화 상자가 나타납니다. 설명이 포함된 레이블 이름과 이 레이블의 사용 용도를 알려 주는 주석을 입력하고 OK를 클릭하여 레이블을 이 프로젝트와 이 프로젝트 아래의 모든 파일 및 하위 폴더에 적용합니다. 그림 14. VSS Explorer 도구를 사용하여 레이블 만들기 VSS Explorer에서 프로젝트를 다시 선택하고 마우스 오른쪽 단추로 클릭한 다음 Show History... 메뉴 항목을 클릭하여 History 대화 상자를 표시하면 그림 15와 같이 레이블이 표시됩니다.
그림 15. VSS Explorer의 History 대화 상자에서 적용한 여러 가지 레이블을 볼 수 있습니다. 기존 레이블에 파일 추가버전에 레이블을 할당했는데 나중에 레이블이 할당된 버전에 포함되었어야 할 파일을 빠뜨린 것을 발견하는 경우도 있을 수 있습니다. 파일을 레이블의 일부분으로 추가하려면 파일을 프로젝트에 추가하기만 하면 됩니다. VSS Explorer에서 해당 파일을 클릭한 다음 File | Label...을 클릭하고 프로젝트에 할당한 것과 동일한 레이블을 할당합니다. 레이블을 기준으로 파일을 가져올 경우 레이블 이름이 동일하기 때문에 이 파일도 함께 가져옵니다. 레이블 이름을 정확히 입력했는지 확인하십시오. 레이블에 할당된 모든 파일 가져오기 특정 레이블이 할당된 파일을 모두 가져오려면 해당 파일에 대해 "가져오기(get)" 작업을 수행하면 됩니다. 즉, 특정 레이블 아래의 파일을 체크 아웃할 수는 없지만 가져올 수는 있습니다. 그러기 위해서는 VSS Explorer에서 내용을 가져올 프로젝트를 마우스 오른쪽 단추로 클릭하고 Show History...를 클릭합니다. Project History Options 대화 상자(그림 16 참고)가 나타나면 Labels Only 확인란을 선택하고 OK를 클릭합니다.
그림 16. 프로젝트의 레이블 가져오기 선택한 프로젝트에 할당한 모든 레이블이 표시됩니다. 가져올 레이블을 클릭한 다음 화면(그림 15 참고) 오른쪽에서 Get을 클릭합니다. 그러면 이 레이블이 있는 모든 파일이 이 프로젝트에 할당된 작업 디렉터리로 복사됩니다. 이미 언급한 대로 레이블이 할당된 릴리스에서는 파일을 체크 아웃할 수 없습니다. 따라서 릴리스된 파일 집합은 아무도 무단으로 변경할 수 없습니다. Visual Studio .NET에서 솔루션 가져오기사용자가 빌드 중인 응용 프로그램에 대해 프로젝트 책임자가 초기 솔루션을 만들면 다른 개발자가 이 솔루션을 가져와서 각자의 컴퓨터에 설정할 수 있도록 해야 합니다. 이때 각 개발자가 자신의 컴퓨터에 가상 디렉터리를 다시 만들고 모든 파일을 올바른 위치로 가져왔는지 확인하게 할 필요는 없습니다. 다행히도 VSS 및 Visual Studio .NET에서는 이를 자동으로 처리합니다. VSS와 통합된 Visual Studio .NET은 하드 드라이브에 적절한 폴더를 자동으로 만들고, 새 가상 디렉터리를 만들며, VSS에서 새 폴더로 파일을 자동 복사합니다. 항상 VSS가 아닌 Visual Studio .NET을 통해 이 프로세스를 수행해야 합니다. 그렇지 않으면 IIS를 수동으로 구성하고 VSS 데이터베이스에 대한 참조를 직접 설정해야 합니다. 다음 단계를 수행하려면 다른 컴퓨터를 사용하거나 앞에서 만든 가상 디렉터리를 지워야 합니다. Visual Studio .NET의 새 인스턴스를 엽니다. File | Source Control | Open from Source Control...을 클릭하면 그림 17과 같은 대화 상자가 나타납니다. 창에서 PortalVBVS 프로젝트를 클릭합니다. Create a new project in the Folder 입력란에 다른 폴더 이름을 입력하고 OK를 클릭합니다.
그림 17. 새 폴더로 SourceSafe 프로젝트 가져오기 드라이브에 없는 폴더를 선택하면 폴더를 만들라는 메시지가 표시됩니다. 이 예제의 경우에는 폴더가 드라이버에 없어야 정상입니다. Yes All을 클릭하여 이 프로젝트에 필요한 모든 폴더를 만듭니다. 그림 18과 같이 이 프로젝트를 배치할 가상 디렉터리를 입력하는 대화 상자가 나타납니다.
그림 18. 프로젝트에 가상 디렉터리 할당 SourceSafe 내의 프로젝트 및 솔루션 레이아웃 방법에 따라 열려는 솔루션 파일을 입력해야 할 수도 있습니다. 그럴 경우 대화 상자에서 .SLN 파일을 선택합니다. Portal 솔루션에서는 .SLN 파일이 분리된 자체 폴더 안에 있으므로 SourceSafe에서 아무런 대화 상자도 표시되지 않습니다. 다음으로는 이 가상 디렉터리를 만들 IIS의 위치를 입력하는 대화 상자가 나타납니다. 웹 서버 이름과 가상 디렉터리 이름을 입력하고 OK를 클릭합니다.
그림 19. 웹 서버에 프로젝트 할당 이제 Visual Studio .NET에서 이 프로젝트의 모든 파일을 가져오기 시작합니다. 이 문서의 예제에서는 D:\PortalVBVS를 사용했습니다. 즉, 솔루션이 이 폴더에 저장된다는 의미입니다. 이 프로젝트의 다른 모든 파일은 기본 웹 사이트가 가리키는 폴더에 배치됩니다. 일반적으로 이 폴더는 c:\inetpub\wwwroot입니다. 이로써 프로젝트가 다른 개발자의 컴퓨터에 설정되었으며 사용 준비가 끝났습니다. 사이트의 시작 페이지를 선택하고 F5를 누르기만 하면 응용 프로그램이 실행됩니다. 이제 파일을 체크 아웃하여 작업하고 다시 체크 인할 수 있습니다. 이 모든 작업이 Visual Studio .NET 내에서 가능합니다. 결론모든 개발자는 일상적인 작업에서 Visual SourceSafe를 사용해야 하며, 모든 개발 관리자는 팀에서 Visual SourceSafe를 사용하도록 해야 합니다. 개발자가 한 명뿐일 경우에도 이 도구를 효율적으로 사용하면 소스 코드를 다른 컴퓨터에 백업하고 소스 코드의 이전 버전으로 돌아갈 수 있습니다. VSS를 만들고 사용하는 것은 간단하고 쉽습니다. 사용 방법을 조금만 배우면 됩니다. 소스 코드 컨트롤이 뛰어나면 개발 프로세스가 향상되고 소프트웨어 구성 관리의 다양한 이점을 모두 활용할 수 있습니다. BIOPaul D. Sheriff는 SDLC 문서 및 아키텍처 프레임워크를 비롯한 .NET 컨설팅, 제품 및 서비스를 제공하는 PDSA, Inc.의 사장입니다(http://www.pdsa.com/ 을 참고하십시오). 또한 남부 캘리포니아의 Microsoft 지역 책임자입니다. .NET 저서로는 ASP.NET Developer's Jumpstart(Addison-Wesley: 영문)와 PDSA 웹 사이트에서 구할 수 있는 여러 eBook이 있습니다. 전자 메일 주소는 PSheriff@pdsa.com입니다. Michael Krasowski는 PDSA, Inc의 개발부 부사장입니다. 이전에는 The Boeing Company, Long Beach Division에서 선임 IT 관리자 직책을 역임했습니다. IT 분야에서 27년이 넘는 경험을 보유하고 있으며 캘리포니아 대학의 Irvine 사회 교육 과정에서 .NET에 대해 강의하고 있습니다. 전자 메일 주소는 Michaelk@pdsa.com입니다.
Posted by tornado
Uploading Files Using the File Field Control By Bill Evjen Reuters Feburary 2004 Applies to: Microsoft® ASP.NET Summary: Learn how to use the Microsoft ASP.NET File Field control to allow your end-users to upload one or more files to your server. (16 printed pages) ContentsIntroduction Looking Closely at the File Field Control Working Around File Size Limitations Controlling Uploaded File Types Uploading Multiple Files at the Same Time Conclusion Related Books IntroductionMicrosoft® ASP.NET Web Forms are all about communicating with an end-user to abstract what information you need to from them depending on the service administered. In many cases with Web Forms, it is usually simply textual data that is collected and stored. However, there are many cases where you are going to need more than simple textual data; you may want your users to upload files to the server. For instance, your application can have a document library that is shared with all users. There are plenty of examples, but invariably you will need this ability to upload any type of document for storage, or to have your application take action upon the document once it is uploaded. In any case, ASP.NET provides you with a simple ability to upload documents to the server with just a little work on your part. This article describes how to use the File Field control for uploading documents to your Web server. Looking Closely at the File Field ControlThe File Field control uses the HtmlInputFile class and is a cool control simply because it does things that were difficult to do before ASP.NET (that is, without buying a third-party component to do it for you). The File Field control uploads files to the server from the client's machine. It is important to note that this control is called the File Field control in all versions of Microsoft Visual Studio® .NET, while in the ASP.NET Web Matrix, this control is called the FileUpload control. In both cases, you will find the control in the HTML section of the Toolbox. The File Field control allows access to program the HTML <input type="file"> tag. This tag is used to work with file data within an HTML form. In the past when using classic ASP (ASP 3.0 or earlier), many programmers worked with third-party components to upload files from the client to the server. Now, with .NET and this new control, uploading is taken care of for you—and it couldn't be simpler. Listing 1 shows you how to use the File Field control to upload files to the server. Note The sample code is provided in both Microsoft Visual Basic® (VB) and C#. Listing 1. Uploading files to the server using the File Field control VB <%@ Page Language="VB" %><script runat="server"> Sub SubmitButton_Click(Source As Object, e As EventArgs) If Not (File1.PostedFile Is Nothing) Then Try File1.PostedFile.SaveAs("C:\Uploads\uploadedfile.txt") Span1.InnerHtml = "Upload Successful!" Catch ex As Exception Span1.InnerHtml = "Error saving file <b>C:\\" & _ File1.Value & "</b><br>" & ex.ToString() End Try End If End Sub</script><html><head></head><body> <form runat="server" enctype="multipart/form-data"> Select a file to upload:<br /> <input type="file" id="File1" runat="Server"> <p> <input type="submit" id="Submit1" runat="Server" value="Upload File" OnServerClick="SubmitButton_Click"> <p> <span id="Span1" runat="Server" /> </form></body></html> C# <%@ Page Language="C#" %><script runat="server"> void SubmitButton_Click(Object sender, EventArgs e) { if (File1.PostedFile != null) { try { File1.PostedFile.SaveAs("C:\\Uploads\\uploadedfile.txt"); Span1.InnerHtml = "Upload Successful!"; } catch (Exception ex) { Span1.InnerHtml = "Error saving file <b>C:\\" + File1.Value + "</b><br>" + ex.ToString(); } } } </script><html><head></head><body> <form runat="server" enctype="multipart/form-data"> Select a file to upload:<br /> <input type="file" id="File1" runat="Server"> <p> <input type="submit" id="Submit1" runat="Server" value="Upload File" OnServerClick="SubmitButton_Click"> <p> <span id="Span1" runat="Server" /> </form></body></html> When the page from Listing 1 is run, you can select a file and upload it to the server by clicking the Upload File button on the page. This is a great feature that ASP 3.0 programmers always wished for. Now it is here in .NET! With only a few lines of code, it is easy to upload any type of files to the server. There are some important items we should go over for this example so you understand all the needed pieces to make this work. First, for the example in Listing 1 to work, you have to make the destination folder on the server writeable for the account used by ASP.NET so the file can be saved to the specified folder. If you think your ASP.NET account is not enabled to write to the folder you want, simply open up Microsoft Windows® Explorer and navigate to the folder to which you want to add this permission. Right-click on the folder (in this case, the Uploads folder), and then select Properties. In the Properties dialog box, click on the Security tab and make sure the ASP.NET Machine Account is included in the list and has the proper permissions to write to disk (see Figure 1). Figure 1. Looking at the Security tab of the Uploads folder If you don't see the ASP.NET Machine Account under the Security tab, you can add it by clicking the Add button and entering ASPNET (without the period) in the text area, as illustrated in Figure 2. Figure 2. Adding the ASP.NET Machine Account to the folder security definition Click OK to add the ASP.NET Machine Account to the list. From here, make sure you give this account the proper permissions; then click OK and you are ready to go. Looking at the code from Listing 1, you might notice some really important things right away. First, the <form> tag has been altered by the addition of another attribute. To upload files to a server, your <form> tag must have the attribute enctype="multipart/form-data" . Without this attribute, the Web form cannot upload the file. The Submit button on the page causes an OnServerClick event to occur. This event uploads the file and then displays a message telling you if the upload was successful. If it was unsuccessful, the page displays an error message describing why the upload failed. By using the <input type="file"> tag, the browser automatically places a Browse button next to the text field on the ASP.NET page. You don't have to program anything else for this to occur. When the end user clicks the Browse button, he can navigate through the local file system to find the file to be uploaded to the server. This is shown in Figure 3. Clicking Open will place that filename and the file's path within the text field. Figure 3. Choosing a file Working Around File Size LimitationsYou may not realize it, but there is a limit to the size of a file that can be uploaded using this technique. By default, the maximum size of a file to be uploaded to the server using the File Field control is around 4MB. You cannot upload anything that is larger than this limit. One of the great things about .NET, however, is that it usually provides a way around limitations. You can usually change the default settings that are in place. To change this size limit, you make some changes in either the machine.config or web.config file. In the machine.config file, find a node called <httpRuntime> that looks like the following: <httpRuntime executionTimeout="90" maxRequestLength="4096" useFullyQualifiedRedirectUrl="false" minFreeThreads="8" minLocalRequestFreeThreads="4" appRequestQueueLimit="100"/> A lot is going on in this single node, but the setting that takes care of the size of the files to be uploaded is the maxRequestLength attribute. By default, this is set to 4096 kilobytes (KB). Simply change this value to increase the size of the files that you can upload to the server. If you want to allow 10 megabyte (MB) files to be uploaded to the server, set the maxRequestLength value to 11264 , meaning that the application allows files that are up to 11000 KB to be uploaded to the server. Making this change in the machine.config file applies this setting to all the applications that are on the server. If you want to apply this to only the application you are working with, then apply this node to the web.config file of your application, overriding any setting that is in the machine.config file. Make sure this node resides between the <system.web> nodes in the configuration file. Another setting involved in the size limitation of files to be uploaded is the value given to the executionTimeout attribute in the <httpRuntime> node. The value given the executionTimeout attribute is the number of seconds the upload is allowed to occur before being shut down by ASP.NET. If you are going to allow large files to be uploaded to the server, you are also going to want to increase this value along with the maxRequestLength value. One negative with increasing the size of a file that can be uploaded is that there are hackers out there who attack servers by throwing a large number of requests at them. To guard against this, you can actually decrease the size of the files that are allowed to be uploaded; otherwise, you may find hundreds or even thousands of 10 MB requests hitting your server. Controlling Uploaded File Types There are a several methods you can use to control the types of files that are uploaded to the server. Unfortunately, there is no bullet-proof method to protect you from someone uploading files that would be considered malicious. You can take a few steps, however, to make this process of allowing end users to upload files a little more manageable. One nice method you can employ is to use the ASP.NET validation controls that are provided for free with ASP.NET. These controls enable you to do a regular-expression check upon the file that is being uploaded to see if the extension of the file is one you permit to be uploaded. This is ideal for browsers that allow client-side use of the validation controls because it forces the checking to be done on the client; the file is not uploaded to the server if the signature isn't one you allow. Listing 2 shows you an example of using validation controls to accomplish this task. Note The use of validation controls is not explained here. Take a look at Validating ASP.NET Server Controls for a complete explanation of validation controls and how to use them in your ASP.NET pages. Listing 2: Using validation controls to restrict the types of files uploaded to the server VB <%@ Page Language="VB" %><script runat="server"> Sub SubmitButton_Click(Source As Object, e As EventArgs) If Not (File1.PostedFile Is Nothing) Then Try File1.PostedFile.SaveAs("C:\Uploads\uploadedfile.txt") Span1.InnerHtml = "Upload Successful!" Catch ex As Exception Span1.InnerHtml = "Error saving file <b>C:\\" & _ File1.Value & "</b><br>" & ex.ToString() End Try End If End Sub</script><html><head></head><body> <form enctype="multipart/form-data" runat="server"> <input id="File1" type="file" runat="Server" /> <p> <input id="Submit1" type="submit" value="Upload File" runat="Server" onserverclick="SubmitButton_Click" /> </p> <span id="Span1" runat="Server" /> <p> <asp:RegularExpressionValidator id="RegularExpressionValidator1" runat="server" ErrorMessage="Only mp3, m3u or mpeg files are allowed!" ValidationExpression="^(([a-zA- Z]:)|(\\{2}\w+)\$?)(\\(\w[\w ].*))+(.mp3|.MP3|.mpeg|.MPEG|.m3u|.M3U)$" ControlToValidate="File1"> </asp:RegularExpressionValidator> <br /> <asp:RequiredFieldValidator id="RequiredFieldValidator1" runat="server" ErrorMessage="This is a required field!" ControlToValidate="File1"> </asp:RequiredFieldValidator> </p> </form></body></html> C# <%@ Page Language="C#" %><script runat="server"> void SubmitButton_Click(Object sender, EventArgs e) { if (File1.PostedFile != null) { try { File1.PostedFile.SaveAs("C:\\Uploads\\uploadedfile.txt"); Span1.InnerHtml = "Upload Successful!"; } catch (Exception ex) { Span1.InnerHtml = "Error saving file <b>C:\\" + File1.Value + "</b><br>" + ex.ToString(); } } }</script><html><head></head><body> <form enctype="multipart/form-data" runat="server"> <input id="File1" type="file" runat="Server" /> <p> <input id="Submit1" type="submit" value="Upload File" runat="Server" onserverclick="SubmitButton_Click" /> </p> <span id="Span1" runat="Server" /> <p> <asp:RegularExpressionValidator id="RegularExpressionValidator1" runat="server" ErrorMessage="Only mp3, m3u or mpeg files are allowed!" ValidationExpression="^(([a-zA- Z]:)|(\\{2}\w+)\$?)(\\(\w[\w ].*))+(.mp3|.MP3|.mpeg|.MPEG|.m3u|.M3U)$" ControlToValidate="File1"> </asp:RegularExpressionValidator> <br /> <asp:RequiredFieldValidator id="RequiredFieldValidator1" runat="server" ErrorMessage="This is a required field!" ControlToValidate="File1"> </asp:RequiredFieldValidator> </p> </form></body></html> This simple ASP.NET page uses validation controls so that the end user can only upload .mp3 , .mpeg , or .m3u files to the server. If the file type is not one these three choices, a Validation control throws an exception onto the screen. This is shown in Figure 4. Figure 4. Validating the file type using validation controls Using validation controls is not a foolproof way of controlling the files that are uploaded to the server. It wouldn't be too hard for someone to change the file extension of a file so it would be accepted and uploaded to the server, thereby bypassing this simple security model. Uploading Multiple Files at the Same TimeSo far, you have seen some good examples of how to upload a file to the server without much hassle. Now let's take a look at how to upload multiple files to the server from a single page. No built-in capabilities in the Microsoft .NET Framework enable you to upload multiple files from a single ASP.NET page. With a little work, however, you can easily accomplish this task. One trick is to import the System.IO class into your ASP.NET page, and to then use the HttpFileCollection class to capture all the files that are sent in with the Request object. This approach enables you to upload as many files as you want from a single page. For this example, you can build an ASP.NET page that has four file-input boxes and one Submit button. After the user clicks the Submit button and the files are posted to the server, the code behind takes the files and saves them to a specific location on the server. After the files are saved, the file information that was posted is displayed in the ASP.NET page (see Listing 3). Listing 3: Uploading multiple files to the server VB <%@ Import Namespace="System.IO" %><%@ Page Language="VB" %><script runat="server"> Sub SubmitButton_Click(Source As Object, e As EventArgs) Dim filepath As String = "C:\Uploads" Dim uploadedFiles As HttpFileCollection = Request.Files Dim i As Integer = 0 Do Until i = uploadedFiles.Count Dim userPostedFile As HttpPostedFile = uploadedFiles(i) Try If (userPostedFile.ContentLength > 0) Then Span1.InnerHtml += "<u>File #" & (i+1) & "</u><br>" Span1.InnerHtml += "File Content Type: " & _ userPostedFile.ContentType & "<br>" Span1.InnerHtml += "File Size: " & _ userPostedFile.ContentLength & "kb<br>" Span1.InnerHtml += "File Name: " & _ userPostedFile.FileName & "<br>" userPostedFile.SaveAs(filepath & "\" & _ Path.GetFileName(userPostedFile.FileName)) Span1.InnerHtml += "Location where saved: " & _ filepath & "\" & _ Path.GetFileName(userPostedFile.FileName) & _ "<p>" End If Catch ex As Exception Span1.InnerHtml += "Error:<br>" & ex.Message End Try i += 1 Loop End Sub</script><html><head></head><body> <form enctype="multipart/form-data" runat="server"> <p> Select File1:<br /> <input id="File1" type="file" runat="Server" /> <br /> Select File2:<br /> <input id="File2" type="file" runat="Server" /> <br /> Select File3:<br /> <input id="File3" type="file" runat="Server" /> <br /> Select File4:<br /> <input id="File4" type="file" runat="Server" /> </p> <p> <input id="Submit1" type="submit" value="Upload Files" runat="Server" onserverclick="SubmitButton_Click" /> <br /> </p> <span id="Span1" runat="Server"></span> </form></body></html> C# <%@ Import Namespace="System.IO" %><%@ Page Language="C#" %><script runat="server"> protected void SubmitButton_Click(Object sender, EventArgs e){ string filepath = "C:\\Uploads"; HttpFileCollection uploadedFiles = Request.Files; for (int i = 0; i < uploadedFiles.Count; i++) { HttpPostedFile userPostedFile = uploadedFiles[i]; try { if (userPostedFile.ContentLength > 0 ) { Span1.InnerHtml += "<u>File #" + (i+1) + "</u><br>"; Span1.InnerHtml += "File Content Type: " + userPostedFile.ContentType + "<br>"; Span1.InnerHtml += "File Size: " + userPostedFile.ContentLength + "kb<br>"; Span1.InnerHtml += "File Name: " + userPostedFile.FileName + "<br>"; userPostedFile.SaveAs(filepath + "\\" + Path.GetFileName(userPostedFile.FileName)); Span1.InnerHtml += "Location where saved: " + filepath + "\\" + Path.GetFileName(userPostedFile.FileName) + "<p>"; } } catch (Exception Ex) { Span1.InnerText += "Error: <br>" + Ex.Message; } } }</script><html><head></head><body> <form enctype="multipart/form-data" runat="server"> <p> Select File1:<br /> <input id="File1" type="file" runat="Server" /> <br /> Select File2:<br /> <input id="File2" type="file" runat="Server" /> <br /> Select File3:<br /> <input id="File3" type="file" runat="Server" /> <br /> Select File4:<br /> <input id="File4" type="file" runat="Server" /> </p> <p> <input id="Submit1" type="submit" value="Upload Files" runat="Server" onserverclick="SubmitButton_Click" /> <br /> </p> <span id="Span1" runat="Server"></span> </form></body></html> The end user can select up to four files and click the Upload Files button, which initializes the SubmitButton_Click event. Using the HttpFileCollection class with the Request.Files property lets you gain control over all the files that are uploaded from the page. When the files are in this state, you can do whatever you want with them. In this case, the files' properties are examined and written to the screen. In the end, the files are saved to the Uploads folder in the root directory of the server. The result of this action is illustrated in Figure 5. Figure 5. Uploading four files at once to the server from a single ASP.NET page As you may have noticed, one interesting point about this example is that the states of the file input text boxes are not saved with the postback. You can see this in Figure 5. In ASP.NET, the state of the file-input text boxes cannot be saved because doing so might pose a security risk. ConclusionThe File Field control provided by ASP.NET is a powerful control that was quite difficult to achieve in the days of Active Server Pages 3.0. This new capability allows your end-users to upload one or more files to your server. Remember, you can control the size of the files that are uploaded by working with settings in either the machine.config or web.config file. Related Books
Posted by tornado
|
Feedback
# re: Quick and Dirty Guide to Configuring Log4Net For Web Applications
Exactly what I was trying to accomplish. Thanks.# re: Quick and Dirty Guide to Configuring Log4Net For Web Applications
Great info - thanks. This works for a website and the classes (Page) objects in the website. What about when the website uses different class libraries? How do I configure the class libraries so that they, too, will log to the same location as the website code does? Can I have the class libraries point to the log4net config file that is used by the website?Thanks in advance.
# re: Quick and Dirty Guide to Configuring Log4Net For Web Applications
That's the beauty of it. If your classes are declaring a logger, they will log to wherever the executing application is configured to log to.If you think of your application as an assembly graph, you need to configure your assembly and all the rest pick up that setting.
Typically your root assembly is your website or your EXE.
Hope that helps.
# re: Quick and Dirty Guide to Configuring Log4Net For Web Applications
Thanks a lot. You don't believe I have been looking for it since 41/2 hours, most of the examples out there: either they are difficult to configure or difficult to understand.Good work, keep it up
# re: Quick and Dirty Guide to Configuring Log4Net For Web Applications
Glad it helped! Thanks for the compliment.# re: Quick and Dirty Guide to Configuring Log4Net For Web Applications
Hello,We are evaluating log4net for one of our customers.
Our application consists of Asp.Net application which uses remoting.
We have included log4net in both web application and remoting application.
but the log4net logs the debug info only for the web application and not in=
the remoting application.
It doesn't even creat the log file in the remoting application directory.
we have given necessary permissions for the folders.
Help on the same is much appriciated. Thanks!
Regards,
Rohit
The following is mentioned in web.config of remoting application
<configSections>
<section name=3D"log4net" type=3D"log4net.Config.Log4NetConfigurationSect=
ionHandler,log4net" />
</configSections>
<log4net>
<appender name=3D"FileAppender" type=3D"log4net.Appender.RollingFileAppen=
der">
<param name=3D"File" value=3D"Test.log" />
<param name=3D"AppendToFile" value=3D"true" />
<param name=3D"RollingStyle" value=3D"Date" />
<param name=3D"MaxSizeRollBackups" value=3D"30" />
<param name=3D"DatePattern" value=3D"yyyyMMdd" />
<layout type=3D"log4net.Layout.PatternLayout">
<param name=3D"ConversionPattern" value=3D"%d [%t] %-5p %c [%x] - %m%n"=
/>
</layout>
<filter type=3D"log4net.Filter.LevelRangeFilter">
<levelMin value=3D"DEBUG" />
<levelMax value=3D"WARN" />
</filter>
</appender>
<appender name=3D"EventLogAppender" type=3D"log4net.Appender.EventLogAppe=
nder">
<applicationName value=3D"ApplicationName" />
<layout type=3D"log4net.Layout.PatternLayout">
<conversionPattern value=3D"%date [%thread] %-5level %logger [%ndc] - %=
message%newline" />
</layout>
<filter type=3D"log4net.Filter.LevelRangeFilter">
<levelMin value=3D"ERROR" />
<levelMax value=3D"FATAL" />
</filter>
</appender>
<root>
<level value=3D"DEBUG" />
<appender-ref ref=3D"FileAppender" />
<appender-ref ref=3D"EventLogAppender" />
</root>
</log4net>
Also in the AssemblyInfo file we have added this line
[assembly: log4net.Config.DOMConfigurator(Watch=3Dtrue)]=
# re: Quick and Dirty Guide to Configuring Log4Net For Web Applications
Rohit, which version of Log4Net are you using. The config sample you sent me uses a different syntax than what I used.# re: Quick and Dirty Guide to Configuring Log4Net For Web Applications
Thanks for the reply, we are using .Net framework version 1.1 and log4net version log4net-1.2.0-beta8. Please let me know where we are going wrong.I also noticed that log.IsDebugEnabled(also any other IsxxxEnabled) function always returns false. I tried with the same configuration with other test projects i created it works but it doesn't work with my actual application. [It doesn't work with the Remoting application but same configuration works with web application]
Thanks again fro the reply...
# re: Quick and Dirty Guide to Configuring Log4Net For Web Applications
I'm using version 1.2.0.30714 and the only thing I noticed is that your configuration file was very different from mine in how you declared the appenders.# re: Quick and Dirty Guide to Configuring Log4Net For Web Applications
Hi all,I need help to set the log4net to delete log files automatically after 10days.
# re: Quick and Dirty Guide to Configuring Log4Net For Web Applications
HiI'm simply trying to get NUnit to run some tests that log, with the Test project being the one that starts the application logging. In fact, ive seen this before and this is what im trying todo: im using a configuration class (that will utlimately read from the registry or db) that says where the configuration path should be, and that in turn tells to write to file, ill write out the TestFixture first and then the config file. I dont get any errors but neither do i get a file created or written to... thanks for any help!
[TestFixture]
public class TestLogging
{
static TestLogging()
{
string configFile = Configuration.LogConfigFilePath;
log4net.Config.DOMConfigurator.Configure(new System.IO.FileInfo(configFile));
ILog log = LogManager.GetLogger(typeof(TestLogging));
log.Info("Starting Logging");
}
[Test]
public void Test()
{
ILog logger = LogManager.GetLogger("my logger");
logger.Info("Logging the Test() method");
}
}
<appender name="FileAppender" type="log4net.Appender.FileAppender">
<file value="c:\logs\log-file.txt" />
<appendToFile value="true" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
</layout>
</appender>
# re: Quick and Dirty Guide to Configuring Log4Net For Web Applications
Great, just what I needed. Been pounding my head against the wall for last couple of hours, trying to get it work, and this was excactly what I needed to get starting.I've made 2 slight chances though
1. I put the assembly line in the global.asax instead of the assembly.cs. As far as I can tell it makes no difference and it just seems a bit more 'logical' place to me - purely a question of taste (and of where I suspect my collegues will be able to find it :)
2. I've used
[assembly: log4net.Config.XmlConfigurator( ConfigFile="Log4Net.config",Watch=true )]
Although it compiled just fine, I got a warning saying that the DOMConfigurator was obsolete and that the XmlConfigurator should be used instead - so I did :)
/emil@obey.your.compiler.or.die.com