. NET 4.6에서 GC.TryStartNoGCRegion ~ GC.EndNoGCRegion 라는 것이 추가 되었다.

GC.TryStartNoGCRegion에서 GC.EndNoGCRegion까지 자동 GC를 억제할 수 있다.

 

 

처리 중 GC가 일어날 경우

아래 코드는 1MB의 바이트 배열을 생성하고 GC 회수 대상이 될 수 있는 구성이다.

using System;

using System.Runtime;

using System.Threading;

 

namespace TestSpace {

    class Program {

        static void Main( string[] args ) {

            for( int i = 0; i < 30; i++ ) {

                var n = create();

                Thread.Sleep( 100 );

                GC.KeepAlive( n );

 

                Console.WriteLine( $"GC:{GC.CollectionCount( 0 ) } {GC.GetTotalMemory( false ):#,##byte} " );

            }

            Console.ReadLine();

 

        }

 

        public static WeakReference<byte[]> create() {

            var weak = new WeakReference<byte[]>( new byte[1048576] );

            return weak;

        }

 

    }

}

 

동작 환경에 따라서 차이가 있겠지만 몇 번 GC가 발생하는지 확인할 수 있다.

 

 

GC.TryStartNoGCRegion ~ GC.EndNoGCRegion 로 둘러싸 본다

속도가 중시되는 경우 GC 발생이 병목이 될 수 가능성이 있다.

 

GC.TryStartNoGCRegion에 메모리 사이즈(바이트 단위)를 지정한다.

이곳에서 지정한 사이즈가 소비될 때까지 GC가 억제되게 된다.

 

예를 들어 GC.TryStartNoGCRegion(15728640)라고 지정하면 15MB 소비할 때까지 GC를 억제하게 된다.

또한 지정 가능한 최대 크기는 실행 환경에 의해서 결정된다.

상세는 Fundamentals of Garbage Collection을 참조.

https://msdn.microsoft.com/en-us/library/ee787088(v=vs.110).aspx

 

일반적인 클라이언트 PC 32비트 환경에서는 16MB, 64비트 환경에서는 256MB 이다.

아래의 예는 32비트 환경을 예상한 것이다.

using System;

using System.Runtime;

using System.Threading;

 

namespace TestSpace {

    class Program {

        static void Main( string[] args ) {

 

            if( GC.TryStartNoGCRegion( 15728640 ) ) {

                for( int i = 0; i < 30; i++ ) {

                    var n = create();

                    Thread.Sleep( 100 );

                    GC.KeepAlive( n );

 

                    Console.WriteLine( $"GC:{GC.CollectionCount( 0 ) } {GC.GetTotalMemory( false ):#,##byte} " );

                }

 

                if( GCSettings.LatencyMode == GCLatencyMode.NoGCRegion )

                    GC.EndNoGCRegion();

            }

            Console.ReadLine();

 

        }

 

        public static WeakReference<byte[]> create() {

            var weak = new WeakReference<byte[]>( new byte[1048576] );

            return weak;

        }

 

    }

 

}

 

GC.TryStartNoGCRegion 에서 지정한 15MB까지 GC가 이뤄지지 않는 것을 확인할 수 있다.

그러나 15MB를 초과 한 시점에서 자동적으로 GC가 발동 하는 것도 확인할 수 있다.

 

 

주의 사항

GC.TryStartNoGCRegion을 호출해서 새롭게 할당된 메모리가 지정 크기까지 도달할 때까지 GC를 억제하는 것이다.

이미 할당된 메모리 사이즈는 무의미하다.

 

GC.TryStartNoGCRegion을 다중으로 호출 할 수는 없다.

GC.TryStartNoGCRegion에 성공하면 GCSettings.LatencyMode값이 GCLatencyMode.NoGCRegion로 변경된다.

거꾸로 말하면, GCSettings.LatencyMode == GCLatencyMode.NoGCRegion 이라면 GC 억제 중이라고 판단할 수 있다.

 

GC.EndNoGCRegion() GCSettings.LatencyMode GCLatencyMode.NoGCRegion인 경우에만 실행 가능하다.

GC.EndNoGCRegion()를 호출하지 않아도 자동으로 해제되는 경우가 있다.

GC.TryStartNoGCRegion에서 지정한 사이즈에 달했다

GC.Collect등이 호출 되어 수동에 의한 GC가 발생했다

GC.GetTotalMemory(true)가 실행되었다

 

이런 경우 그 시점에서 GC의 억제 상태가 해제된다.

 

 

MSDN: GC.TryStartNoGCRegion

https://msdn.microsoft.com/en-us/library/system.gc.trystartnogcregion(v=vs.110).aspx

 

동시에 복수의 GC.TryStartNoGCRegion을 호출한 경우 InvalidOperationException이 발생하므로, 멀티 쓰레드에서 GC.TryStartNoGCRegion~GC.EndNoGCRegion을 할 때는 반드시 동기화 해야 한다.

 

출처: http://qiita.com/Temarin_PITA/items/749ad661fd13d7402794

 

저작자 표시
신고
by 흥배 2016.08.01 08:00

현재 내용으로 봐서는 2017 2분기쯤 되면 꽤 쓸만하겠네요^^;

링크: https://blogs.msdn.microsoft.com/dotnet/2016/07/15/net-core-roadmap/

 

 

8월말쯤에 1.0.1 나올 예정.

 

올해 말 혹은 내년 3월까지의 예정.

 

 

.NET Core Tooling

- .csproj/MSBuild project system 지원

- 프레임워크 관리용 명령어 추가

 

 

언어 관련. C# 7, VB 15 추가 언어 사양

- tuple, 패턴 매칭

- Value Task, Ref return, Throw expression, Binary Literals, Digit Separators

- Out vars, Local Function

 

 

ASP.NET

- Web Socket

- URL Rewriting Middleware

- Azure 관련 강화

- Service Fabric support via WebListener based server

- MVC & DI Startup Time Improvements

- 프리뷰

- SignalR

- View Pages (Views without MVC Controllers)

 

 

.NET Core Runtime

- ARM 32/64

- .NET Core Runtime

 

 

Entity Framework Core

- Azure

- Transient fault handling (resiliency)

- Mapping

- Custom type conversions

- Complex types (value objects)

- Entity entry APIs

- Update pipeline

- CUD stored procedures

- Better batching (TVPs)

- Ambient transactions

- Query

- Stability, performance.

- Migrations

- Seed data

- Stability

- Reverse engineer

- Pluralization

- VS item template (UX)

 

 

Q1 2017 / Q2 2017

- .NET Standard 2.0

 

 

출처: https://opcdiary.net/?p=32475

 

저작자 표시
신고
by 흥배 2016.07.26 10:05

◎ : OS 에 처음부터 포함 되어 있다

● : 다운로드 해서 설치 가능 ※ ( ) 은 시스템 요건 상 필요로 하는 OS 서비스 팩



출처: http://blogs.msdn.com/b/visualstudio_jpn/archive/2015/10/19/dotnet-framework-support-lifecycle-policy-summary-2015oct.aspx

저작자 표시
신고
by 흥배 2015.10.23 08:00

Windows Forms

처리하지 못한 예외가 발생한 경우 Application.ThreadException 이벤트 핸들러가 자동으로 호출된다. 그래서 여기에 에러 메시지를 보여주는 기능 등을 작성하면 된다.

)

namespace WindowsFormsApplication1

{

    static class Program

    {

        [STAThread]

        static void Main()

        {

            Application.ThreadException +=

 new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);

            Application.EnableVisualStyles();

            Application.SetCompatibleTextRenderingDefault(false);

            Application.Run(new Form1());

        }

 

        static void Application_ThreadException(object sender,

 System.Threading.ThreadExceptionEventArgs e)

        {

            MessageBox.Show("처리하지 못한 예외 발생");

            Application.Exit();

        }

    }

}

 

 

Console

AppDomain.CurrentDomain.UnhandledException 이벤트 핸들러를 자동으로 호출한다.

namespace ConsoleApplication1

{

    class Program

    {

        static void Main(string[] args)

        {

            AppDomain.CurrentDomain.UnhandledException +=

new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

        }

 

        static void CurrentDomain_UnhandledException(object sender,

UnhandledExceptionEventArgs e)

        {

            MessageBox.Show("처리하지 못한 예외 발생");

            Environment.Exit(-1);

        }

    }

}

 

 

저작자 표시
신고
by 흥배 2014.04.30 08:00

.NET 언어에서 Native 코드를 직접 만드는 것.

.NET 언어를 컴파일 할 때 C++ 컴파일러가 관여.

클라이언트의 디바이스에 완전하게 네이티브화 한 상태에서 배포.

클라이언트에서 쓸데 없는 전력 소모를 안하고 첫 실행도 빠르다.

현재는 Windows Store App만 지원. 장래에 데스크탑 App도 지원할 예정임.

 

클라이언트 디바이스에 닷넷 프레임워크 설치?

필요없음. 실행에 필요한 것은 전부 네이티브 화, 정적 링크

 

C# 네이티브 컴파일러가 동작하는가?

아니다. MSIL의 사전 컴파일. C#의 경우 C# -> MSIL ->MDIL -> 네이티브화 순서로.

사양 상 .NET 언어라면 무엇이든지 네이티브화 가능하나 현재는 C#만 지원.

 

GC?

여전히 사용. 메모리는 관리된다. Native이지만 unmanaged는 아니다.

.NET Native로 빌드하면 실행 파일에 MRT.dll이라는 DLL이 배포되어 그 안에 GC 코드가 들어가 있다.

 

바이너리 사이즈는 커지지 않나?

사용하지 않는 부분은 빠지고 네이티브화 하여 정적 링크 되므로 괜찮다.

 

라이브러리가 갱신 될 때는?

이것이 현재 Store App만 적용된 이유가 생각. Store 서버 상에서 클라우드 컴파일 하므로 의존하고 있는 라이브러리가 변경 된다면 자동으로 바이너리가 갱신된다.

 

리플렉션은?

가장 제약이 많은 곳이다. 사용하지 않는 코드는 제거되므로 사전에 '리플렉션을 사용하고 있다'라고 컴파일러에 알려줘야 한다.

XAML 데이터 바인딩으로 사용한 타입이나 DataContractSerializer 생상자에 typeof(T)로 넘긴 타입은 자동으로 리플렉션 처리된다.

참조 정의 파일을 만들면 리플렉션 사용에 문제 없고 dynamic도 사용할 수 있다.

 

제너릭은?

타입형 제너릭이 일부 실행할 수 없는 문제가 발생할 수 있다.

 

사용할 수 있는 .NET 표준 라이브러리는?

현재 베이스는 .NET 코어 기준. 여기에 몇 개의 클래스/메소드를 사용할 수 없다.

예를 들면 아래의 것은 사용 불가

DataAnnotations

Microsoft.VisualBasic

WCF

DelegateBeginInvoke/EndInvoke

COM Interop로 VARIANT

DateTime.Parse로 파스 할 수 있는 문자욜에 제약이 있다.

 

그 외 제약은?

구조체 최대 사이즈는 1M Byte

3차원 이상의 다상 배열은 사용할 수 없다.

포인터 타입 배열 사용 불가

 

코드 최적화는?

JIT와 달리 컴파일에 시간이 걸리므로 이런저런 최적화를 해준다. 컴파일러는 C++과 같은 것을 공유하고 있다. 그래서 auto-vectorization도 해 준다.

 

 

 

관련 세션 자료 http://channel9.msdn.com/Events/Build/2014/3-589

공식 사이트 http://msdn.microsoft.com/en-us/vstudio/dotnetnative

MSDN  http://msdn.microsoft.com/en-us/library/dn584397.aspx

FAQ   http://msdn.microsoft.com/en-US/vstudio/dn642499.aspx

 

 

 

출처: http://ufcpp.wordpress.com/2014/04/03/net-native/

 

 

 

 

저작자 표시
신고
by 흥배 2014.04.10 08:00

  • Method = "GET" 방식은 인자 타입은 string 이어야 하고 "/{id}" 방식이어야 한다.
    • Post 방식에서는 int 타입도 사용할 있다.

신고
by 흥배 2014.03.17 08:00

mono: ECMA CLI 런타임

mono 런타임은 exe 파일이나 dll 파일을 해석하여 그것의 CIL(MSIL) 코드를 CPU의 네이티브 명령으로 옮겨서 실행하는 실행 엔진이다. 그 내용은 CIL(MSIL) 메타 데이터 로더, JIT 컴파일러 등의 실행 엔진, 메모리 관리(가베지 컬렉션), AppDomain 이나 리모, 스레드 관리,  I/O처리,  P/Invoke 구현, 디버거 지원 등 다방면에 걸친다.

 

어셈블리의 로드와 실행

mono CIL 코드를 실행하는 방식으로서 가장 간단한 것은 exe 형식의 CIL 코드 실행이다. executable(실행 형식)로서의 mono(Windows라면 mono.exe) exe 형식의 MSIL 코드를 실행하기 위한 콘솔 도구이다. 예를 들면 다음 명령은 실제로 executable mono를 콘솔에서 실행하고 있다.

 

<콘솔>

$ mono MyProgram.exe   // MyProgram.exe를 실행한다

 

.NET exe 파일은 Windows 상에서는 네이티브 실행 파일 형식(Coff)에 들어가 있지만 Windows 이외의 OS에서는 그렇지 않다. CIL은 어떻게 발버둥 쳐도 Linux의 표준 실행 파일 형식인 ELF 포맷에는 적합하지 않다. ECMA CLI는 이 점에서는 분명히 Windows에 유리한 사양을 책정하고 있다. "mono에서 인수에 실행 파일을 지정하여 실행할 수 밖에 없는 것은 불편하다"라는 것은 착각이다(Linux에서는 "binfmt_misc" 라는 기능을 사용해 .NET 형식의 바이너리를 mono의 호출로 연결할 수 있다. .exe 형식은 Windows의 기본 실행 파일로 Wine에서 실행하지 않으면 쓸 수 없는 가능성도 있으으므로 Linux 상에서 확장자나 MIME 타입으로 연관시키는 것은 추천할 수 없다).

 

.NET 애플리케이션은 exe 형식의 엔트리 포인트를 가질 수는 없다. 예를 들면 ASP.NET Web 어플리케이션은 dll 형식으로 생성되며, ASP.NET의 실행 엔진(IIS이라면 ISAPI)가 독자적으로 CLR 혹은 mono의 호스팅 API를 사용하여 독립된 응용 프로그램 도메인을 만들어서 거기에 dll을 로드하고, 소정의 엔트리 포인트에서 애플리케이션을 실행한다. Silverlight 이라면 CoreCLR로 불리는 호스트가 xap 애플리케이션 패키지에서 공약에 따라 dll을 찾아내 그것을 로드하고 있다.

 

같은 일이 mono에 대해서도 들어맞는다. mono의 실행 엔진의 몸체는 "libmono"라고 하는 라이브러리로 독립하고 있으며 이를 사용하면 독자적인 호스트를 작성할 수 있다. libmono API "mono의 임베디드 API" 또는 "embedded mono"로 불린다. 전형적인 이용 사례는 브라우저 플러그 인이다. Unity는 가장 유명한 embedded mono의 사용자라고 말할 수 있지만, 그들은 "Web Player"라는 런타임 플러그 인을 제공하고 있다. Mono팀에서도 과거 "Moonlight" 라는 Silverlight 호환 환경을 공개했었는데 이것도 브라우저 플러그 인 API의 표준적 존재로 NPAPI을 경유하여 mono 런타임을 실행하는 플러그 인이다. 구글은 "NaCL(native client)"라는 브라우저용 네이티브 코드 실행 환경에서 mono를 동작시키기 위한 패치를 mono에 컨트리뷰트 했다. mono를 움직이는 NaCL에서는 Unity를 움직일 수 있다.

 

mono 런타임은 어셈블리의 CIL 메타 데이터를 로드하고, 환경 변수나 구성 파일과 대조, 적절한 mscorlib.dll 파일을 로드하여 .NET 런타임의 프로파일을 선택한다. 로드 되는 mscorlib.dll 버전에 따라 그 런타임의 프로파일(.NET 2.0/.NET 4.0/.NET 4.5/iOS/Android )이 결정된다. .NET 2.0 프로파일은 공식은 이미 지원하지 않으므로 언제 없어져도 이상하지 않는 프로파일이므로 .NET 4.x로 이행하는 것이 좋다.

 

 

네이티브 실행 가능한 코드 생성

프로파일이 정해지면 AppDomain이 생성되고 거기에 어셈블리가 로드되어 실행 엔진에 의해 CPU 명령으로 변환된 CIL의 코드가 실행된다. 현재 실행 엔진은 2종류가 있다. "mini"로 불리는 JIT 컴파일러(실행 시 컴파일) AOT 컴파일러(사전 컴파일)이다(이하 단순히 JIT AOT로 표기). 이전에는 이 밖에 "mint" 라는 인터프리터도 있었지만 이미 역할을 끝내고 조만간 소멸할 예정이다. AOT.NET에서 NGen가 비슷한 일을 하고 있는데, 애플리케이션의 실행을 수반하지 않고 dll을 타겟 CPU의 명령으로 컴파일하여 네이티브 공유 라이브러리를 생성하는 기능이다.

 

mono 런타임에서는 직접 CPU 명령을 생성하는 mono 자신의 코드 생성 엔진에 더해, LLVM에 의한 코드 생성 엔진도 지원하고 있다. LLVM이 더 빠른 코드를 생성하는 부분은 적지 않다.  한편 LLVM을 로드 하기 위해 필요한 메모리가 증가하므로 LLVM은 만인 대상은 아니다.

* LLVM 코드생성: http://www.mono-project.com/Mono_LLVM

 

mono AOT는 실제로는 " AOT" "통상 AOT" 2 종류가 있다. AOT는 모든 CIL 코드를 네이티브 명령으로 변환 할 수 있는 것은 아니다. 실행 시 CPU 명령을 JIT에서 생성하는 것이 불가결한 기능이 있다. 예를 들면 System.Reflection.Emit 이름 공간의 API를 사용하여 동적으로 코드를 생성하고 실행하는 것은 AOT에서는 불가능하다. 통상의 AOT모드의 경우 AOT 변환되지 않는 코드를 만나면 코드는 JIT에 폴 백하여 실행된다. AOT의 경우는 에러가 되면 끝이다.

 

동적으로 생성된 코드가 동작하지 않는 환경이 있다. 대표적으로 Jailbreak 하지 않은 iOS 환경이다. iOS는 코드를 실행하는 플랫폼 차원에서 동적으로 코드를 생성하기 위해 필요한 코드를 무효화한다. AOT는 이러한 플랫폼에서 코드를 실행할 때 사용한다. Xamarin.iOS는 빌드 툴 체인에서 자동으로 이 AOT를 사용하여 애플리케이션 코드를 빌드한다(시뮬레이션용 빌드를 제외).

 

 

가베지 컬렉션(GC)

mono 런타임은 오랫동안 고전적 GC의 대표적인 존재인 "Boehm GC"에 손을 더한 것을 사용했지만 현재는 세대별 가베지 컬렉션을 구현하고 있다. 이 구현은 "SGen"이라고 불린다(원래는 "Simple Generational GC" 이었는데 지금은 분명히 simple이 아니다). mono 3.2 이후는 기본적으로 SGen이 유효하게 되어 있다. 현재의 Xamarin.iOS는 기본은 SGen이 아니며 선택 가능하다. Xamarin.Android Java GC와 협조 동작인 관계로 당초부터 SGen에서만 실행 가능했다.

 

 

mscorlib의 내부 호출 기능(및 파일 I/O의 특기 사항)

mono 런타임의 또 하나 중요한 기능은 mscorlib.dll에서 MethodImpl 속성에 "MethodImplOptions.InternalCall"를 지정하여 선언하고 있는 extern 메서드의 실체인 InternalCall(mono의 소스 코드에서는 "icall"으로 불린다). mono mscorlib 소스 코드(이하 "소스"로 표기)에는 C#에서는 구현할 수 없기 때문에 C에서 런타임 기능을 호출 하는 부분이 불가피하게 존재한다. 형 시스템에 접속 또는 쓰레드의 조작, I/O 조작 등이 있으므로 이들은 InternalCall을 통해 mono 런타임에서 네이티브로 실행된다.

 

여기서 특별히 언급해 둘 것이 I/O 주변이다. 이 부분은 Windows 이외의 환경에서는 "io-layer"로 불리는 Windows I/O API의 에뮬레이션 구현이 담당하고 있으며 직접 파일 I/O가 이뤄지고 있는 것은 아니다. 일반적으로는 Windows Linux에서는 파일 처리에 큰 차이가 있다. 가장 중요한 것은 Linux에서는 대문자와 소문자를 구분하는 것이다. Windows 상에서는 대문자와 소문자가 구분되지 않으므로 예를 들면 다음과 같은 코드는 문제없이 동작한다.

 

C#

File.WriteAllText ("FILE.txt", "test string");

return File.ReadAllText ("file.txt");

 

이를 Linux 환경에 가져가면 거의 실패하다. Linux 상에서 사용되는 파일 시스템은 "FILE.txt" "file.txt"을 별개로 다루기 때문이다. 이것은 프로그래머가 스스로 주의해야 할 일이지만 .NET 애플리케이션은 대부분 Windows 상에서 개발되고 있기 때문에 Windows에서 움직이면 OK라는 전제 아래 적혀 있는 것이 많다. 이러한 프로그램에도 어느 정도 움직이도록 mono에는 "MONO_IOMAP" 이란 환경 변수에서 Windows I/O 같은 거동을 지정할 수 있는 기능이 있다. "MONO_IOMAP=all"로 지정하고 mono 상에서 애플리케이션을 실행하면 위의 코드도 생각대로에 동작한다(참고로 이를 사용하면 파일 이름의 문제가 모두 해결하려는 게 아니다. 어디까지나 파일 I/O API가 미칠 수 있는 범위에서만이다. 파일 이름의 대 소문자는 항상 일치시켜 두는 것이 바람직하다).

 

참고로 Mac OS X에서 사용하는 HFS+ 파일 시스템에서는(기본 포맷에서는) 대 소문자를 구분하지 않는다. 한편 이는 mono에 한정 하지 않는 이야기지만 HFS+는 파일명을 취급할 때 Unicode NFD 정규화와 유사한 독자적인 변환을 실시하기 때문에 Mac OS X와 그 이외의 환경에서 크로스 개발하고 있는 경우에는 조심하는 것이 좋다(NTFS ext4 등의 파일 시스템은 특히 이러한 변환은 않는다). iOS Mac OS X Android Linux을 바탕으로 구축된 OS 라는 것을 기억해 두면 덫에 걸리지 않는다.

 

 

클래스 라이브러리

mono에 어느 클래스 라이브러리의 어셈블리가 있을지는 GitHub 저장소를 보는 것이 가장 빠르지만 각각 설명하는 것만으로도 너무 많으므로 개요만 정리한다. 기본적으로 Xamarin이 지원하고 있는 분야는 상용 지원으로서 비교적 두터운 개선을 받고 있다. 반대로 현재의 Xamarin이 모바일 플랫폼에서 사용하지 않는 클래스 라이브러리는 거의 관리되지 않는 상태이다.

 

.NET 3.0에서 추가된 API는 대부분 존재하지 않는다. WPF(Windows Presentation Foundation) WF(Windows Workflow Foundation)이 없고 일부 코드가 "olive"라는 다른 저장소에 형태만 존재하고 있다. WCF도 모바일에서 사용 되는 부분 이외는 거의 미완성으로 그치고 있다. 모바일 프로파일의 WCF Silverlight 2.0과 거의 같다.

 

Windows 고유 API를 전제로 하는 라이브러리(System.Management.dll, System.Core.dll System.IO.Pipes 이름 공간 등)도 구현은 없다. COM Windows의 고유 기능이라서 mono에서 동작하는 것은 기대할 수 없다(사실 Windows 한정으로 mono 런타임에 COM 기능을 구현하고 있는 커뮤니티 해커는 존재했지만 아무튼 의존하는 라이브러리 측이 Windows 전용이므로 mono로 지원하는 의의는 거의 없다).

 

.NET 클래스 라이브러리는 너무 방대하여 이것을 구현하는 Mono 클래스 라이브러리에는 미 구현이 많다. 구체적으로는 틀이나 멤버가 존재하지 않는 것과 존재하고 있지만 호출해 보면 NotImplementedException 예외를 던진다(원래 어떤 형태가 얼마나 구현되고 있는가 하는 정보는 Mono "class status" web 페이지에서 확인할 수 있도록 되어 있는데 본고 집필 시점에서 갱신이 멈춘 상태다).

 

mono의 클래스 라이브러리는 mono의 소스 트리 상 mcs/class 디렉토리 아래에 대량으로 작성되고 있다(역사적으로는 C#로 쓰여진 코드는 모두 "mcs"라는 모듈에 포함되었다. GitHub에 이행했을 때 mono의 소스 트리에 통합되었다). 디렉터리 구성 규칙은 기본적으로 다음과 같다.

 

mcs/class/[Assembly]/[Namespaces]/[Type]. cs

 

, 클래스 라이브러리의 테스트에는 NUnit(2013년 시점에서는 다소 오래된 2.4.8)이 이용되고 있으며, 각 라이브러리의 테스트의 소스 디렉터리 구성은 대체로 다음과 같다.

 

mcs/class/[Assembly]/Test/[Namespaces]/[Type]Test.cs

 

디렉토리 구조의 주요 예외로서 mscorlib 어셈블리(mscorlib.dll 파일) "corlib", System.Windows.Forms 어셈블리(System.Windows.Forms.dll 파일) "Managed.Windows.Forms" 라는 디렉토리 이름이 되어 있다(외에도 몇 가지 예외는 있다). 클래스 라이브러리는 물론 모두 C#으로 적혀 있다. 다만 빌드에 IDE는 사용하지 않고, make 명령이 사용되고 있다. 소스에 대해서는 aspnetwebstack, entityframeworkrx, cecil 등 외부 프로젝트를 git submodule 로 주입한 것도 있지만 어셈블리의 디렉토리 구성은 변하지 않는다(소스의 목록은 서브 모듈 속을 참조하고 있다).

 

소스 코딩 규칙은 mono적이다. Linux kernel 문화에 친화적이고 불필요하게 줄 수를 늘리지 않는 설계가 되어 있다. 이 코딩 규칙이 마음에 들지 않지만 소스를 읽을 필요가 있는 경우는 monodevelop의 코딩 스타일 설정을 Visual Studio로 한 후에 자동 포맷을 설정하면 낫다.

 

만약 클래스 라이브러리의 미 구현 부분을 구현하거나, 버그 등을 발견하고 수정을 시도 하고 싶은 경우는 한번 mono의 트리 전체를 빌드 한 뒤 그 어셈블리의 디렉토리로 이동하여 make run-test를 실행하면, NUnit 테스트가 실행되므로 편리하다. 참고로 빌드된 dll 파일 등은 mcs/class/lib/net_4_5 와 같은 디렉토리에 생성된다.

 

mono의 클래스 라이브러리에는 사실 복수의 프로파일이 있다. .NET 2.0, .NET 4.0, .NET 4.5로 각각 .NET의 프로파일에 상당한다. 클래스 라이브러리 디렉터리에서 make를 실행했을 때 빌드되는 것은 최신의 프로파일 뿐이다(현재는 .NET 4.5). 낡은 프로파일의 어셈블리를 빌드 하려면 make PROFILE=net_2_0 라는 빌드를 실행할 필요가 있다. 사실 더해서 모바일 프로파일도 있지만 그것에 대해서는 다음에 이야기 하겠다.

 

 

 

 

출처: http://www.buildinsider.net/mobile/insidexamarin/03

 

저작자 표시
신고
by 흥배 2014.02.26 08:00

.NET 라이브러리에서 제공하는 string, Int, DateTime, DBCommand 등의 클래스 라이브러리의 기능을 확장 시켜주는 오픈 소스 라이브러리.

기존에 없는 기능을 직접 만들어야 하는 수고를 크게 들어준다. 코드 가독성에도 아주 좋다.

 

http://zextensionmethods.codeplex.com

 

아래는 bool을 확장하여 if문 코드를 줄여준다.

 

 

 

사용 방법

 

 

 

 

기능이 아주 아주 기능이 많으므로 온라인 도움말을 참고한다.

http://zzzportal.com/Documentations/Z.ExtensionMethods/index.html

 

예) string의 기능 확장 중 일부 들

Concat, ConcatWith: 문자열 연결.

Contains, ContainsAll, ContainsAny: 특정 문자가 포함되어 있는지 여부.

Copy: 새로운 string 인스턴스 반환.

EncodeBaset64, DecodeBaset64: Base64 스트링 변환.

EncryptRSA, DecryptRSA: RSA 암호화 변환.

DeserializeBinary<T>: 스트링 바이너리를 <T>로 변환.

ExtractLetter: 문자열에서 이외 것(숫자) 제거.

ExtractNumer: 문자열에서 숫자만 가져오기.

IfEmpty: string이 빈 것이면 지정된 문자열을 반환.

IsValidEmail: 문자열이 email 형식인지 여부 조사.

IsValidIP: 문자열이 IP 주소 형식인지 여부 조사.

ToByteArray: 문자열을 바이너리로 변환.

ToEnum<T>: 문자열을 enum 타입으로 변환.

ToFileInfo: 문자열을 파일 정보 타입으로 변환.

ToMemoryStream: 문자열을 stream으로 변환.

 

 

 

신고
by 흥배 2014.02.10 08:00


Log4netTest.zip

log4net 이란

아파치의 오픈소스 로그 라이브러리로 Java, C++, .NET 다양한 언어와 플랫폼을 지원하는 로그 라이브러리의 .NET 버전이다. 로그를 파일, 콘솔화면, DB 출력할 있고 날짜, 파일 크기마다 로그를 새로 생성하도록 있다.

 

.NET에서 사용하기

  • .NET 경우 NuGet 사용하여 설치한다.

 

  • NuGet 사용하여 log4net 라이브러리를 설치하면 아래 그림처럼 자동으로 프로젝트에 설정된다.

 

  • 상태에서 빌드를 한다. 실행 파일이 있는 위치에 log4net 관련 dll xml 파일이 있다.

 

  • NuGet으로 설치한 경우 log4net 관련 설정 정보는 app.config 기록한다.

 

  • 로그가 남도록 하면 아래와 같이 로그 파일이 생겨진다.

 

  • 프로그램 실행 도중 로그 설정 파일을 변경하여 적용하기 위해서는 아래 처럼 AssemblyInfo.cs [assembly: log4net.Config.XmlConfigurator(Watch = true)] 추가한다.

 

  • 로그 설정 정보가 있는 app.confog

 

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

    <startup>

        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />

    </startup>

   

    <log4net>

      <!-- 파일을 사용하는 Appender -->

      <appender name="LogToFile" type="log4net.Appender.FileAppender" >

   

        <!-- 만들 로그 파일 -->

        <param name="File" value="log-file.txt" />

        <!-- 기존에 로그 파일이 있다면 연결해서 사용 여부 -->

        <param name="AppendToFile" value="true" />

        <!-- 로그 형식 -->

        <layout type="log4net.Layout.PatternLayout">

          <param name="ConversionPattern" value="%date [%thread] %-5level %logger - %message%newline" />

        </layout>

      </appender>

   

      <!-- 날짜 단위로 Rolling 하는 파일 Appender -->

      <appender name="DayRollingLogToFile" type="log4net.Appender.RollingFileAppender">

        <!-- 로그 파일 이름의 선두 부분 -->

        <File value="LogSample" />

        <!-- 기존에 로그 파일이 있다면 연결해서 사용 여부 -->

        <appendToFile value="true" />

        <!-- 날짜 마다 파일을 작성하는 것을 지정 -->

        <rollingStyle value="date" />

        <!-- 로그 파일 이름 고정 여부. 고정하지 않을 때는 false -->

        <staticLogFileName value="false" />

        <!-- 파일 이름에의 날짜 부분 -->

        <datePattern value='"."yyyyMMdd".log"' />

   

        <layout type="log4net.Layout.PatternLayout">

          <!-- 로그 형식 -->

          <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />

        </layout>

      </appender>

   

      <!-- 파일 크기 단위로 Rolling 하는 파일 Appender -->

      <appender name="SizeRollingLogToFile" type="log4net.Appender.RollingFileAppender">

        <file value="mylogfile.txt" />

        <appendToFile value="true" />

        <rollingStyle value="Size" />

        <maxSizeRollBackups value="5" />

        <maximumFileSize value="10MB" />

        <staticLogFileName value="true" />

        <layout type="log4net.Layout.PatternLayout">

          <conversionPattern value="%date [%thread] %level %logger - %message%newline" />

        </layout>

      </appender>

   

      <!-- 콘솔을 사용하는 Appender -->

      <appender name="LogToConsole" type="log4net.Appender.ConsoleAppender">

        <layout type="log4net.Layout.PatternLayout">

          <conversionPattern value="%date{ABSOLUTE} [%thread] %level %logger - %message%newline"/>

        </layout>

      </appender>

   

      <root>

        <!-- 로그 레벨 지정. 아래에서는 INFO 레벨 이상만 로그가 남는다 -->

        <level value="INFO" />

        <!-- 아래 로그 레벨 중 하나 선택

        <level value="OFF " />

        <level value="FATAL" />

        <level value="ERROR" />

        <level value="WARN" />

        <level value="INFO" />

        <level value="DEBUG" />

        <level value="ALL" />

        -->

        <!-- 어느 로그를 사용할 것인지 지정한다. 현재 파일로그와 날짜별 로그 사용 -->

        <appender-ref ref="LogToFile" />

        <appender-ref ref="DayRollingLogToFile" />

        <!-- 복수 지정 가능 -->

      </root>

    </log4net>

     

</configuration>

 

  • 예제 소스 코드

 

public partial class Form1 : Form

{

   private static readonly log4net.ILog log =

   log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

   

   public Form1()

   {

       InitializeComponent();

   }

   

   private void button1_Click(object sender, EventArgs e)

   {

       log.Info("INFO 출력");

       log.Debug("Debug 출력");

       log.Error("Error 출력");

   }

}

  

 

로그 정보에 로그를 남기는 코드 부분의 파일명이나 위치를 남기고 싶은 경우

로그 형식에 %location 사용한다.

 

<param name="ConversionPattern" value="%date [%thread] %-5level %logger - %location %message%newline" />

  • 로그 파일을 특정 폴더에 남기고 싶은경우

  • 현재 실행 파일이 있는 폴더의 Logs 폴더에 남기고 싶은 경우

 

<log4net>

    <!--날짜 단위로 Rolling 하는 파일 Appender-->

    <appender name="DayRollingLogToFile" type="log4net.Appender.RollingFileAppender">

      <!--로그 파일 이름의 선두 부분-->

      <File value="Logs\GameLog" />

 

로그 설정 정보를 app.config 파일 이외의 파일에 남기고 싶을

'AssemblyInfo.cs' 파일에 아래의 코드를 입력한다

 

[assembly: log4net.Config.DOMConfigurator(ConfigFile = "../../log4net.xml", Watch = true)]

또는

 

// 로그 설정 파일 로딩 후 적용

string logFilePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log4net_config.xml");

var finfo = new System.IO.FileInfo(logFilePath);

log4net.Config.XmlConfigurator.ConfigureAndWatch(finfo);

logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

 

nunit에서 사용할 동작하지 않는 경우

Log4Net.config 파일을 만들어서 아래와 같이 만든다.

 

<?xml version="1.0" encoding="utf-8" ?>

<log4net>

<!-- 필요한 설정 정보 입력 -->

</log4net>

소스에서 위에서 만든 설정 파일을 사용하도록 한다.

 

[SetUp]

public void RunBeforeAnyTests()

{

    string logFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Log4Net.config");

    FileInfo finfo = new FileInfo(logFilePath);

    log4net.Config.XmlConfigurator.ConfigureAndWatch(finfo);

}

 

로그를 메일로 보내기

  • 로그 설정 파일에서 log4net.Appender.SmtpAppender 사용한다.

  • 보낼 메일의 문지 인코딩은 UTF-8 사용한다.

  • 로그 메시지 하나마다 메일을 보내는 것이 아니고 하나의 메일에는 복수의 로그 메시지가 있다.

  • 설정

 

 

<appender name="SmtpAppender" type="log4net.Appender.SmtpAppender">

   

  <!-- 메일을 받을 곳의 주소 -->

  <to value="to@domain.com" />

   

  <!-- 메일을 보내는 곳의 주소 -->

  <from value="from@domain.com" />

   

  <!-- 제목 -->

  <subject value="test logging message" />

   

  <!-- 메일 서버 정보 -->

  <smtpHost value="SMTPServer.domain.com" />

   

  <!-- 메일을 보낼 때의 버퍼 사이즈 -->

  <bufferSize value="512" />

  <lossy value="true" />

  <evaluator type="log4net.Core.LevelEvaluator">

    <threshold value="WARN"/>

  </evaluator>

   

  <layout type="log4net.Layout.PatternLayout">

    <conversionPattern value="%newline%date [%thread] %-5level %logger - %message%newline" />

  </layout>

</appender>

 

Filter

  • 특정 appender에서만 로그 레벨을 바꾸고 싶은 경우 <appender> 요소 내에서 <filter> 요소를 추가하여 조정할 있다.
  • 로그 레벨을 FATAL 때만 남기고 싶다면 다음과 같이 한다.

 

<filter type="log4net.Filter.LevelRangeFilter">

  <param name="LevelMin" value="FATAL" />

  <param name="LevelMax" value="FATAL" />

</filter>

 

로그 파일 로테이션 시 파일크기+날짜 사용하기

<appender name="debugAppender" type="log4net.Appender.RollingFileAppender">

        <filter type="log4net.Filter.LevelMatchFilter">

            <levelToMatch value="DEBUG" />

        </filter>

        <filter type="log4net.Filter.DenyAllFilter" />

        <File value="Logs\debug.log" />

        <PreserveLogFileNameExtension value="true" />

        <appendToFile value="true" />

        <rollingStyle value="Composite" />

        <maximumFileSize value="32MB" />



로그 파일 이름에 날짜 붙이기

<File value="Logs\perf_.log" />

<staticLogFileName value="false" />

<datePattern value="yyyyMMdd-HHmm" />


perf_201406011140.log 라는 식으로 로그 파일이 만들어진다.



로그 파일 개수 제한

생성된 로그 파일의 최대 개수를 제한 할 수 있다.

<maxSizeRollBackups value="10" />



서식 타입

%literal 리터럴을 표시(「%」을 출력하는 경우 등)

%newline 개행

%logger logger 이름

%class 클래스 이름

%date 실행 시간

%exception 예외 정보

%location 메소드 이름, 행 번호

%line 행 번호

%message 메시지

%method 메소드 이름

%level 로그 레벨

%thread 스레드 ID

%appdomain 실행 파일 이름

%identity 현재 컨텍스트의 유저 이름

(System.Threading.Thread.CurrentPrincipal.Identity.Name 의 값이 설정 되어 있다.)

%username 유저 이름



정보

Log4Net 간단 사용법(Web 포함http://mvcp.tistory.com/404

log4net 사용해 보자 http://nalra999.blog.me/110095620129

[ASP.NET] log4net 설정과 사용법 예제 http://oneprimary.blog.me/30076001926

Apache log4net™ Config Examples   http://logging.apache.org/log4net/release/config-examples.html

신고
by 흥배 2014.01.14 08:00

출처:

http://blog.naver.com/empty_wagon/20151928869

http://blog.naver.com/empty_wagon/20151986599

 

 

사용하는가?

  • Mock 라이브러리는 단위 테스트를 쉽게 하기 위해 존재합니다.
  • 예를 들어서, 클래스 테스트에 데이터베이스를 활용해야 하는 경우가 있다고 가정을 합시다. 경우 직접 데이터베이스에 정보를 넣었다 뺐다 하면 상당히 부담이 됩니다.
  • 이런 부담을 주지 않고, 데이터베이스의 행동을 흉내냄으로써 테스트를 더욱 용이하게 합니다.
  • 하지만 이런 용이함을 얻기 위해서는 인터페이스들에 의해서만 프로그램을 조작하는 설계를 해야 하기는 합니다. (이런 설계가 처음에는 많이 어색하지만, 나중에는 이런 설계가 나은 설계임을 깨닫게 됩니다.)
  • 간단히 이야기해서 Mock 라이브러리는 복잡하고 시간이 걸리는 테스트의 시간을 줄여주기 위해서 존재한다고 있습니다.

 

설치

 

예제

 

public class Book

{

    public string Name { get; set; }

    public int Price { get; set; }

    public string ISBN { get; set; }

}

   

public interface IBookRepository

{

    List<Book> GetBooks();

}

   

public class PriceProcessor

{

    public PriceProcessor(IBookRepository bookRepository)

    {

        bookRepo = bookRepository;

    }

    

    public int GetTotalPrice()

    {

        int result = 0;

               

        foreach (Book b in bookRepo.GetBooks())

        {

            result += b.Price;

        }

    

        return result;

    }

    

    private IBookRepository bookRepo;

}

   

   

[TestFixture]

public class PriceProcessorTests

{

    [Test]

    public void GetTotalPrice_NoDiscount_ReturnCorrectPriceSum()

    {

        List<Book> bookList = new List<Book>()

        {

            new Book() {ISBN="1234", Price=10000, Name="C언어 입문"},

            new Book() {ISBN="1235", Price=20000, Name="C++ 입문"},

            new Book() {ISBN="1236", Price=30000, Name="ASP.NET MVC3 완성"},

        };

    

        Mock<IBookRepository> bookRepoMock = new Mock<IBookRepository>();

        bookRepoMock.Setup(b => b.GetBooks()).Returns(bookList);

    

        PriceProcessor priceProcessor = new PriceProcessor(bookRepoMock.Object);

    

        Assert.That(priceProcessor.GetTotalPrice(), Is.EqualTo(60000));

    }

}

  • bookRepoMock.Setup(b => b.GetBooks()).Returns(bookList);

  • GetBooks함수가 불리면, bookList 반환하라고 명령을 내리고 있습니다. 그리고 완성이 bookRepoMock Object 속성을 이용해서 priceProcessor 넘겨 주고 있습니다

 

함수가 불려졌는지 확인

 

 

public class BestSellers

{

    public BestSellers(IBookRepository bookRepository)

    {

        bookRepo = bookRepository;

    }

    

    public Result AddBook(Book book)

    {

        if (book.Name.Contains("<") || book.Name.Contains(">"))

        {

            return Result.XssAlert;

        }

               

        bookRepo.AddBook(book);

        return Result.Success;

    }

    

    IBookRepository bookRepo;

}

   

public interface IBookRepository

{

    List<Book> GetBooks();

    bool AddBook(Book book);

}

   

   

[Test]

public void AddBook_ContainsTag_IBookRepositoryAddBookIsNotCalled()

{

    Book b = new Book() { Name="<iframe></iframe>"};

    

    Mock<IBookRepository> mock = new Mock<IBookRepository>();

    mock.Setup(r => r.AddBook(b)).Verifiable();

    

    BestSellers bestSellers = new BestSellers(mock.Object);

    

    bestSellers.AddBook(b);

    

    mock.Verify(r => r.AddBook(b), Times.Never());

}

 

Mock 객체에서 생성된 함수들을 매개 변수에 따라 동작을 조절

 

public class Book

{

    public string Name { get; set; }

    public int Price { get; set; }

    public string ISBN { get; set; }

}

   

public void ReducePrice(float reductionRatio)

{

    foreach (Book b in bookRepo.GetBooks())

    {

        b.Price = (int)(reductionRatio * b.Price);

        bookRepo.UpdateBook(b);

    }

}

   

   

[Test]

public void ReducePrice_ValueBetween0And1_IBookRepositoryUpdateBookCalled()

{

    List<Book> bookList = new List<Book>()

    {

        new Book() {ISBN="1234", Price=10000, Name="C언어 입문"},

        new Book() {ISBN="1235", Price=20000, Name="C++ 입문"},

        new Book() {ISBN="1236", Price=30000, Name="ASP.NET MVC3 완성"},

    };

    

    Mock<IBookRepository> bookRepoMock = new Mock<IBookRepository>();

    bookRepoMock.Setup(b => b.GetBooks()).Returns(bookList);

    

    PriceProcessor priceProcessor = new PriceProcessor(bookRepoMock.Object);

    

    priceProcessor.ReducePrice(0.1f);

    

    bookRepoMock.Verify(r => r.UpdateBook(It.IsAny<Book>()),

Times.Exactly(bookList.Count));

}

 

Returs, Callback, Throws

 

public Result AddBook(Book book)

{

    if (book.Name.Contains("<") || book.Name.Contains(">"))

    {

        return Result.XssAlert;

    }

               

    bool added = bookRepo.AddBook(book);

    if (added == false)

    {

        return Result.AlreadyExist;

    }

    return Result.Success;

}

   

   

[Test]

public void AddBook_AddSameBookAgain_ReturnsAlreadyExist()

{

    Book b = new Book() { ISBN = "1234", Name = "abc" };

    

    int count = 0;

    Mock<IBookRepository> mock = new Mock<IBookRepository>();

    mock.Setup(r => r.AddBook(b)).Callback(() => count++).Returns(() => count <= 1);

    

    BestSellers bestSellers = new BestSellers(mock.Object);

    

    Result result1 = bestSellers.AddBook(b);

    Result result2 = bestSellers.AddBook(b);

    

    Assert.That(result1, Is.EqualTo(Result.Success));

    Assert.That(result2, Is.EqualTo(Result.AlreadyExist));

}

 

참고

  • Moq活用して.NETでモックを使ったテストを

 

 

 

 

신고
by 흥배 2014.01.08 08:00
| 1 2 |