今天也要做这个功能,就百度一下,结果搜索到了自己的文章。一开始还没注意,当看到里面的一个注释的方法时,一开始还以为自己复制错了代码,结果仔细一看网页的文章,我去,原来是自己写的,写的确实不咋地。
把自己的文章的代码复制了下来,稍微改了一下,运行看了一下效果,然后仔细一看,计算的总时间不对,如下图:
上一篇文章的地址:https://www.cnblogs.com/guxingy/p/10142242.html
改了几个地方:
/// <summary> /// 拦截器 /// </summary> public class CallingLogInterceptor : IInterceptor { private DateTime dt { get; set; } private TimeSpan ts { get; set; } /// <summary> /// 方法执行前 /// </summary> /// <param name="invocation"></param> private void PreProceed(IInvocation invocation) { dt = DateTime.Now; } /// <summary> /// 方法执行后 /// </summary> /// <param name="invocation"></param> private void PostProceed(IInvocation invocation) { ts = DateTime.Now - dt; Console.WriteLine($"方法名称:{invocation.Method.Name}" ); Console.WriteLine($"开始时间:{dt.ToString("yyyy-MM-dd HH:mm:ss fff")}"); Console.WriteLine($"结束时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")}"); Console.WriteLine($"所用时间:{ts.TotalSeconds}"); MethodOperationInfo.Add(invocation, ts.TotalMilliseconds); } /// <summary> /// 拦截 /// </summary> /// <param name="invocation"></param> public void Intercept(IInvocation invocation) { this.PreProceed(invocation); invocation.Proceed();//调用 this.PostProceed(invocation); } }
/// <summary> /// 测试类1 /// </summary> public class Class5_test1 { public virtual void test1() { test4(); test3(); test2(); } public virtual void test2() { System.Threading.Thread.Sleep(1000); } public virtual void test3() { System.Threading.Thread.Sleep(2000); } public virtual void test4() { System.Threading.Thread.Sleep(3000); } }
public class MyProxyGenerator { /// <summary> /// 创建一个代理对象 /// </summary> /// <typeparam name="T"></typeparam> /// <returns></returns> public static T CreateProxy<T>() where T : class { ProxyGenerator generator = new ProxyGenerator();//代理 CallingLogInterceptor interceptor = new CallingLogInterceptor();//定义 拦截器 T entity = generator.CreateClassProxy<T>(interceptor); return entity; } }
public class Class2 { public static void test1() { Class5_test1 entity = MyProxyGenerator.CreateProxy<Class5_test1>(); entity.test1(); MethodOperationInfo.ShowContainsDetail(); Console.Read(); } }
说明:所有的测试,只是修改了一下Class5_test1 这个类给
测试1
/// <summary> /// 测试类1 /// </summary> public class Class5_test1 { public virtual void test1() { test2(); test3(); test4(); } public virtual void test2() { System.Threading.Thread.Sleep(1000); } public virtual void test3() { System.Threading.Thread.Sleep(2000); } public virtual void test4() { System.Threading.Thread.Sleep(3000); } }
问题:这里test1调用了test2、test3、test4,那么执行test1所用时间,应该是test2、test3、test4所用时间的总和,正确所用时间至少应该是6秒(1+2+3),结果这里输出的是3秒多,肯定不对。
测试2
/// <summary> /// 测试类1 /// </summary> public class Class5_test1 { public virtual void test1() { test4(); test3(); test2(); } public virtual void test2() { System.Threading.Thread.Sleep(1000); } public virtual void test3() { System.Threading.Thread.Sleep(2000); } public virtual void test4() { System.Threading.Thread.Sleep(3000); } }
问题:这里的test1方法的所用时间是1秒多,更不对了。
结论:
测试1 和 测试2 的区别,只是在test1里面改变了test2、test3、test4的执行顺序,结果时间却迥然不同,更奇怪的是,test1的所用时间,更是莫名的接近调用的倒数第二个方法。
测试1里面,test1的所用时间,很接近test4。
测试2里面,test1的所用时间,很接近test2。
反正就是所用时间,很接近 方法体里面 最后一个 调用的方法,原因找到了再补充,了解一下动态代理的原理或许就知道了,也获取是自己写错了,估计是第一种可能
乘着自己好奇,又测试了一下,看下面哦
/// <summary> /// 测试类1 /// </summary> public class Class5_test1 { public virtual void test1() { Console.WriteLine("开始执行test1"); test4(); test3(); test2(); Console.WriteLine("执行完成test1"); } public virtual void test2() { System.Threading.Thread.Sleep(1000); } public virtual void test3() { System.Threading.Thread.Sleep(2000); } public virtual void test4() { System.Threading.Thread.Sleep(3000); } }
主要是是在,test1方法里面添加了方法的执行情况, 输出 开始执行 和 执行完成。
找到原因了:拦截器对象只创建了一个,所以dt一直都是最后调用的那个方法的开始时间
还是自己写的代码有问题啊
赋上 改好的全部代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Castle.Core.Interceptor; using Castle.DynamicProxy; using System.Reflection; using Newtonsoft.Json; using System.Security.Cryptography; namespace ConsoleApplication3 { //原文:https://www.cnblogs.com/guxingy/p/10142242.html public class Class2 { public static void test1() { Class5_test1 entity = MyProxyGenerator.CreateProxy<Class5_test1>(); entity.test1(); MethodOperationInfo.ShowContainsDetail(); } } public class MyProxyGenerator { /// <summary> /// 创建一个代理对象 /// </summary> /// <typeparam name="T"></typeparam> /// <returns></returns> public static T CreateProxy<T>() where T : class { ProxyGenerator generator = new ProxyGenerator();//代理 CallingLogInterceptor interceptor = new CallingLogInterceptor();//定义 拦截器 T entity = generator.CreateClassProxy<T>(interceptor); return entity; } } /// <summary> /// 拦截器 /// </summary> public class CallingLogInterceptor : IInterceptor { private DateTime dt { get; set; } private TimeSpan ts { get; set; } /// <summary> /// 方法执行前 /// </summary> /// <param name="invocation"></param> private void PreProceed(IInvocation invocation) { //创建一个唯一的标识 //创建一个对象,并记录开始时间 //dt = DateTime.Now; //Console.WriteLine($"开始执行,方法名称:{invocation.Method.Name},开始时间:{dt.ToString("yyyy-MM-dd HH:mm:ss fff")}"); //Console.WriteLine(); MethodOperationInfo.SetStartTime(invocation); } /// <summary> /// 方法执行后 /// </summary> /// <param name="invocation"></param> private void PostProceed(IInvocation invocation) { //通用标识找到之前创建的那个对象,并记录结束时间 //ts = DateTime.Now - dt; //Console.WriteLine($"执行完成,方法名称:{invocation.Method.Name}"); //Console.WriteLine($" 开始时间:{dt.ToString("yyyy-MM-dd HH:mm:ss fff")}"); //Console.WriteLine($" 结束时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")}"); //Console.WriteLine($" 所用时间:{ts.TotalSeconds}"); //Console.WriteLine(); MethodOperationInfo.SetEndTime(invocation); } /// <summary> /// 拦截 /// </summary> /// <param name="invocation"></param> public void Intercept(IInvocation invocation) { this.PreProceed(invocation); invocation.Proceed();//调用 this.PostProceed(invocation); } } /// <summary> /// 扩展 /// </summary> public static class IntExtension { public static string ToString(this object obj, int len, bool afterFill = true) { string name = obj.ToString(); //int count = len - name.Length;//不能用这个 汉字和英文占用的长度不同 int count = len - System.Text.Encoding.Default.GetBytes(name).Length; if (afterFill) { for (int i = 0; i < count; i++) { name += " "; } return name; } else { string value = ""; for (int i = 0; i < count; i++) { value += " "; } value += name; return value; } } } /// <summary> /// 操作日志 /// </summary> public class MethodOperationInfo { public string NameSpaceName { get; set; } public string ClassName { get; set; } public string MethodName { get; set; } public string Parameters { get; set; } public string ParameterTypes { get; set; } public double TotalMilliseconds { get; set; } public string Name { get; set; } public DateTime StartTime { get; set; } public DateTime EndTime { get; set; } public bool IsEnd { get; set; }//标识这个对象是否完成计算耗时 /// <summary> /// 构造函数 /// </summary> /// <param name="invocation"></param> public MethodOperationInfo(IInvocation invocation) { StartTime = DateTime.Now; NameSpaceName = invocation.TargetType.Namespace; ClassName = invocation.TargetType.Name; MethodName = invocation.Method.Name;//方法名 ParameterTypes = GetParameterTypes(invocation);//参数类型 Parameters = GetParameter(invocation);//参数 Name = GetName(invocation); IsEnd = false; } /// <summary> /// 操作完成 /// </summary> /// <param name="invocation"></param> public void OperationFinish(IInvocation invocation) { EndTime = DateTime.Now; TotalMilliseconds = (EndTime - StartTime).TotalMilliseconds; IsEnd = true; } //存放 所有的 详细信息 public static List<MethodOperationInfo> operationInfos = new List<MethodOperationInfo>(); /// <summary> /// 设置开始时间 /// </summary> /// <param name="invocation"></param> public static void SetStartTime(IInvocation invocation) { MethodOperationInfo model = new MethodOperationInfo(invocation); operationInfos.Add(model); } /// <summary> /// 设置结束时间 /// </summary> /// <param name="invocation"></param> public static void SetEndTime(IInvocation invocation) { MethodOperationInfo model = GetModel(invocation); model.OperationFinish(invocation); } /// <summary> /// 获取一个对象 /// </summary> /// <param name="invocation"></param> /// <returns></returns> public static MethodOperationInfo GetModel(IInvocation invocation) { string name = GetName(invocation); return operationInfos.Where(c => (c.Name == name) && !c.IsEnd).FirstOrDefault(); } /// <summary> /// 获取 这个方法 唯一的编号,针对方法而已 /// </summary> /// <param name="invocation"></param> /// <returns></returns> public static string GetName(IInvocation invocation) { string NameSpaceName = invocation.TargetType.Namespace; string ClassName = invocation.TargetType.Name; string MethodName = invocation.Method.Name;//方法名 string ParameterTypes = GetParameterTypes(invocation); string Parameters = GetParameter(invocation);//参数 string strId = $"{NameSpaceName},{ClassName},{MethodName},{ParameterTypes}"; //MD5加密 byte[] result = Encoding.Default.GetBytes(strId); MD5 md5 = new MD5CryptoServiceProvider(); byte[] output = md5.ComputeHash(result); return BitConverter.ToString(output).Replace("-", ""); } /// <summary> /// 获取方法的参数类型 /// 如:(System.String, System.Object, System.Int32) /// </summary> /// <param name="invocation"></param> /// <returns></returns> public static string GetParameterTypes(IInvocation invocation) { MethodInfo mInfo = invocation.Method; ParameterInfo[] pInfos = mInfo.GetParameters(); string str = ""; str += "("; for (int j = 0; j < pInfos.Length; j++) { var p = pInfos[j]; string pTypeName = $"{p.ParameterType.ToString()}, "; if (p.ParameterType.IsGenericType && (p.ParameterType.GetGenericTypeDefinition() == typeof(Nullable<>))) { pTypeName = $"{Nullable.GetUnderlyingType(p.ParameterType).Name}?, "; } str += pTypeName; } str = str.TrimEnd(' ').TrimEnd(','); str += ")"; return str; } /// <summary> /// 获取方法的参数 /// </summary> /// <param name="invocation"></param> /// <returns></returns> public static string GetParameter(IInvocation invocation) { string Parameters = "";//参数 if ((invocation.Arguments != null) && (invocation.Arguments.Length > 0)) { Parameters = JsonConvert.SerializeObject(invocation.Arguments); } return Parameters; } /// <summary> /// 显示日志 /// </summary> /// <param name="ShowDetailRecord">是否显示详情记录-比较占用时间</param> /// <param name="IsFilter">是否开启过滤</param> public static void Show(bool ShowDetailRecord = true, bool IsFilter = false) { StringBuilder sb = new StringBuilder(); DateTime beforDT = DateTime.Now; List<string> list_Show_Method = new List<string>() { "GetSingle_Value1" };//可改为配置参数 //每个方法-耗时 string str = ""; double totalMilliseconds_AllMethod = 0; //去重 var operationInfos_NotRepeat = operationInfos.GroupBy(c => c.Name).Select(c => c.First()).ToList(); //分组统计 operationInfos.GroupBy(c => c.Name).Select(c => new { Name = c.Key, ExecuteNumber = c.Count(), TotalMilliseconds = c.Sum(a => a.TotalMilliseconds) }).ToList().ForEach(c => { var item = operationInfos_NotRepeat.Where(x => x.Name == c.Name).First(); totalMilliseconds_AllMethod += item.TotalMilliseconds; str += $"命名空间:{item.NameSpaceName.ToString(40)} "; str += $"类名:{item.ClassName.ToString(30)} "; str += $"方法:{item.MethodName.ToString(80)} "; str += $"次数:{c.ExecuteNumber.ToString(10)} "; str += $"耗时:{c.TotalMilliseconds.ToString(10, false) }毫秒 "; str += $" "; }); sb.Append(str + " "); //方法-总耗时 str = ""; str += $"所有方法-耗时:{totalMilliseconds_AllMethod}毫秒 "; str += $"{totalMilliseconds_AllMethod / 1000}秒 "; str += $"{(totalMilliseconds_AllMethod / 1000 / 60).ToString("f2")}分钟 "; str += $"当前时间:{DateTime.Now} "; sb.Insert(0, str + " "); #region 方法 每次耗时的 详情信息 if (ShowDetailRecord) { for (int i = 0; i < operationInfos.Count; i++) { Console.WriteLine($"处理数据-当前行:{operationInfos.Count - i}"); var item = operationInfos[i]; //数据过滤 if (IsFilter && !list_Show_Method.Contains(item.MethodName)) { continue; } sb.Append($"命名空间:{item.NameSpaceName.ToString(40)} "); sb.Append($"类名:{item.ClassName.ToString(30)} "); sb.Append($"方法:{item.MethodName + item.ParameterTypes.ToString(80)} "); sb.Append($"耗时:{item.TotalMilliseconds.ToString(10, false) }毫秒 "); sb.Append($"参数:{item.Parameters.ToString(50)} "); sb.Append($" "); } } #endregion //计算日志-耗时 sb.Insert(0, $"计算日志-耗时:{DateTime.Now.Subtract(beforDT).TotalSeconds.ToString("#0.00000")}秒 "); System.IO.File.WriteAllText($"LOG_{DateTime.Now.ToString("yyyyMMddHHmmssfff")}.txt", sb.ToString()); Console.WriteLine("完成!"); } /// <summary> /// 显示日志 包含详情数据 /// </summary> public static void ShowContainsDetail() { Show(true); } /// <summary> /// 显示日志 不包含详情数据 /// </summary> public static void ShowNotContainsDetail() { Show(false); } } /// <summary> /// 测试类1 /// </summary> public class Class5_test1 { public virtual void test1() { Console.WriteLine("主方法-test1-开始执行"); Console.WriteLine(); for (int i = 0; i < 4; i++) { test4(); } for (int i = 0; i < 3; i++) { test3(); } for (int i = 0; i < 2; i++) { test2(); } Console.WriteLine("主方法-test1-执行完成"); Console.WriteLine(); } public virtual void test2() { System.Threading.Thread.Sleep(200); } public virtual void test3() { System.Threading.Thread.Sleep(300); } public virtual void test4() { System.Threading.Thread.Sleep(400); } } }
但是当要代理的那个的类,如果没有无参构造函数,这种方法就还是用不了,准备解决这个问题,解决了到时候再贴上解决方案。
本来是准备用到生产环境测试一下自己的代码的,结果,我去,有参构造函数,接口,多态,麻蛋都有啊,改好了,再继续贴代码