문제
주어진 클래스에 대해 추적 기능을 갖고 싶습니다. 즉, 모든 메서드 호출 (메서드 서명 및 실제 매개 변수 값)과 모든 메서드 종료 (메서드 서명 만)를 기록하고 싶습니다.
다음을 가정하여이를 수행하는 방법 :
- 타사를 사용하고 싶지 않습니다. C # 용 AOP 라이브러리,
- 추적하려는 모든 메소드에 중복 코드를 추가하고 싶지 않습니다.
- 클래스의 공용 API를 변경하고 싶지 않습니다. 클래스의 사용자는 모든 메서드를 똑같은 방식으로 호출 할 수 있어야합니다.
질문을 더 구체적으로 만들기 위해 다음과 같은 3 가지 클래스가 있다고 가정 해 보겠습니다. 라코 디스
Method1 및 Method2 에 대한 모든 호출에 대해 Logger.LogStart 및 Logger.LogEnd 를 호출하려면 Caller.Call 메서드를 수정하고 Traced.Method1 및 Traced.Method2 에 대한 호출을 명시 적으로 추가하지 않습니까?
편집 : Call 메서드를 약간 변경하면 해결 방법은 무엇입니까?
해결책
C #은 AOP 지향 언어가 아닙니다. 일부 AOP 기능이 있고 다른 일부를 에뮬레이션 할 수 있지만 C #으로 AOP를 만드는 것은 고통 스럽습니다.
원하는 일을 정확히 수행 할 수있는 방법을 찾았지만 쉽게 할 수있는 방법이 없습니다.
이해 한대로 다음과 같이하세요. 라코 디스
이를 위해 두 가지 주요 옵션이 있습니다.
-
MarshalByRefObject 또는 ContextBoundObject에서 클래스를 상속하고 IMessageSink에서 상속되는 속성을 정의합니다. 이 기사 에 좋은 예가 있습니다. 그럼에도 불구하고 MarshalByRefObject를 사용하면 성능이 지옥처럼 떨어질 것이라는 점을 고려해야합니다. 즉, 10 배 성능 손실에 대해 이야기하고 있으므로 시도하기 전에 신중하게 생각하십시오.
-
다른 옵션은 코드를 직접 삽입하는 것입니다. 런타임에서 모든 클래스를 "읽기"위해 리플렉션을 사용하고 해당 속성을 가져오고 적절한 호출을 주입해야합니다 (이 문제에 대해 Reflection.Emit이 생각하는 것처럼 Reflection.Emit 메서드를 사용할 수 없다고 생각합니다. 이미 존재하는 메서드 안에 새 코드를 삽입 할 수 없습니다). 디자인 타임에 이것은 CLR 컴파일러에 대한 확장을 생성하는 것을 의미하며, 이것이 어떻게 수행되는지 솔직히 알 수 없습니다.
마지막 옵션은 IoC 프레임 워크 를 사용하는 것입니다. 대부분의 IoC 프레임 워크는 메소드를 연결할 수있는 진입 점을 정의하여 작동하므로 완벽한 솔루션이 아닐 수도 있지만 달성하려는 항목에 따라 공정한 근사치가 될 수 있습니다.
다른 팁
가장 간단한 방법은 PostSharp 를 사용하는 것입니다.적용하는 속성을 기반으로 메서드 내부에 코드를 삽입합니다.원하는 것을 정확하게 할 수 있습니다.
또 다른 옵션은 프로파일 링 API 를 사용하는 것입니다.메서드 내부에 코드를 삽입하기는하지만 정말 어렵습니다.
IDisposable 인터페이스를 구현하는 클래스를 Tracing이라고 부르면 모든 메서드 본문을 하나의 라코 디스 Tracing 클래스에서 Tracing 클래스의 생성자 / Dispose 메서드에서 각각 추적 논리를 처리하여 메서드의 시작 및 종료를 추적 할 수 있습니다.다음과 같습니다. 라코 디스
Castle Windsor 와 같은 DI 컨테이너의> 인터 셉션 기능. 실제로 특정 속성으로 장식 된 메서드가있는 모든 클래스를 가로 채도록 컨테이너를 구성 할 수 있습니다.
3 번 포인트와 관련하여 OP는 AOP 프레임 워크가없는 솔루션을 요청했습니다. 다음 답변에서 피해야 할 것은 Aspect, JointPoint, PointCut 등이라고 가정했습니다. CastleWindsor의 인터 셉션 문서 , 요청 된 작업을 수행하는 데 필요한 문서는 없습니다.
속성의 존재에 따라 인터셉터의 일반 등록 구성 : 라코 디스
생성 된 IContributeComponentModelConstruction을 컨테이너에 추가 라코 디스
그리고 인터셉터 자체에서 원하는 것은 무엇이든 할 수 있습니다 라코 디스
기록 할 방법에 로깅 속성 추가 라코 디스
클래스의 일부 메소드 만 인터셉트해야하는 경우 속성의 일부 처리가 필요합니다. 기본적으로 모든 공개 메소드가 차단됩니다.
이것 좀보세요-꽤 무거운 물건 .. http://msdn.microsoft.com/en-us/magazine/cc164165.aspx
Essential .net-don box에는 Interception이라고하는 필요한 사항에 대한 장이 있습니다. 여기에서 일부를 긁어 냈습니다 (글꼴 색상에 대해 죄송합니다-그 당시에는 어두운 테마가있었습니다 ...) http://madcoderspeak.blogspot.com/2005/09/essential-interception-using-contexts.html
더 쉬울 수있는 다른 방법을 찾았습니다 ...
메소드 InvokeMethod 선언 라코 디스
그런 다음 방법을 이렇게 정의합니다 라코 디스
이제 종속성 주입없이 런타임에 확인할 수 있습니다 ...
사이트에 문제 없음 :)
이것이 AOP 프레임 워크보다 덜 중요하거나 MarshalByRefObject에서 파생되거나 원격 또는 프록시 클래스를 사용하는 것에 동의 할 것입니다.
먼저 인터페이스를 구현하도록 클래스를 수정해야합니다 (MarshalByRefObject를 구현하는 대신). 라코 디스
다음에는 데코 레이팅 된 객체에 대한 호출을 가로 챌 수 있도록 인터페이스를 데코 레이팅하려면 RealProxy를 기반으로하는 일반 래퍼 객체가 필요합니다. 라코 디스
이제 ITraced의 Method1 및 Method2에 대한 호출을 가로 챌 준비가되었습니다. 라코 디스
제한없이 메서드를 추적하고 싶다면 (코드 적응, AOP 프레임 워크, 중복 코드 없음) 마법이 필요합니다 ...
진심으로 런타임에 작동하는 AOP 프레임 워크를 구현하도록 해결했습니다.
여기에서 찾을 수 있습니다. NConcern .NET AOP Framework
나는 이러한 종류의 요구에 대응하기 위해이 AOP 프레임 워크를 만들기로 결정했습니다. 매우 가벼운 간단한 라이브러리입니다. 홈 페이지에서 로거의 예를 볼 수 있습니다.
타사 어셈블리를 사용하지 않으려면 코드 소스 (오픈 소스)를 탐색하고 두 파일을 모두 복사 할 수 있습니다. Aspect.Directory.cs 및 Aspect.Directory.Entry.cs 를 원하는대로 조정합니다. 이러한 클래스를 사용하면 런타임에 메서드를 대체 할 수 있습니다. 나는 당신에게 라이센스를 존중 해달라고 부탁합니다.
필요한 것을 찾거나 마침내 AOP 프레임 워크를 사용하도록 설득하기를 바랍니다.
CodePlex에서 오픈 소스 프레임 워크 CInject 를 사용할 수 있습니다.최소한의 코드를 작성하여 Injector를 만들고 CInject로 모든 코드를 빠르게 가로 챌 수 있습니다.또한 이것은 오픈 소스이기 때문에 이것을 확장 할 수도 있습니다.
또는 차단에서이 문서에 언급 된 단계를 따를 수 있습니다.IL을 사용한 메서드 호출 및 C #의 Reflection.Emit 클래스를 사용하여 자체 인터셉터를 만듭니다.
해결 방법은 모르지만 접근 방식은 다음과 같습니다.
사용자 정의 속성으로 클래스 (또는 해당 메서드)를 장식합니다.프로그램의 다른 어딘가에서 초기화 함수가 모든 유형을 반영하도록하고, 속성으로 장식 된 메소드를 읽고 메소드에 IL 코드를 삽입하십시오.실제로 LogStart
, 실제 메서드 및 LogEnd
를 호출하는 스텁으로 메서드를 바꾸는 더 실용적 일 수 있습니다.또한 리플렉션을 사용하여 메서드를 변경할 수 있는지 모르겠으므로 전체 유형을 대체하는 것이 더 실용적 일 수 있습니다.
잠재적으로 GOF 데코레이터 패턴을 사용하고 추적이 필요한 모든 클래스를 '장식'할 수 있습니다.
아마도 IOC 컨테이너에서만 실제로 실용적 일 수 있습니다 (하지만 앞서 언급 한 포인터로서 IOC 경로를 따라 내려 가려는 경우 메소드 인터 셉션을 고려할 수 있습니다).
Ayende가 어떻게했는지에 대한 답변을 얻기 위해 Ayende를 괴롭혀야합니다. http://ayende.com/Blog / archive / 2009 / 11 / 19 / can-you-hack-this-out.aspx
AOP는 깔끔한 코드 구현을 위해 반드시 필요하지만 C #으로 블록을 둘러싸고 싶다면 제네릭 메서드를 비교적 쉽게 사용할 수 있습니다.(인텔리 감각과 강력한 형식의 코드 포함) 확실히 AOP의 대안이 될 수는 없습니다.
PostSHarp 에는 약간의 버그 문제가 있지만 (저는프로덕션에서) 좋은 것입니다.
일반 래퍼 클래스, 라코 디스
사용법은 다음과 같을 수 있습니다 (물론 인텔리 감각으로) 라코 디스
- 자신의 AOP 라이브러리를 작성합니다.
- 반사를 사용하여 인스턴스에 로깅 프록시를 생성합니다 (기존 코드의 일부를 변경하지 않고 수행 할 수 있는지 확실하지 않음).
- 어셈블리를 다시 작성하고 로깅 코드를 삽입합니다 (기본적으로 1과 동일).
- CLR을 호스팅하고이 수준에서 로깅을 추가합니다 (CLR에 필요한 후크가 있는지 확실하지 않지만 구현하기 가장 어려운 솔루션이라고 생각합니다).
'nameof'가 출시 된 C # 6 이전에 할 수있는 최선의 방법은 느린 StackTrace 및 linq 표현식을 사용하는 것입니다.
예 :그러한 방법을 위해 라코 디스
이러한 줄이 로그 파일에 생성 될 수 있습니다. 라코 디스
구현은 다음과 같습니다. 라코 디스