DispatchProxy를 사용하여 C#의 인터페이스로 모든 클래스 프록시
10445 단어 proxydotnetcsharpprogramming
NHibernate
~ PostgreSQL
를 통해 수행되는 성능 테스트 데이터베이스 호출 방법을 알아내는 임무를 받았습니다. 내가 찾을 수 있는 한 NHibernate
는 SQL
및 각 쿼리의 실행 시간을 가로채서 수동으로 기록하는 정말 쉬운 방법을 제공하지 않습니다.조금 더 파고든 후 사용자 정의 또는 타사 라이브러리의 모든 인터페이스를 쉽게 프록시할 수 있는 덜 알려진 편리한 도구를 발견했습니다. 이 도구는
System.Reflection.DispatchProxy
nuget 패키지에 있습니다. 약간의 코드로 인터페이스를 구현하는 클래스의 모든 인스턴스를 프록시로 래핑하여 기록할 뿐만 아니라 인수와 반환된 데이터를 모두 조작할 수 있습니다.예시
이 간단한 클래스와 인터페이스를 사용하십시오.
interface IHello
{
bool SayHello(string name);
}
class Hello : IHello
{
public bool SayHello(string name)
{
Console.WriteLine($"Hello {name}");
return true;
}
}
특별한 것은 없고 단순한 이름 매개변수를 사용하는 하나의 메소드만 노출되어 콘솔에 문자열을 쓰고 성공을 나타내는 bool을 반환합니다.
이 예에서는 고안되었지만
SayHello
로 가는 통화를 녹음하고 싶었습니다.class HelloDispatchProxy<T> : DispatchProxy where T : class, IHello
{
private IHello Target { get; set; }
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
// Code here to track time, log call etc.
var result = targetMethod.Invoke(Target, args);
return result;
}
public static T CreateProxy(T target)
{
var proxy = Create<T, HelloDispatchProxy<T>>() as HelloDispatchProxy<T>;
proxy.Target = target;
return proxy as T;
}
}
이것은 우리의
DispatchProxy
, HelloDispatchProxy
입니다. 사용법은 꽤 간단합니다.IHello hello = HelloDispatchProxy<IHello>.CreateProxy(new Hello());
CreateProxy
메서드는 기본 추상 클래스에서 Create<T>
를 호출하여 프록시 개체를 생성합니다. 그런 다음 대상 인스턴스는 Target
속성에 할당된 후 프록시 캐스트를 T
(이 인스턴스에서는 IHello
) 유형으로 반환합니다.그러면 프록시된
IHello
인스턴스에서 호출되는 모든 메서드가 Invoke()
트리거됩니다. 대상 메서드를 호출하고 매개 변수를 전달하고 결과를 반환하는 것은 프록시의 책임입니다. 이 방법에서는 원하는 모든 작업을 수행할 수 있으며 원본Target
인스턴스에도 액세스할 수 있습니다.내GitHub에서 이에 대한 완전한 예를 볼 수 있습니다.
With great power comes great responsibility
이렇게 하면 많은 제어가 가능하므로
args
및 반환된 결과로 수행하는 작업에 주의하십시오. 그렇지 않으면 예기치 않은 동작이 발생할 수 있습니다.성능 시험
DispatchProxy
는 System.Reflection
네임스페이스 아래에 있습니다. 이 때문에 이 접근 방식에서 어떤 오버헤드가 발생하는지 궁금했습니다. 직접 테스트하는 것보다 더 나은 이해 방법은 무엇입니까?프록시되지 않은 인스턴스와 프록시된 인스턴스 모두에 대한 호출 간의 원시 비교를 제공하기 위해 slightly over-engineered set of tests을 설정했습니다. 결과를 분석하기 전에 평균을 얻기 위해 여러 번 실행했습니다.
100000 W/O 100000 W % Increase
2233.023 2382.8835 6.71%
2235.5306 2398.1216 7.27%
2236.2449 2386.4416 6.72%
2236.8529 2389.209 6.81%
2239.9692 2402.4627 7.25%
2227.0108 2367.0306 6.29%
2237.8432 2384.8429 6.57%
2233.5528 2392.8394 7.13%
2238.411 2398.3124 7.14%
2235.7398 2388.134 6.82%
2262.3454 2380.0595 5.20%
2340.0243 2385.0553 1.92%
2249.3693 2383.6925 5.97%
2246.9835 2381.507 5.99%
2240.7711 2381.4675 6.28%
2232.5296 2374.0465 6.34%
2248.0095 2359.8636 4.98%
2250.2518 2394.8422 6.43%
2234.7757 2387.8114 6.85%
2238.7645 2387.8047 6.66%
0.00112245 0.001192661 6.27%
따라서 평균적으로 프록시만 도입하면 실행에 6% 정도가 추가로 소요되었습니다. 이 현재 상태에서 프록시는 전혀 유용한 작업을 수행하지 않지만 컨텍스트에 효율성을 부여하는 데 도움이 됩니다.
그러나 프록시에 차단 콘솔 쓰기 라인과 같이 느린 것을 추가하면 % 증가가 6.27%에서 100% 이상으로 치솟습니다.
System.Console.WriteLine($"Going to call {targetMethod.Name}");
성능에 미치는 영향에서 가장 중요한 부분은
Invoke
메서드에서 무엇을 하기로 결정하느냐 입니다.성능이 가장 중요한 응용 프로그램을 제외한 모든 응용 프로그램에서 프록시 자체의 오버헤드는 미미합니다. 예를 들어 HTTP 웹 요청 또는 메시지 처리기와 같은 컨텍스트에서 2-3개의 DB 호출을 프록시하는 경우 영향은 무시할 수 있습니다. 사실, 실제 응용 프로그램을 사용한 테스트에서 숫자는 APM 도구를 추적할 때도 허용 가능한 오차 범위 내에 있습니다.
최대 절전 모드 및 PostgreSQL
저처럼
NHibernate
에서 정보를 수집하는 데 특히 관심이 있다면 사용자 지정 드라이버 구현에서 IDBCommand
를 래핑하여 이 작업을 수행할 수 있습니다.public class ProfiledNpgsqlDriver : NpgsqlDriver {
public override IDbCommand CreateCommand() {
var command = base.CreateCommand();
if (ConfigurationManager.AppSettings["EnableDBTracing"]?.ToLower() == "true") {
command = ProfiledDbCommandDispatchProxy<IDbCommand>.CreateProxy(command);
}
return command;
}
}
대상
IDBCommand
의 Command
속성 및 기록 시간 등을 StopWatch
로 실행 중인 명령에 접근할 수 있습니다. 한 단계 더 나아가 프록시를 IObservable<T>
으로 구현하여 유스케이스에 따라 다르게 처리할 수 있도록 했습니다.마무리
바라건대, 당신이 이것을 흥미롭게 찾았습니다. 이에 대한 좋은 사용 사례가 생각나거나 의견이 있습니까? 듣고 싶습니다 ❤
Reference
이 문제에 관하여(DispatchProxy를 사용하여 C#의 인터페이스로 모든 클래스 프록시), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/nullabletype/proxy-any-class-by-interface-in-c-with-dispatchproxy-2i6a텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)