You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何统计C# .NET Core项目各方法调用次数(支持生产/开发模式)

针对.NET Core方法调用次数统计的解决方案(支持开发/生产模式)

针对你的需求,我整理了几种适合.NET Core + VS2017 Enterprise环境的方法调用次数统计方案,分别适配开发和生产模式,你可以按需选择:

一、开发模式:用VS2017自带工具快速搞定

作为VS2017 Enterprise用户,其实不用写代码就能快速完成开发阶段的统计需求——自带的性能探查器完全能满足:

  • 打开你的项目,点击顶部菜单栏的分析 -> 性能探查器(快捷键Alt+F2)
  • 在弹出的性能会话窗口里,选择CPU使用率(方法调用次数属于CPU分析的一部分),然后点击开始
  • 运行项目,执行你要测试的业务操作后,停止探查器。在生成的报告里,展开调用树或者函数详细信息面板,就能看到每个方法的调用次数(Call Count),还能顺带看到执行时间、CPU占用等额外数据
  • 优势:零代码侵入,可视化界面直观,适合开发阶段快速验证业务逻辑的调用情况

二、代码实现:支持开关控制的通用统计方案

如果需要在生产环境也能灵活开启/关闭统计,或者要自定义统计逻辑,推荐用代码实现,通过配置开关控制,不影响正常业务性能。

1. 基于AOP拦截器的方案(适合DI管理的服务)

这里用Castle动态代理实现AOP拦截,好处是不用修改业务代码,通过DI配置就能给指定服务添加统计逻辑:

首先定义统计配置和拦截器:

using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Castle.DynamicProxy;

// 统计开关配置,可通过appsettings控制
public class MethodCountOptions
{
    public bool EnableCounting { get; set; } = false; // 默认关闭,生产环境按需开启
}

// 方法调用统计拦截器
public class MethodCountInterceptor : IInterceptor
{
    // 线程安全的统计字典,存储方法名和调用次数
    private static readonly ConcurrentDictionary<string, int> _methodCallCounts = new ConcurrentDictionary<string, int>();
    private readonly MethodCountOptions _options;
    private readonly ILogger<MethodCountInterceptor> _logger;

    public MethodCountInterceptor(IOptions<MethodCountOptions> options, ILogger<MethodCountInterceptor> logger)
    {
        _options = options.Value;
        _logger = logger;
    }

    public void Intercept(IInvocation invocation)
    {
        // 如果统计未开启,直接执行原方法
        if (!_options.EnableCounting)
        {
            invocation.Proceed();
            return;
        }

        // 生成唯一方法标识(命名空间+类名+方法名)
        var methodKey = $"{invocation.TargetType.FullName}.{invocation.Method.Name}";
        // 更新调用次数
        _methodCallCounts.AddOrUpdate(methodKey, 1, (key, count) => count + 1);

        // 区分异步/同步方法处理
        if (invocation.Method.ReturnType.IsGenericType && invocation.Method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>))
        {
            invocation.Proceed();
            var task = (Task)invocation.ReturnValue;
            task.ContinueWith(t => 
                _logger.LogInformation("方法 [{Method}] 累计调用次数: {Count}", methodKey, _methodCallCounts[methodKey])
            );
        }
        else if (invocation.Method.ReturnType == typeof(Task))
        {
            invocation.Proceed();
            var task = (Task)invocation.ReturnValue;
            task.ContinueWith(t => 
                _logger.LogInformation("方法 [{Method}] 累计调用次数: {Count}", methodKey, _methodCallCounts[methodKey])
            );
        }
        else
        {
            invocation.Proceed();
            _logger.LogInformation("方法 [{Method}] 累计调用次数: {Count}", methodKey, _methodCallCounts[methodKey]);
        }
    }

    // 暴露统计数据的方法,可用于接口查询或导出
    public static ConcurrentDictionary<string, int> GetMethodCallCounts()
    {
        return _methodCallCounts;
    }
}

然后在Startup.cs里配置DI:

using Castle.DynamicProxy;
using Microsoft.Extensions.DependencyInjection;

public void ConfigureServices(IServiceCollection services)
{
    // 绑定配置到appsettings.json的MethodCount节点
    services.Configure<MethodCountOptions>(Configuration.GetSection("MethodCount"));
    
    // 注册动态代理生成器和拦截器
    services.AddSingleton<IProxyGenerator, ProxyGenerator>();
    services.AddScoped<MethodCountInterceptor>();

    // 给需要统计的服务注册代理(以IMyService为例)
    services.AddScoped<IMyService>(sp => 
    {
        var generator = sp.GetRequiredService<IProxyGenerator>();
        var interceptor = sp.GetRequiredService<MethodCountInterceptor>();
        var actualService = new MyService(); // 你的真实服务实现
        return generator.CreateInterfaceProxyWithTarget<IMyService>(actualService, interceptor);
    });

    // 其他服务配置...
}

最后在appsettings.json里配置开关:

{
  "MethodCount": {
    "EnableCounting": true // 开发模式设为true,生产模式可根据环境变量或配置中心动态切换
  }
}

2. 基于DiagnosticSource的低侵入方案(适合所有方法)

如果需要统计非DI管理的类方法,推荐用.NET Core原生的DiagnosticSource,性能开销极低,非常适合生产环境:

using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

public class MethodCountDiagnosticListener
{
    private static readonly ConcurrentDictionary<string, int> _methodCallCounts = new ConcurrentDictionary<string, int>();
    private readonly MethodCountOptions _options;
    private readonly ILogger<MethodCountDiagnosticListener> _logger;
    private readonly DiagnosticListener _diagnosticListener;

    public MethodCountDiagnosticListener(IOptions<MethodCountOptions> options, ILogger<MethodCountDiagnosticListener> logger, DiagnosticListener diagnosticListener)
    {
        _options = options.Value;
        _logger = logger;
        _diagnosticListener = diagnosticListener;
        
        // 只有开启统计时才订阅事件
        if (_options.EnableCounting)
        {
            _diagnosticListener.SubscribeWithAdapter(this);
        }
    }

    // 监听方法进入事件
    [DiagnosticName("System.Runtime.MethodEnter")]
    public void OnMethodEnter(string methodName, Type type)
    {
        var methodKey = $"{type.FullName}.{methodName}";
        _methodCallCounts.AddOrUpdate(methodKey, 1, (key, count) => count + 1);
    }

    // 可选:监听方法退出事件,输出统计日志
    [DiagnosticName("System.Runtime.MethodExit")]
    public void OnMethodExit(string methodName, Type type)
    {
        var methodKey = $"{type.FullName}.{methodName}";
        if (_methodCallCounts.TryGetValue(methodKey, out var count))
        {
            _logger.LogDebug("方法 [{Method}] 累计调用次数: {Count}", methodKey, count);
        }
    }

    // 暴露统计数据
    public static ConcurrentDictionary<string, int> GetMethodCallCounts()
    {
        return _methodCallCounts;
    }
}

在Startup.cs注册并激活:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<MethodCountOptions>(Configuration.GetSection("MethodCount"));
    services.AddSingleton<MethodCountDiagnosticListener>();
    
    // 其他配置...
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // 激活诊断监听器
    app.ApplicationServices.GetRequiredService<MethodCountDiagnosticListener>();
    
    // 其他中间件配置...
}

三、生产模式的注意事项

  • 代码方案建议仅在需要排查问题或统计时开启,平时保持关闭,避免不必要的性能开销
  • 高并发场景下,务必使用ConcurrentDictionary保证线程安全
  • 可以将统计数据输出到专业日志系统(如Serilog+ELK),方便后续分析和可视化
  • 如果需要实时监控,也可以用.NET Core自带的dotnet counters命令行工具:
    dotnet counters monitor --process-id <你的项目进程ID> --counters System.Runtime
    

内容的提问来源于stack exchange,提问作者TestIt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 12:21:13