.NET新手开发:如何实现每日统一的未完成订单统计变量初始化
解决.NET网站当日统一初始统计基准值的方案
Hey there! 作为.NET开发新手遇到这个需求太正常了——要让所有用户当天看到完全一致的初始未完成订单数基准,核心就是把这个值「固定在当天」,不管用户什么时候访问、服务器有没有重启,都能拿到同一个数。下面给你几个落地性强的方案,适配不同场景:
方案1:内存缓存+每日定时任务(单实例部署首选)
如果你的网站是单服务器部署,用ASP.NET Core的IHostedService做每日定时初始化,把基准值存到内存缓存里,过期时间设为当天结束。这样所有用户访问时直接取缓存,性能拉满还不会出错。
步骤:
- 写一个定时任务服务,每天凌晨0点拉取初始值:
public class DailyOrderBaseJob : IHostedService, IDisposable { private readonly IServiceScopeFactory _scopeFactory; private readonly IMemoryCache _cache; private Timer _timer; public DailyOrderBaseJob(IServiceScopeFactory scopeFactory, IMemoryCache cache) { _scopeFactory = scopeFactory; _cache = cache; } public Task StartAsync(CancellationToken cancellationToken) { // 首次启动立即执行一次,之后每天0点执行 SetDailyBaseValue(); _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromHours(24)); return Task.CompletedTask; } private void DoWork(object state) { SetDailyBaseValue(); } private void SetDailyBaseValue() { using var scope = _scopeFactory.CreateScope(); var dbContext = scope.ServiceProvider.GetRequiredService<YourDbContext>(); // 计算当日初始未完成订单数(这里根据你的业务逻辑调整) var initialUnfinishedCount = dbContext.Orders .Where(o => !o.IsCompleted && o.CreatedDate.Date <= DateTime.Today) .Count(); // 缓存到当天23:59:59过期 var expireTime = new DateTime(DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day, 23, 59, 59); _cache.Set("DailyInitialOrderCount", initialUnfinishedCount, expireTime); } public Task StopAsync(CancellationToken cancellationToken) { _timer?.Change(Timeout.Infinite, 0); return Task.CompletedTask; } public void Dispose() { _timer?.Dispose(); } }
- 在
Program.cs里注册这个服务:
builder.Services.AddHostedService<DailyOrderBaseJob>(); builder.Services.AddMemoryCache();
- 业务代码里直接取缓存:
public async Task<IActionResult> GetStatistics() { // 从缓存拿当日基准值 if (!_cache.TryGetValue("DailyInitialOrderCount", out int initialCount)) { // 兜底:如果缓存没加载(比如刚重启),临时计算一次并缓存 using var scope = _scopeFactory.CreateScope(); var dbContext = scope.ServiceProvider.GetRequiredService<YourDbContext>(); initialCount = dbContext.Orders.Where(o => !o.IsCompleted && o.CreatedDate.Date <= DateTime.Today).Count(); var expireTime = new DateTime(DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day, 23, 59, 59); _cache.Set("DailyInitialOrderCount", initialCount, expireTime); } // 获取实时已完成数 var completedCount = await _dbContext.Orders.CountAsync(o => o.IsCompleted && o.CompletedDate.Date == DateTime.Today); // 计算统计结果 var rate = initialCount == 0 ? 0 : (double)completedCount / initialCount; return Ok(new { InitialCount = initialCount, CompletedCount = completedCount, Rate = rate }); }
方案2:数据库存储当日基准值(多实例/需持久化场景)
如果你的网站是多服务器部署,或者担心服务器重启导致缓存丢失,直接把当日基准值存在数据库里更靠谱。逻辑是:当天第一个用户访问时,自动计算并插入基准值,之后所有用户都读这个记录。
步骤:
先建一个表(比如
DailyOrderBase),字段可以是:- Id(主键)
- Date(日期,唯一键)
- InitialUnfinishedCount(初始未完成数)
- CreatedTime(创建时间)
业务代码里的获取逻辑:
public async Task<int> GetDailyInitialOrderCount() { var today = DateTime.Today; var dailyBase = await _dbContext.DailyOrderBase .FirstOrDefaultAsync(o => o.Date == today); if (dailyBase == null) { // 当天还没初始化,计算并插入 var initialCount = await _dbContext.Orders .CountAsync(o => !o.IsCompleted && o.CreatedDate.Date <= today); dailyBase = new DailyOrderBase { Date = today, InitialUnfinishedCount = initialCount, CreatedTime = DateTime.Now }; _dbContext.DailyOrderBase.Add(dailyBase); await _dbContext.SaveChangesAsync(); } return dailyBase.InitialUnfinishedCount; }
这个方案的好处是,不管多少服务器实例,都读同一个数据库记录,绝对统一;而且即使应用重启,数据也不会丢。
方案3:分布式缓存(多实例高性能场景)
如果是多实例部署又想兼顾性能,可以用Redis这类分布式缓存代替内存缓存。逻辑和方案1类似,只是把IMemoryCache换成IDistributedCache,缓存的key带上日期(比如DailyInitialOrderCount_20240520),过期时间设为当天结束。
示例代码(简化版):
public async Task<int> GetDailyInitialOrderCount(IDistributedCache cache, YourDbContext dbContext) { var todayStr = DateTime.Today.ToString("yyyyMMdd"); var cacheKey = $"DailyInitialOrderCount_{todayStr}"; var cachedValue = await cache.GetStringAsync(cacheKey); if (!string.IsNullOrEmpty(cachedValue) && int.TryParse(cachedValue, out int initialCount)) { return initialCount; } // 缓存不存在,计算并存入分布式缓存 initialCount = await dbContext.Orders.CountAsync(o => !o.IsCompleted && o.CreatedDate.Date <= DateTime.Today); await cache.SetStringAsync(cacheKey, initialCount.ToString(), new DistributedCacheEntryOptions { AbsoluteExpiration = new DateTimeOffset(DateTime.Today.AddDays(1).AddSeconds(-1)) }); return initialCount; }
关键注意点
- 一定要用
DateTime.Today来判断日期,避免时区问题(如果是多时区网站,要统一用UTC时间) - 兜底逻辑不能少:比如缓存没加载、数据库记录没创建时,要能临时计算并补全,避免用户看到错误
- 如果是多实例,方案2和3更稳妥,方案1的内存缓存是单实例隔离的,不同服务器会有不同值
内容的提问来源于stack exchange,提问作者heyjaynell
相关产品推荐
相关产品推荐

