C\#를 이용 하여 AOP 에서 흔히 볼 수 있 는 몇 가지 방법 에 대한 상세 한 설명 을 실현 합 니 다.

머리말
AOP 는 Aspect Oriented Programming 의 줄 임 말로 절단면 프로 그래 밍 을 위 한 사전 컴 파일 방식 과 런 타임 동적 대 리 를 통 해 프로그램 기능 을 실현 하 는 중 업무 논 리 를 통일 적 으로 처리 하 는 기술 로 흔히 볼 수 있 는 장면 은 로그 기록,오류 캡 처,성능 모니터링 등 이다.
AOP 의 본질은 대리 대상 을 통 해 실제 대상 을 간접 적 으로 집행 하 는 것 이다.대리 류 에 다음 과 같은 추가 업무 코드 를 추가 하 는 경우 가 많다.

 class RealA
 {
 public virtual string Pro { get; set; }
 
 public virtual void ShowHello(string name)
 {
 Console.WriteLine($"Hello!{name},Welcome!");
 }
 }
 
 
//  :
 
 var a = new RealA();
 a.Pro = "  ";
 a.ShowHello("    ");
이 코드 는 매우 간단 합 니 다.단지 NEW 의 대상 일 뿐 입 니 다.그리고 속성 과 호출 방법 을 설정 합 니 다.그러나 속성 을 설정 한 전후 와 호출 방법 전후 또는 오 류 를 보고 하면 로그 정 보 를 수집 할 수 있 습 니 다.어떻게 해 야 합 니까?속성 설정 및 호출 방법 전후 에 로 그 를 기록 하 는 코드 를 추가 하면 되 지 않 을 까 생각 하 실 수 있 습 니 다.그럼 에 도 불구 하고 여러 곳 에서 이 종 류 를 사용 해 야 할 때 중복 되 는 코드 가 너무 많은 지 알 수 있 습 니 다.따라서 우 리 는 프 록 시 모드 나 장식 모드 를 사용 하여 기 존의 실제 클래스 인 RealA 를 프 록 시 리 얼 A 에 의뢰 하여 실행 해 야 합 니 다.프 록 시 클래스 에서 속성 과 호출 방법 을 설정 할 때 로 그 를 기록 하 는 코드 를 추가 하면 됩 니 다.이렇게 하면 코드 의 깨끗 하고 깨끗 하 며 코드 의 후기 유지 에 도 편리 합 니 다.(C\#에서 이불 클래스 를 다시 쓰 려 면 부모 클래스 는 가상 방법 이나 가상 속성 virtual 이 어야 합 니 다)
다음 코드:

class ProxyRealA : RealA
 {
 
 public override string Pro
 {
 get
 {
 return base.Pro;
 }
 set
 {
 ShowLog("  Pro       ");
 base.Pro = value;
 ShowLog($"  Pro       :{value}");
 }
 }
 
 public override void ShowHello(string name)
 {
 try
 {
 ShowLog("ShowHello       ");
 base.ShowHello(name);
 ShowLog("ShowHello       ");
 }
 catch(Exception ex)
 {
 ShowLog($"ShowHello        :{ex.Message}");
 }
 }
 
 private void ShowLog(string log)
 {
 Console.WriteLine($"{DateTime.Now.ToString()}-{log}");
 }
 }
 
 
//  :
 var aa = new ProxyRealA();
 aa.Pro = "  2";
 aa.ShowHello("zuowenjun.cn");
이 코드 역시 매우 간단 하 다.바로 프 록 시 리 얼 A 가 리 얼 A 류 를 계승 하면 프 록 시 리 얼 A 대리 리 얼 A 로 볼 수 있 고 프 록 시 리 얼 A 가 각종 속성 과 방법 을 제공 하여 호출 할 수 있다.이렇게 하면 ProxyRealA 류 내부 속성 및 방법 집행 전후 에 로 그 를 통일 적 으로 기록 하 는 코드 가 있 습 니 다.어디서 든 이 RealA 류 를 사용 하면 ProxyRealA 류 로 직접 대체 할 수 있 습 니 다.리 씨 교체 원칙 으로 인해 부 류 는 하위 클래스 로 교체 할 수 있 습 니 다.그리고 나중에 로그 기록 코드 방식 을 변경 하려 면 ProxyRealA 에서 만 변경 하면 됩 니 다.이렇게 하면 모든 프 록 시 리 얼 A 류 의 로그 가 바 뀌 어 시원 하지 않 습 니까?
상기 집행 결 과 는 다음 과 같다.

이상 은 프 록 시 클래스 를 정의 하 는 방식 으로 방법 에서 각종 실행 점 의 차단 코드 논리 처 리 를 통일 적 으로 할 수 있 습 니 다.차단 점(또는 횡단면,절단면 점)은 보통 실행 전,실행 후 오류 가 발생 합 니 다.전에 실제 클래스 인 RealA 를 직접 사용 할 때 각종 논리 코드 를 반복 적 으로 추가 해 야 하 는 문 제 를 해결 하 였 지만,그러나 이에 따 른 새로운 문제 가 또 생 겼 다.그것 은 시스템 에 클래스 가 매우 많 을 때 우리 가 모든 클래스 에 대해 하나의 에이전트 클래스 를 정의 하면 시스템 의 클래스 의 개수 가 배로 증가 하고 서로 다른 에이전트 클래스 에서 일부 차단 업무 논리 코드 가 똑 같 을 수 있다 는 것 이다.이런 상황 도 허용 할 수 없 는 것 이다.그러면 좋 은 방법 이 없 을 까?답 은 긍정 적 입 니 다.다음은 제 가 인터넷 자원 과 개인 정 리 를 결합 한 다음 과 같은 몇 가지 흔히 볼 수 있 는 AOP 를 실현 하 는 방식 입 니 다.여러분 은 참고 하여 공부 하 실 수 있 습 니 다.
첫 번 째:정적 삽입,즉 컴 파일 할 때 AOP 차단 과 관련 된 각종 코드 를 일정한 규칙 에 부합 되 는 클래스 에 주입 합 니 다.컴 파일 된 코드 는 우리 가 RealA 호출 속성 이나 방법 전후 에 코드 를 추가 하 는 것 과 같 습 니 다.다만 이 작업 은 컴 파일 러 에 의 해 이 루어 집 니 다.
PostSharp:PostSharp 의 Aspect 는 Attribute 를 사용 하여 이 루어 집 니 다.우 리 는 OnMethod Boundary Aspect 를 계승 한 다음 에 몇 가지 흔히 볼 수 있 는 방법 을 다시 쓰 면 됩 니 다.예 를 들 어 OnEntry,OnExit 등 은 마지막 으로 AOP 차단 이 필요 한 속성 이나 방법 에 AOP 차단 특성 류 를 추가 하면 됩 니 다.PostSharp 는 정적 으로 짜 여 져 있 기 때문에 다른 반사 나 EMIT 반 사 를 통 해 효율 이 가장 높 지만 PostSharp 는 유 료 버 전 이 고 인터넷 의 튜 토리 얼 이 비교적 많 기 때문에 저 는 여기 서 중복 설명 하지 않 겠 습 니 다.
두 번 째:EMIT 반사,즉:Emit 반사 동적 생 성 에이전트 클래스 를 통 해 다음 과 같 습 니 다Castle.DynamicProxyAOP 실현 방식 은 코드 도 비교적 간단 합 니 다.효율 은 첫 번 째 보다 느 리 지만 일반적인 반사 에 있어 서 는 높 습 니 다.코드 는 다음 과 같 습 니 다.

using Castle.Core.Interceptor;
using Castle.DynamicProxy;
using NLog;
using NLog.Config;
using NLog.Win32.Targets;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace ConsoleApp
{
 class Program
 {
 static void Main(string[] args)
 {
 ProxyGenerator generator = new ProxyGenerator();
 var test = generator.CreateClassProxy<TestA>(new TestInterceptor());
 Console.WriteLine($"GetResult:{test.GetResult(Console.ReadLine())}");
 test.GetResult2("test");
 Console.ReadKey();
 }
 }
 
 public class TestInterceptor : StandardInterceptor
 {
 private static NLog.Logger logger;
 
 protected override void PreProceed(IInvocation invocation)
 {
 Console.WriteLine(invocation.Method.Name + "   ,  :" + string.Join(",", invocation.Arguments));
 }
 
 protected override void PerformProceed(IInvocation invocation)
 {
 Console.WriteLine(invocation.Method.Name + "   ");
 try
 {
 base.PerformProceed(invocation);
 }
 catch (Exception ex)
 {
 HandleException(ex);
 }
 }
 
 protected override void PostProceed(IInvocation invocation)
 {
 Console.WriteLine(invocation.Method.Name + "   ,   :" + invocation.ReturnValue);
 }
 
 private void HandleException(Exception ex)
 {
 if (logger == null)
 {
 LoggingConfiguration config = new LoggingConfiguration();
 
 ColoredConsoleTarget consoleTarget = new ColoredConsoleTarget();
 consoleTarget.Layout = "${date:format=HH\\:MM\\:ss} ${logger} ${message}";
 config.AddTarget("console", consoleTarget);
 
 LoggingRule rule1 = new LoggingRule("*", LogLevel.Debug, consoleTarget);
 config.LoggingRules.Add(rule1);
 LogManager.Configuration = config;
 
 logger = LogManager.GetCurrentClassLogger(); //new NLog.LogFactory().GetCurrentClassLogger();
 }
 logger.ErrorException("error",ex);
 }
 }
 
 public class TestA
 {
 public virtual string GetResult(string msg)
 {
 string str = $"{DateTime.Now.ToString("yyyy-mm-dd HH:mm:ss")}---{msg}";
 return str;
 }
 
 public virtual string GetResult2(string msg)
 {
 throw new Exception("throw Exception!");
 }
 }
}
코드 원 리 를 간략하게 설명 하고 프 록 시 Generator 클래스 인 스 턴 스 를 만 듭 니 다.이름 에서 알 수 있 듯 이 프 록 시 생 성기 입 니 다.그 다음 에 StandardInterceptor 를 계승 하 는 TestInterceptor 를 예화 합 니 다.이 TestInterceptor 는 사용자 정의 차단기 입 니 다.마지막 으로generator.CreateClassProxy<TestA>(new TestInterceptor())동적 으로 TestA 를 계승 하 는 동적 프 록 시 클래스 를 만 들 었 습 니 다.이 프 록 시 클래스 는 실행 할 때 만 생 성 됩 니 다.그 다음 에 코드 와 같이 동적 프 록 시 대상 인 스 턴 스 Test 로 TestA 의 모든 속성 과 방법 을 직접 조작 할 수 있 습 니 다.물론 여기 서 주의해 야 합 니 다.동적 프 록 시 클래스 에 의 해 대리 되 고 차단 되 어야 한다 면 부모 클래스 의 속성 이나 방법 은 virtual 이 어야 합 니 다.이 점 은 제 가 위 에서 말 한 프 록 시 클래스 와 같 습 니 다.
상기 코드 실행 효 과 는 다음 과 같 습 니 다.

세 번 째:일반 반사+Remoting 의 원 격 방문 대상 을 이용 할 때 직 실 대리 류 를 실현 합 니 다.코드 는 다음 과 같 습 니 다.이것 은 상기 두 가지 에 비해 약간 복잡 할 수 있 습 니 다.
이상 코드 구현 절차 설명:
1.여기 서 정 의 된 실제 클래스 인 AopClass 는 ContextBoundObject 클래스 에서 계승 해 야 합 니 다.ContextBoundObject 클래스 는 Marshalby RefObject 클래스 에서 직접 계승 되 어 컨 텍스트 바 인 딩 대상 임 을 나타 내 고 원 격 처 리 를 지원 하 는 응용 프로그램 에서 응용 프로그램 도 메 인 경계 방문 대상 을 뛰 어 넘 을 수 있 습 니 다.말하자면 이 실제 클래스 의 모든 정 보 를 얻 을 수 있 습 니 다.동적 에이전트 가 생 성 될 수 있 도록 합 니 다.
2.프 록 시 Attribute 에서 계승 하 는 프 록 시 특성 표지 류 AopAttribute 를 정의 하여 어떤 종류 가 대 리 될 수 있 는 지 를 나타 내 는 동시에 Create Instance 방법 을 다시 쓰 는 것 에 주의 하고 Create Instance 방법 에서 투명 프 록 시 클래스 를 위탁 하고 생 성 하 는 과정 을 실현 합 니 다realProxy.GetTransparentProxy() 매우 중요 합 니 다.목 표 는 정 의 된 AopProxy 프 록 시 클래스 에 따라 투명 프 록 시 클래스 를 생 성 하 는 인 스 턴 스 를 얻 는 것 입 니 다.
3.일반적인 AopProxy 프 록 시 클래스 를 실현 하려 면 프 록 시 클래스 는 RealProxy 클래스 에서 계승 해 야 합 니 다.이 프 록 시 클래스 에서 Invoke 방법 을 다시 작성 합 니 다.이 방법 은 프 록 시 된 실제 클래스 의 모든 방법,속성,필드 의 출입 구 를 통일 적 으로 실행 하 는 것 입 니 다.우 리 는 이 방법 에서 전 송 된 IMessage 에 따라 판단 하고 해당 하 는 차단 코드 를 실현 하면 됩 니 다.
4.마지막 으로 Aop 차단 이 필요 한 클래스 에 AopAttribute 를 표시 하면 됩 니 다(주의:표 지 된 클래스 는 제1 조 에서 설명 한 계승 은 ContextBoundObject 류 에서 이 루어 져 야 함).실제 호출 과정 에서 어떠한 변화 도 감지 하지 못 합 니 다.또한 AopAttribute 는 하위 클래스 에 의 해 계승 되 고 모든 하위 클래스 가 대리 되 고 차단 된다 는 것 을 의미한다.
위의 코드 실행 효 과 는 다음 과 같 습 니 다.

여기 서 마이크로소프트 공식 이 RealProxy 류 를 이용 하여 AOP 를 실현 하면 주 소 를 참조 하 십시오.https://msdn.microsoft.com/zh-cn/library/dn574804.aspx
네 번 째:반사+통 일 된 출입 구 를 정의 하고 일부 특성 을 활용 하여 AOP 의 효 과 를 실현 합 니 다.예 를 들 어 흔히 볼 수 있 는 MVC,WEB API 의 필터 특성 입 니 다.저 는 MVC 의 사고 에 따라 유사 한 MVC 필터 의 AOP 효 과 를 실 현 했 습 니 다.중간 에 반 사 를 사 용 했 을 뿐 가능성 이 좋 지 않 지만 효 과 는 여러 가지 차단 을 성공 적 으로 실 현 했 습 니 다.마치 MVC 와 같이.필터 기능 도 지원 하고 Controller 의 Action 실행 전,실행 후,오류 등 방법 으로 차단 할 수 있 습 니 다.
실현 방향 은 다음 과 같다.
A.필터 및 컨트롤 러 의 특정 방법 차단 실현 원리:
1.가 져 오기 프로그램 은 모든 계승 컨트롤 러 의 유형 을 집중 합 니 다.
2.Controller 의 이름 에 따라 첫 번 째 단계 에 대응 하 는 Controller 의 종 류 를 찾 습 니 다:FindController Type
3.찾 은 Controller 유형 및 Action 이름 에 따라 해당 하 는 방법 찾기:FindAction
4.Controller 형식의 인 스 턴 스 만 들 기;
5.Action 방법 에 따라 방법 에 정 의 된 모든 필터 특성 을 찾 습 니 다(실행 전,실행 후,오류 포함)
6.Controller 의 OnAction Executing 방법 을 실행 한 다음 실행 전 필터 특성 목록 을 실행 합 니 다.예 를 들 어 Action Executing Filter
7.액 션 방법 을 실행 하여 결 과 를 얻는다.
8.Controller 의 OnAction Executed 방법 을 실행 한 다음 실행 후의 필터 특성 목록 을 실행 합 니 다.예 를 들 어 Action Executed Filter
9.try catch 를 통 해 catch 에서 Controller 의 OnAction Error 방법 을 실행 한 다음 에 오류 필터 특성 목록 을 실행 합 니 다.예 를 들 어 Action Error Filter
10.마지막 으로 결 과 를 되 돌려 줍 니 다.
B.실행 경로 설정 효과 원리:
1.경로 템 플 릿 목록 을 설정 할 수 있 는 방법 을 추가 합 니 다.AddExecRouteTemplate,방법 에서 contrller,action 을 검증 하고 템 플 릿 의 자리 표시 자 배열 을 가 져 옵 니 다.마지막 으로 클래스 전역 대상 에 routeTemplates 를 저장 합 니 다.
2.실행 경로 에 따라 대응 하 는 Controller 의 Action 방법 을 실행 하 는 효 과 를 증가 합 니 다.Run 은 이 방법 에서 모든 경로 템 플 릿 을 옮 겨 다 닌 다음 에 실 행 된 요청 경로 정보 와 정규 로 일치 합 니 다.OK 와 일치 하면 Controller 와 Action 을 정확하게 찾 을 수 있 으 면 정확 한 설명 을 하고 최종 적 으로 통일 적 으로 호출 합 니 다.Process 방법,A 의 모든 절 차 를 실행 하고 결 과 를 되 돌려 줍 니 다.
이 시 뮬 레이 션 MVC 방안 이 Action 방법 파라미터 의 연결 기능 을 실현 하지 못 했다 는 것 을 설명해 야 한다.ModelBinding 자체 가 비교적 복잡 한 메커니즘 이기 때문에 여 기 는 AOP 의 실현 원 리 를 알 기 위해 서 이 분야 의 연 구 를 하지 않 는 다.여러분 이 시간 이 있 으 면 MVC 를 실현 할 수 있다.최종 적 으로 MVC 는 ASP.NET MVC 뿐만 아니 라 Console MVC,심지어 Winform MVC 등 도 실현 할 수 있다.
다음은 실 현 된 모든 코드 입 니 다.코드 에서 저 는 기본 적 인 최적화 를 했 기 때문에 직접 사용 할 수 있 습 니 다.

public abstract class Controller
{
 public virtual void OnActionExecuting(MethodInfo action)
 {
 
 }
 
 public virtual void OnActionExecuted(MethodInfo action)
 {
 
 }
 
 public virtual void OnActionError(MethodInfo action, Exception ex)
 {
 
 }
 
}
 
public abstract class FilterAttribute : Attribute
{
 public abstract string FilterType { get; }
 public abstract void Execute(Controller ctrller, object extData);
}
 
public class ActionExecutingFilter : FilterAttribute
{
 public override string FilterType => "BEFORE";
 
 public override void Execute(Controller ctrller, object extData)
 {
 Console.WriteLine($"   {ctrller.GetType().Name}.ActionExecutingFilter        !-{DateTime.Now.ToString()}");
 }
}
 
public class ActionExecutedFilter : FilterAttribute
{
 public override string FilterType => "AFTER";
 
 public override void Execute(Controller ctrller, object extData)
 {
 Console.WriteLine($"   {ctrller.GetType().Name}.ActionExecutedFilter        !-{DateTime.Now.ToString()}");
 }
}
 
public class ActionErrorFilter : FilterAttribute
{
 public override string FilterType => "EXCEPTION";
 
 public override void Execute(Controller ctrller, object extData)
 {
 Console.WriteLine($"   {ctrller.GetType().Name}.ActionErrorFilter        !-{DateTime.Now.ToString()}-Error Msg:{(extData as Exception).Message}");
 }
}
 
public class AppContext
{
 private static readonly Type ControllerType = typeof(Controller);
 private static readonly Dictionary<string, Type> matchedControllerTypes = new Dictionary<string, Type>();
 private static readonly Dictionary<string, MethodInfo> matchedControllerActions = new Dictionary<string, MethodInfo>();
 private Dictionary<string,string[]> routeTemplates = new Dictionary<string, string[]>();
 
 
 public void AddExecRouteTemplate(string execRouteTemplate)
 {
 if (!Regex.IsMatch(execRouteTemplate, "{controller}", RegexOptions.IgnoreCase))
 {
  throw new ArgumentException("         ,  {controller}");
 }
 
 if (!Regex.IsMatch(execRouteTemplate, "{action}", RegexOptions.IgnoreCase))
 {
  throw new ArgumentException("         ,  {action}");
 }
 
 string[] keys = Regex.Matches(execRouteTemplate, @"(?<={)\w+(?=})", RegexOptions.IgnoreCase).Cast<Match>().Select(c => c.Value.ToLower()).ToArray();
 
 routeTemplates.Add(execRouteTemplate,keys);
 }
 
 public object Run(string execRoute)
 {
 //{controller}/{action}/{id}
 string ctrller = null;
 string actionName = null;
 ArrayList args = null;
 Type controllerType = null;
 bool findResult = false;
 
 foreach (var r in routeTemplates)
 {
  string[] keys = r.Value;
  string execRoutePattern = Regex.Replace(r.Key, @"{(?<key>\w+)}", (m) => string.Format(@"(?<{0}>.[^/\\]+)", m.Groups["key"].Value.ToLower()), RegexOptions.IgnoreCase);
 
  args = new ArrayList();
  if (Regex.IsMatch(execRoute, execRoutePattern))
  {
  var match = Regex.Match(execRoute, execRoutePattern);
  for (int i = 0; i < keys.Length; i++)
  {
   if ("controller".Equals(keys[i], StringComparison.OrdinalIgnoreCase))
   {
   ctrller = match.Groups["controller"].Value;
   }
   else if ("action".Equals(keys[i], StringComparison.OrdinalIgnoreCase))
   {
   actionName = match.Groups["action"].Value;
   }
   else
   {
   args.Add(match.Groups[keys[i]].Value);
   }
  }
 
  if ((controllerType = FindControllerType(ctrller)) != null && FindAction(controllerType, actionName, args.ToArray()) != null)
  {
   findResult = true;
   break;
  }
  }
 }
 
 if (findResult)
 {
  return Process(ctrller, actionName, args.ToArray());
 }
 else
 {
  throw new Exception($"                             :{execRoute}");
 }
 }
 
 public object Process(string ctrller, string actionName, params object[] args)
 {
 Type matchedControllerType = FindControllerType(ctrller);
 
 if (matchedControllerType == null)
 {
  throw new ArgumentException($"      {ctrller} Controller  ");
 }
 
 object execResult = null;
 if (matchedControllerType != null)
 {
  var matchedController = (Controller)Activator.CreateInstance(matchedControllerType);
  MethodInfo action = FindAction(matchedControllerType, actionName, args);
  if (action == null)
  {
  throw new ArgumentException($" {matchedControllerType.FullName}        :{actionName}     :{args.Count()}      ");
  }
 
 
  var filters = action.GetCustomAttributes<FilterAttribute>(true);
  List<FilterAttribute> execBeforeFilters = new List<FilterAttribute>();
  List<FilterAttribute> execAfterFilters = new List<FilterAttribute>();
  List<FilterAttribute> exceptionFilters = new List<FilterAttribute>();
 
  if (filters != null && filters.Count() > 0)
  {
  execBeforeFilters = filters.Where(f => f.FilterType == "BEFORE").ToList();
  execAfterFilters = filters.Where(f => f.FilterType == "AFTER").ToList();
  exceptionFilters = filters.Where(f => f.FilterType == "EXCEPTION").ToList();
  }
 
  try
  {
  matchedController.OnActionExecuting(action);
 
  if (execBeforeFilters != null && execBeforeFilters.Count > 0)
  {
   execBeforeFilters.ForEach(f => f.Execute(matchedController, null));
  }
 
  var mParams = action.GetParameters();
  object[] newArgs = new object[args.Length];
  for (int i = 0; i < mParams.Length; i++)
  {
   newArgs[i] = Convert.ChangeType(args[i], mParams[i].ParameterType);
  }
 
  execResult = action.Invoke(matchedController, newArgs);
 
  matchedController.OnActionExecuted(action);
 
  if (execBeforeFilters != null && execBeforeFilters.Count > 0)
  {
   execAfterFilters.ForEach(f => f.Execute(matchedController, null));
  }
 
  }
  catch (Exception ex)
  {
  matchedController.OnActionError(action, ex);
 
  if (exceptionFilters != null && exceptionFilters.Count > 0)
  {
   exceptionFilters.ForEach(f => f.Execute(matchedController, ex));
  }
  }
 
 
 }
 
 return execResult;
 
 }
 
 private Type FindControllerType(string ctrller)
 {
 Type matchedControllerType = null;
 if (!matchedControllerTypes.ContainsKey(ctrller))
 {
  var assy = Assembly.GetAssembly(typeof(Controller));
 
  foreach (var m in assy.GetModules(false))
  {
  foreach (var t in m.GetTypes())
  {
   if (ControllerType.IsAssignableFrom(t) && !t.IsAbstract)
   {
   if (t.Name.Equals(ctrller, StringComparison.OrdinalIgnoreCase) || t.Name.Equals($"{ctrller}Controller", StringComparison.OrdinalIgnoreCase))
   {
    matchedControllerType = t;
    matchedControllerTypes[ctrller] = matchedControllerType;
    break;
   }
   }
  }
  }
 }
 else
 {
  matchedControllerType = matchedControllerTypes[ctrller];
 }
 
 return matchedControllerType;
 }
 
 private MethodInfo FindAction(Type matchedControllerType, string actionName, object[] args)
 {
 string ctrlerWithActionKey = $"{matchedControllerType.FullName}.{actionName}";
 MethodInfo action = null;
 if (!matchedControllerActions.ContainsKey(ctrlerWithActionKey))
 {
  if (args == null) args = new object[0];
  foreach (var m in matchedControllerType.GetMethods(BindingFlags.Instance | BindingFlags.Public))
  {
  if (m.Name.Equals(actionName, StringComparison.OrdinalIgnoreCase) && m.GetParameters().Length == args.Length)
  {
   action = m;
   matchedControllerActions[ctrlerWithActionKey] = action;
   break;
  }
  }
 }
 else
 {
  action = matchedControllerActions[ctrlerWithActionKey];
 }
 
 return action;
 }
}
사용 하기 전에 먼저 Controller 에서 계승 하 는 클래스 를 정의 합 니 다.예 를 들 어 TestController,그리고 해당 하 는 방법 을 다시 쓰 거나 지정 한 방법 에 필요 한 필터 특성 을 추가 합 니 다.다음 코드 는 다음 과 같 습 니 다.

public class TestController : Controller
{
 public override void OnActionExecuting(MethodInfo action)
 {
 Console.WriteLine($"{action.Name}   ,OnActionExecuting---{DateTime.Now.ToString()}");
 }
 
 public override void OnActionExecuted(MethodInfo action)
 {
 Console.WriteLine($"{action.Name}   ,OnActionExecuted--{DateTime.Now.ToString()}");
 }
 
 public override void OnActionError(MethodInfo action, Exception ex)
 {
 Console.WriteLine($"{action.Name}  ,OnActionError--{DateTime.Now.ToString()}:{ex.Message}");
 }
 
 [ActionExecutingFilter]
 [ActionExecutedFilter]
 public string HelloWorld(string name)
 {
 return ($"Hello World!->{name}");
 }
 
 [ActionExecutingFilter]
 [ActionExecutedFilter]
 [ActionErrorFilter]
 public string TestError(string name)
 {
 throw new Exception("           !");
 }
 
 [ActionExecutingFilter]
 [ActionExecutedFilter]
 public int Add(int a, int b)
 {
 return a + b;
 }
}
마지막 으로 전단 의 실제 호출 은 매우 간단 합 니 다.코드 는 다음 과 같 습 니 다.

class MVCProgram
{
 static void Main(string[] args)
 {
 try
 {
  var appContext = new AppContext();
  object rs = appContext.Process("Test", "HelloWorld", "    ");
  Console.WriteLine($"Process     1:{rs}");
 
  Console.WriteLine("=".PadRight(50, '='));
 
  appContext.AddExecRouteTemplate("{controller}/{action}/{name}");
  appContext.AddExecRouteTemplate("{action}/{controller}/{name}");
 
  object result1 = appContext.Run("HelloWorld/Test/    -zuowenjun.cn");
  Console.WriteLine($"     1:{result1}");
 
  Console.WriteLine("=".PadRight(50, '='));
 
  object result2 = appContext.Run("Test/HelloWorld/    -zuowenjun.cn");
  Console.WriteLine($"     2:{result2}");
 
  Console.WriteLine("=".PadRight(50, '='));
 
  appContext.AddExecRouteTemplate("{action}/{controller}/{a}/{b}");
  object result3 = appContext.Run("Add/Test/500/20");
  Console.WriteLine($"     3:{result3}");
 
  object result4 = appContext.Run("Test/TestError/    -zuowenjun.cn");
  Console.WriteLine($"     4:{result4}");
 }
 catch (Exception ex)
 {
  Console.ForegroundColor = ConsoleColor.Red;
  Console.WriteLine($"    :{ex.Message}");
  Console.ResetColor();
 }
 
 Console.ReadKey();
 }
}
이 를 통 해 알 수 있 듯 이 ASP.NET MVC 와 약간 유사 합 니 다.다만 ASP.NET MVC 는 URL 로 접근 합 니 다.여 기 는 AppContext.Run 을 통 해 경로 URL 이나 Process 방법 을 실행 하고 Controller,Action,인 자 를 직접 지정 하여 실행 합 니 다.
상기 호출 코드 를 통 해 경로 설정 이 비교적 유연 하 다 는 것 을 알 수 있 습 니 다.물론 매개 변수 설정 은 제외 합 니 다.여러분 이 더 좋 은 생각 이 있다 면 아래 에서 댓 글 을 달 아 주 셔 도 됩 니 다.감사합니다!
MVC 코드 실행 효 과 는 다음 과 같 습 니 다.

총결산
이상 은 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 참고 학습 가치 가 있 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.

좋은 웹페이지 즐겨찾기