什么是功能开关以及为什么需要它们
\n想象一下:您编写了一个很棒的新功能——例如,带有自动补全的智能搜索。一切都已测试,代码已通过审查。但您不能 100% 确定用户会接受它。或者您担心它在高负载下会崩溃。该怎么办?
\n以前,答案很简单:“部署到生产环境然后祈祷”。今天,我们更聪明。我们使用 功能开关(feature flags, feature toggles)——一种允许在不重新编译和重新部署的情况下动态启用和禁用功能的机制。
\n在 PythonLib 的这篇文章中,我们将探讨如何在 C# 项目中 实现功能开关——从最简单的实现到包含 A/B 测试和金丝雀发布的进阶场景。
\n\n为什么功能开关是 C# 开发者的必备工具
\nC# 功能开关 解决了三个关键问题:
\n- \n
- 安全部署:即使功能尚未准备好,也可以将代码部署到生产环境。只需关闭开关即可。 \n
- 即时关闭:出了问题?在管理后台点击一下——有问题的功能就消失了。 \n
- 灵活的功能管理:为特定用户、地区或按时间启用功能。 \n
最棒的是:对于 .NET 中的功能管理,您无需重新发明轮子——有现成的库 Microsoft.FeatureManagement 可用。
最简单的方法:配置文件
\n我们从基础方法开始——通过 appsettings.json。这对于简单的 .NET 功能切换 来说是理想的选择。
步骤 1. 添加设置
\n{\n "FeatureManagement": {\n "NewSearchEngine": true,\n "DarkMode": false,\n "BetaDashboard": true\n }\n}\n\n步骤 2. 注册服务
\n在 Program.cs 中添加:
builder.Services.AddFeatureManagement();\n\n步骤 3. 在代码中使用
\npublic class SearchController\n{\n private readonly IFeatureManager _featureManager;\n\n public SearchController(IFeatureManager featureManager)\n {\n _featureManager = featureManager;\n }\n\n public async Task<IActionResult> Search(string query)\n {\n if (await _featureManager.IsEnabledAsync("NewSearchEngine"))\n {\n return View("NewSearch", await _newEngine.SearchAsync(query));\n }\n return View("LegacySearch", await _oldEngine.SearchAsync(query));\n }\n}\n完成!现在,要启用新功能,只需在配置文件中将 true 改为 false 并重启应用程序。但我们希望无需重启,对吧?
动态功能开关:无需重启即可切换
\n对于无需重启的 C# 条件功能,我们使用能够加载更改的配置提供程序。最佳选择是数据库或 Redis。
\n\n从数据库创建提供程序
\npublic class DatabaseFeatureDefinitionProvider : IFeatureDefinitionProvider\n{\n private readonly AppDbContext _db;\n\n public DatabaseFeatureDefinitionProvider(AppDbContext db)\n {\n _db = db;\n }\n\n public async Task<FeatureDefinition> GetFeatureDefinitionAsync(string featureName)\n {\n var feature = await _db.FeatureFlags\n .FirstOrDefaultAsync(f => f.Name == featureName);\n \n return feature != null \n ? new FeatureDefinition { Name = feature.Name, EnabledFor = feature.EnabledFor }\n : null;\n }\n\n public IAsyncEnumerable<FeatureDefinition> GetAllFeatureDefinitionsAsync()\n {\n return _db.FeatureFlags\n .Select(f => new FeatureDefinition { Name = f.Name, EnabledFor = f.EnabledFor })\n .AsAsyncEnumerable();\n }\n}\n\n注册:
\nbuilder.Services.AddFeatureManagement