商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > .NET开发中全局数据存储的常见方式

.NET开发中全局数据存储的常见方式

  发布于2026-07-23 阅读(0)

扫一扫,手机访问

一、静态类与静态成员

先从最基础的静态类说起吧。很多开发者最初接触全局数据,都是从静态类开始的——一个静态类,几个静态属性或字段,数据就这么“全局”了。

public static class GlobalData{    public static string ApplicationName { get; set; } = "MyApp";    public static int MaxConnections { get; } = 100;        private static readonly ConcurrentDictionary _cache         = new ConcurrentDictionary();        public static void SetCache(string key, object value)    {        _cache[key] = value;    }        public static T GetCache(string key)    {        return _cache.TryGetValue(key, out var value) ? (T)value : default;    }}

特点

  • 生命周期:应用程序域生命周期,从应用启动到应用结束,贯穿整个进程
  • 线程安全:需要手动实现。上例中用了 ConcurrentDictionary,但如果是普通的 Dictionary,就需要自行加锁
  • 适用场景:小型应用、工具类、全局配置常量

优缺点

  • 简单易用,写起来很直接
  • 访问速度快,没有额外开销
  • 但缺乏持久化,应用重启数据就丢了
  • 测试困难——静态依赖很难替换或Mock

二、应用程序配置系统

说到配置,.NET 提供了两种主流方式:一种是 ASP.NET Core 的 appsettings.json,另一种是 WinForms/WPF 的用户设置。先看前者。

1. appsettings.json (ASP.NET Core)

{  "AppConfig": {    "Theme": "Dark",    "Timeout": 30  }}

使用方式:

// 在Startup中配置services.Configure(Configuration.GetSection("AppConfig"));// 注入使用public class MyService{    private readonly AppConfig _config;        public MyService(IOptions config)    {        _config = config.Value;    }}

2. 用户设置 (WinForms/WPF)

// 保存设置Properties.Settings.Default.Theme = "Dark";Properties.Settings.Default.Sa ve();// 读取设置var theme = Properties.Settings.Default.Theme;

特点

  • 生命周期:持久化到配置文件,数据写在磁盘上
  • 线程安全:内置线程安全,多线程读取没问题
  • 适用场景:应用程序配置、用户偏好设置

三、依赖注入容器

在 ASP.NET Core 时代,依赖注入已经成为了标配。它不仅仅是解耦服务的工具,更是管理全局服务生命周期的利器。

// 注册服务services.AddSingleton();services.AddScoped();// 使用public class MyController : Controller{    private readonly IGlobalCache _cache;        public MyController(IGlobalCache cache)    {        _cache = cache;    }}

特点

生命周期有三种:

  • Singleton: 应用程序生命周期,全局唯一实例
  • Scoped: 请求生命周期,每次HTTP请求内唯一
  • Transient: 每次解析都创建新实例

线程安全:取决于你的实现——如果用 Singleton 服务存了可变状态,那就得小心并发问题。

适用场景:ASP.NET Core 应用、需要共享的服务实例。

四、内存缓存 (IMemoryCache)

如果只是临时存一些高频访问的小数据,内存缓存是首选。

// 注册services.AddMemoryCache();​​​​​​​// 使用public class DataService{    private readonly IMemoryCache _cache;        public DataService(IMemoryCache cache)    {        _cache = cache;    }        public string GetCachedData(string key)    {        return _cache.GetOrCreate(key, entry =>         {            entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30);            return ExpensiveDatabaseCall();        });    }}

特点

  • 生命周期:应用程序重启后丢失,数据存在于进程内存中
  • 线程安全:内置,并发读写安全
  • 适用场景:频繁访问的临时数据,比如配置缓存、查询结果缓存

五、分布式缓存 (IDistributedCache)

当你的应用扩展到多台服务器时,每个实例各自的内存缓存就不灵了。分布式缓存就是为了解决跨进程、跨服务器数据共享的问题。

// 使用Redisservices.AddStackExchangeRedisCache(options =>{    options.Configuration = "localhost:6379";});// 使用public async Task GetCachedDataAsync(string key){    return await _distributedCache.GetAsync(key);}

特点

  • 生命周期:持久化到外部存储(Redis、SQL Server等)
  • 线程安全:内置,Redis本身是单线程处理命令的
  • 适用场景:分布式应用、多实例共享数据、Session共享

六、HttpContext.Items (ASP.NET Core)

这是一个轻量级的请求级数据容器,特别适合在中间件和控制器之间传递临时数据。

// 中间件中设置app.Use(async (context, next) =>{    context.Items["RequestStartTime"] = DateTime.UtcNow;    await next();});// 控制器中访问var startTime = HttpContext.Items["RequestStartTime"] as DateTime?;

特点

  • 生命周期:单个HTTP请求期间,请求结束就销毁
  • 线程安全:每个请求独立,不存在跨请求并发问题
  • 适用场景:请求级数据共享,比如记录请求开始时间、传递中间件处理结果

七、环境变量

环境变量是跨平台部署时最常用的配置方式,特别是在容器化和云环境中。

var envVar = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");

特点

  • 生命周期:进程生命周期或系统级(取决于设置方式)
  • 线程安全:只读操作不需要额外处理
  • 适用场景:部署配置、环境特定设置(开发/测试/生产)

八、数据库存储

对于那些需要持久化、需要管理、需要版本控制的全局配置,数据库存储是最可靠的方案。

// 使用EF Corepublic class AppDbContext : DbContext{    public DbSet GlobalSettings { get; set; }}// 使用var setting = await _dbContext.GlobalSettings    .FirstOrDefaultAsync(s => s.Key == "MaintenanceMode");

特点

  • 生命周期:无限持久化,只要数据库不删
  • 线程安全:取决于数据库访问层,EF Core本身是线程安全的
  • 适用场景:需要持久化的全局配置、动态配置管理

九、选择指南

这么多方式,到底该选哪个?下面这张表可以帮助你快速决策:

存储方式生命周期持久化分布式支持典型使用场景
静态成员应用程序域全局常量、简单缓存
应用程序配置持久化部分应用设置、用户偏好
依赖注入容器取决于注册类型服务共享、全局服务
内存缓存应用程序频繁访问的临时数据
分布式缓存持久化多实例共享数据
HttpContext.Items请求期间请求级数据传递
环境变量进程/系统部署配置、环境特定设置
数据库存储持久化需要持久化的全局配置

十、最佳实践建议

综合以上,几个需要记住的原则:

1. 按需选择:别一上来就上 Redis。先看数据特性——大小、访问频率、生命周期,再决定用什么。高频小数据用内存缓存,配置数据用 appsettings.json,用户数据用数据库,这是基本的分层思路。

2. 分层设计

  • 高频小数据 → 内存缓存
  • 配置数据 → appsettings.json
  • 用户数据 → 数据库

3. 线程安全:多线程访问时,要么用 ConcurrentDictionary 这类线程安全集合,要么用 Immutable 集合防止意外修改。

4. 性能考虑:大数据集别用静态变量,它一直占着内存不释放。缓存要设置过期策略,否则内存会越来越高。

5. 测试友好:静态类写起来爽,但测试起来痛苦。能通过依赖注入管理全局服务,就尽量别用静态类。

6. 分布式场景:多个服务器实例时,不要依赖本地缓存共享 Session 或状态,用分布式缓存或消息队列来同步。

十一、高级模式示例

混合缓存策略

在实际项目中,很多时候你会同时用内存缓存和分布式缓存。思路很简单:本地内存缓存扛高频访问,分布式缓存做跨进程共享和持久化。下面是一个典型的混合缓存实现:

public class HybridCache{    private readonly IMemoryCache _memoryCache;    private readonly IDistributedCache _distributedCache;        public HybridCache(IMemoryCache memoryCache, IDistributedCache distributedCache)    {        _memoryCache = memoryCache;        _distributedCache = distributedCache;    }        public async Task GetOrCreateAsync(string key, Func> factory, TimeSpan expiration)    {        // 先从本地缓存查        if (_memoryCache.TryGetValue(key, out T memoryValue))        {            return memoryValue;        }                // 本地没有,查分布式缓存        var distributedValue = await _distributedCache.GetStringAsync(key);        if (distributedValue != null)        {            var value = JsonSerializer.Deserialize(distributedValue);            _memoryCache.Set(key, value, expiration); // 回填本地缓存            return value;        }                // 都没有,执行工厂方法获取新数据        var newValue = await factory();        _memoryCache.Set(key, newValue, expiration);        await _distributedCache.SetStringAsync(key,             JsonSerializer.Serialize(newValue),             new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = expiration });                return newValue;    }}

配置热重载

另一个常见需求是配置热重载——不改代码、不重启应用,直接修改配置文件就能生效。依赖注入结合 IOptionsMonitor 可以优雅地实现:

// Program.csbuilder.Services.Configure(builder.Configuration.GetSection("AppConfig"));builder.Services.AddSingleton>(provider =>     provider.GetRequiredService>());​​​​​​​// 使用public class ConfigService{    private readonly AppConfig _config;        public ConfigService(IOptionsMonitor configMonitor)    {        _config = configMonitor.CurrentValue;        configMonitor.OnChange(newConfig =>         {            _config = newConfig;        });    }}

通过合理选择和组合这些全局数据存储方式,你可以构建出既高效又易于维护的 .NET 应用程序架构。没有银弹,但理解每种方案的优缺点和适用场景,就已经赢了一半。

本文转载于:https://www.jb51.net/aspnet/343671dnv.htm 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注