Skip to content

Commit 195325e

Browse files
Merge pull request #295 from microsoft/zhiyuanliang/preview-merge-from-main
preview merge from main
2 parents e05591c + 51bcf73 commit 195325e

File tree

174 files changed

+134336
-9741
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

174 files changed

+134336
-9741
lines changed

Microsoft.FeatureManagement.sln

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio Version 17
44
VisualStudioVersion = 17.0.31825.309
55
MinimumVisualStudioVersion = 10.0.40219.1
6-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FeatureFlagDemo", "examples\FeatureFlagDemo\FeatureFlagDemo.csproj", "{E58A64A6-BE10-4D7A-AAB8-C3E2925CB32F}"
7-
EndProject
86
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.FeatureManagement", "src\Microsoft.FeatureManagement\Microsoft.FeatureManagement.csproj", "{ED1A3494-6D5B-4B27-BA9C-8BAF93E36955}"
97
EndProject
108
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.FeatureManagement", "tests\Tests.FeatureManagement\Tests.FeatureManagement.csproj", "{FDBB27BA-C5BA-48A7-BA9B-63159943EA9F}"
@@ -15,24 +13,22 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.FeatureManagement
1513
EndProject
1614
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{FB5C34DF-695C-4DF9-8AED-B3EA2516EA72}"
1715
EndProject
18-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleApp", "examples\ConsoleApp\ConsoleApp.csproj", "{E50FB931-7A42-440E-AC47-B8DFE5E15394}"
16+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.FeatureManagement.AspNetCore", "tests\Tests.FeatureManagement.AspNetCore\Tests.FeatureManagement.AspNetCore.csproj", "{FC0DC3E2-5646-4AEC-A7DB-2D6167BC3BB4}"
17+
EndProject
18+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp", "examples\ConsoleApp\ConsoleApp.csproj", "{7B98D293-F270-423E-A9A6-0D388E903AE9}"
1919
EndProject
20-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TargetingConsoleApp", "examples\TargetingConsoleApp\TargetingConsoleApp.csproj", "{6558C21E-CF20-4278-AA08-EB9D1DF29D66}"
20+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RazorPages", "examples\RazorPages\RazorPages.csproj", "{36DBB413-D9CA-4C56-AE5B-EAEA4C344DB3}"
2121
EndProject
22-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RazorPages", "examples\RazorPages\RazorPages.csproj", "{BA29A1BB-81D5-4EB1-AF37-6ECF64AF27E2}"
22+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FeatureFlagDemo", "examples\FeatureFlagDemo\FeatureFlagDemo.csproj", "{DACAB624-4611-42E8-844C-529F93A54980}"
2323
EndProject
24-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.FeatureManagement.AspNetCore", "tests\Tests.FeatureManagement.AspNetCore\Tests.FeatureManagement.AspNetCore.csproj", "{FC0DC3E2-5646-4AEC-A7DB-2D6167BC3BB4}"
24+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TargetingConsoleApp", "examples\TargetingConsoleApp\TargetingConsoleApp.csproj", "{283D3EBB-4716-4F1D-BA51-A435F7E2AB82}"
2525
EndProject
2626
Global
2727
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2828
Debug|Any CPU = Debug|Any CPU
2929
Release|Any CPU = Release|Any CPU
3030
EndGlobalSection
3131
GlobalSection(ProjectConfigurationPlatforms) = postSolution
32-
{E58A64A6-BE10-4D7A-AAB8-C3E2925CB32F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33-
{E58A64A6-BE10-4D7A-AAB8-C3E2925CB32F}.Debug|Any CPU.Build.0 = Debug|Any CPU
34-
{E58A64A6-BE10-4D7A-AAB8-C3E2925CB32F}.Release|Any CPU.ActiveCfg = Release|Any CPU
35-
{E58A64A6-BE10-4D7A-AAB8-C3E2925CB32F}.Release|Any CPU.Build.0 = Release|Any CPU
3632
{ED1A3494-6D5B-4B27-BA9C-8BAF93E36955}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
3733
{ED1A3494-6D5B-4B27-BA9C-8BAF93E36955}.Debug|Any CPU.Build.0 = Debug|Any CPU
3834
{ED1A3494-6D5B-4B27-BA9C-8BAF93E36955}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -45,33 +41,37 @@ Global
4541
{CFD3E549-2E24-490D-A7F6-F95E56A81092}.Debug|Any CPU.Build.0 = Debug|Any CPU
4642
{CFD3E549-2E24-490D-A7F6-F95E56A81092}.Release|Any CPU.ActiveCfg = Release|Any CPU
4743
{CFD3E549-2E24-490D-A7F6-F95E56A81092}.Release|Any CPU.Build.0 = Release|Any CPU
48-
{E50FB931-7A42-440E-AC47-B8DFE5E15394}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
49-
{E50FB931-7A42-440E-AC47-B8DFE5E15394}.Debug|Any CPU.Build.0 = Debug|Any CPU
50-
{E50FB931-7A42-440E-AC47-B8DFE5E15394}.Release|Any CPU.ActiveCfg = Release|Any CPU
51-
{E50FB931-7A42-440E-AC47-B8DFE5E15394}.Release|Any CPU.Build.0 = Release|Any CPU
52-
{6558C21E-CF20-4278-AA08-EB9D1DF29D66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
53-
{6558C21E-CF20-4278-AA08-EB9D1DF29D66}.Debug|Any CPU.Build.0 = Debug|Any CPU
54-
{6558C21E-CF20-4278-AA08-EB9D1DF29D66}.Release|Any CPU.ActiveCfg = Release|Any CPU
55-
{6558C21E-CF20-4278-AA08-EB9D1DF29D66}.Release|Any CPU.Build.0 = Release|Any CPU
56-
{BA29A1BB-81D5-4EB1-AF37-6ECF64AF27E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
57-
{BA29A1BB-81D5-4EB1-AF37-6ECF64AF27E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
58-
{BA29A1BB-81D5-4EB1-AF37-6ECF64AF27E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
59-
{BA29A1BB-81D5-4EB1-AF37-6ECF64AF27E2}.Release|Any CPU.Build.0 = Release|Any CPU
6044
{FC0DC3E2-5646-4AEC-A7DB-2D6167BC3BB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
6145
{FC0DC3E2-5646-4AEC-A7DB-2D6167BC3BB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
6246
{FC0DC3E2-5646-4AEC-A7DB-2D6167BC3BB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
6347
{FC0DC3E2-5646-4AEC-A7DB-2D6167BC3BB4}.Release|Any CPU.Build.0 = Release|Any CPU
48+
{7B98D293-F270-423E-A9A6-0D388E903AE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
49+
{7B98D293-F270-423E-A9A6-0D388E903AE9}.Debug|Any CPU.Build.0 = Debug|Any CPU
50+
{7B98D293-F270-423E-A9A6-0D388E903AE9}.Release|Any CPU.ActiveCfg = Release|Any CPU
51+
{7B98D293-F270-423E-A9A6-0D388E903AE9}.Release|Any CPU.Build.0 = Release|Any CPU
52+
{36DBB413-D9CA-4C56-AE5B-EAEA4C344DB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
53+
{36DBB413-D9CA-4C56-AE5B-EAEA4C344DB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
54+
{36DBB413-D9CA-4C56-AE5B-EAEA4C344DB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
55+
{36DBB413-D9CA-4C56-AE5B-EAEA4C344DB3}.Release|Any CPU.Build.0 = Release|Any CPU
56+
{DACAB624-4611-42E8-844C-529F93A54980}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
57+
{DACAB624-4611-42E8-844C-529F93A54980}.Debug|Any CPU.Build.0 = Debug|Any CPU
58+
{DACAB624-4611-42E8-844C-529F93A54980}.Release|Any CPU.ActiveCfg = Release|Any CPU
59+
{DACAB624-4611-42E8-844C-529F93A54980}.Release|Any CPU.Build.0 = Release|Any CPU
60+
{283D3EBB-4716-4F1D-BA51-A435F7E2AB82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
61+
{283D3EBB-4716-4F1D-BA51-A435F7E2AB82}.Debug|Any CPU.Build.0 = Debug|Any CPU
62+
{283D3EBB-4716-4F1D-BA51-A435F7E2AB82}.Release|Any CPU.ActiveCfg = Release|Any CPU
63+
{283D3EBB-4716-4F1D-BA51-A435F7E2AB82}.Release|Any CPU.Build.0 = Release|Any CPU
6464
EndGlobalSection
6565
GlobalSection(SolutionProperties) = preSolution
6666
HideSolutionNode = FALSE
6767
EndGlobalSection
6868
GlobalSection(NestedProjects) = preSolution
69-
{E58A64A6-BE10-4D7A-AAB8-C3E2925CB32F} = {FB5C34DF-695C-4DF9-8AED-B3EA2516EA72}
7069
{FDBB27BA-C5BA-48A7-BA9B-63159943EA9F} = {8ED6FFEE-4037-49A2-9709-BC519C104A90}
71-
{E50FB931-7A42-440E-AC47-B8DFE5E15394} = {FB5C34DF-695C-4DF9-8AED-B3EA2516EA72}
72-
{6558C21E-CF20-4278-AA08-EB9D1DF29D66} = {FB5C34DF-695C-4DF9-8AED-B3EA2516EA72}
73-
{BA29A1BB-81D5-4EB1-AF37-6ECF64AF27E2} = {FB5C34DF-695C-4DF9-8AED-B3EA2516EA72}
7470
{FC0DC3E2-5646-4AEC-A7DB-2D6167BC3BB4} = {8ED6FFEE-4037-49A2-9709-BC519C104A90}
71+
{7B98D293-F270-423E-A9A6-0D388E903AE9} = {FB5C34DF-695C-4DF9-8AED-B3EA2516EA72}
72+
{36DBB413-D9CA-4C56-AE5B-EAEA4C344DB3} = {FB5C34DF-695C-4DF9-8AED-B3EA2516EA72}
73+
{DACAB624-4611-42E8-844C-529F93A54980} = {FB5C34DF-695C-4DF9-8AED-B3EA2516EA72}
74+
{283D3EBB-4716-4F1D-BA51-A435F7E2AB82} = {FB5C34DF-695C-4DF9-8AED-B3EA2516EA72}
7575
EndGlobalSection
7676
GlobalSection(ExtensibilityGlobals) = postSolution
7777
SolutionGuid = {84DA6C54-F140-4518-A1B4-E4CF42117FBD}

README.md

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,6 @@ In the above example, `FeatureW` specifies a `RequirementType` of `All`, meaning
142142

143143
### Status
144144

145-
146145
`Status` is an optional property of a feature flag that controls how a flag's enabled state is evaluated. By default, the status of a flag is `Conditional`, meaning that feature filters should be evaluated to determine if the flag is enabled. If the `Status` of a flag is set to `Disabled` then feature filters are not evaluated and the flag is always considered to be disabled.
147146

148147

@@ -158,21 +157,7 @@ In the above example, `FeatureW` specifies a `RequirementType` of `All`, meaning
158157
```
159158

160159
In this example, even though the `AlwaysOn` filter would normally always make the feature enabled, the `Status` property is set to `Disabled`, so this feature will always be disabled.
161-
162-
### Referencing
163-
164-
To make it easier to reference these feature flags in code, we recommend to define feature flag variables like below.
165-
166-
``` C#
167-
// Define feature flags in an enum
168-
public enum MyFeatureFlags
169-
{
170-
FeatureT,
171-
FeatureU,
172-
FeatureV
173-
}
174-
```
175-
160+
176161
### Service Registration
177162

178163
Feature flags rely on .NET Core dependency injection. We can register the feature management services using standard conventions.
@@ -194,7 +179,6 @@ public class Startup
194179

195180
This tells the feature manager to use the "FeatureManagement" section from the configuration for feature flag settings. It also registers two built-in feature filters named `PercentageFilter` and `TimeWindowFilter`. When filters are referenced in feature flag settings (appsettings.json) the _Filter_ part of the type name can be omitted.
196181

197-
198182
**Advanced:** The feature manager looks for feature definitions in a configuration section named "FeatureManagement". If the "FeatureManagement" section does not exist, it falls back to the root of the provided configuration.
199183

200184
## Consumption
@@ -207,7 +191,7 @@ The basic form of feature management is checking if a feature is enabled and the
207191
208192
IFeatureManager featureManager;
209193
210-
if (await featureManager.IsEnabledAsync(nameof(MyFeatureFlags.FeatureU)))
194+
if (await featureManager.IsEnabledAsync("FeatureX"))
211195
{
212196
// Do something
213197
}
@@ -236,7 +220,7 @@ The feature management library provides functionality in ASP.NET Core and MVC to
236220
MVC controller and actions can require that a given feature, or one of any list of features, be enabled in order to execute. This can be done by using a `FeatureGateAttribute`, which can be found in the `Microsoft.FeatureManagement.Mvc` namespace.
237221

238222
``` C#
239-
[FeatureGate(MyFeatureFlags.FeatureX)]
223+
[FeatureGate("FeatureX")]
240224
public class HomeController : Controller
241225
{
242226
@@ -246,14 +230,14 @@ public class HomeController : Controller
246230
The `HomeController` above is gated by "FeatureX". "FeatureX" must be enabled before any action the `HomeController` contains can be executed.
247231

248232
``` C#
249-
[FeatureGate(MyFeatureFlags.FeatureY)]
233+
[FeatureGate("FeatureX")]
250234
public IActionResult Index()
251235
{
252236
return View();
253237
}
254238
```
255239

256-
The `Index` MVC action above requires "FeatureY" to be enabled before it can execute.
240+
The `Index` MVC action above requires "FeatureX" to be enabled before it can execute.
257241

258242
### Disabled Action Handling
259243

@@ -271,11 +255,19 @@ public interface IDisabledFeaturesHandler
271255
In MVC views `<feature>` tags can be used to conditionally render content based on whether a feature is enabled or not.
272256

273257
``` HTML+Razor
274-
<feature name=@nameof(MyFeatureFlags.FeatureX)>
258+
<feature name="FeatureX">
275259
<p>This can only be seen if 'FeatureX' is enabled.</p>
276260
</feature>
277261
```
278262

263+
You can also negate the tag helper evaluation to display content when a feature or set of features are disabled. By setting `negate="true"` in the example below, the content is only rendered if `FeatureX` is disabled.
264+
265+
``` HTML+Razor
266+
<feature negate="true" name="FeatureX">
267+
<p>This can only be seen if 'FeatureX' is disabled.</p>
268+
</feature>
269+
```
270+
279271
The `<feature>` tag requires a tag helper to work. This can be done by adding the feature management tag helper to the _ViewImports.cshtml_ file.
280272
``` HTML+Razor
281273
@addTagHelper *, Microsoft.FeatureManagement.AspNetCore
@@ -289,17 +281,17 @@ The feature management pipeline supports async MVC Action filters, which impleme
289281
``` C#
290282
services.AddMvc(o =>
291283
{
292-
o.Filters.AddForFeature<SomeMvcFilter>(nameof(MyFeatureFlags.FeatureV));
284+
o.Filters.AddForFeature<SomeMvcFilter>("FeatureX");
293285
});
294286
```
295287

296-
The code above adds an MVC filter named `SomeMvcFilter`. This filter is only triggered within the MVC pipeline if the feature it specifies, "FeatureV", is enabled.
288+
The code above adds an MVC filter named `SomeMvcFilter`. This filter is only triggered within the MVC pipeline if the feature it specifies, "FeatureX", is enabled.
297289

298290
### Razor Pages
299291
MVC Razor pages can require that a given feature, or one of any list of features, be enabled in order to execute. This can be done by using a `FeatureGateAttribute`, which can be found in the `Microsoft.FeatureManagement.Mvc` namespace.
300292

301293
``` C#
302-
[FeatureGate(MyFeatureFlags.FeatureU)]
294+
[FeatureGate("FeatureX")]
303295
public class IndexModel : PageModel
304296
{
305297
public void OnGet()
@@ -308,7 +300,7 @@ public class IndexModel : PageModel
308300
}
309301
```
310302

311-
The code above sets up a Razor page to require the "FeatureU" to be enabled. If the feature is not enabled, the page will generate an HTTP 404 (NotFound) result.
303+
The code above sets up a Razor page to require the "FeatureX" to be enabled. If the feature is not enabled, the page will generate an HTTP 404 (NotFound) result.
312304

313305
When used on Razor pages, the `FeatureGateAttribute` must be placed on the page handler type. It cannot be placed on individual handler methods.
314306

@@ -317,10 +309,10 @@ When used on Razor pages, the `FeatureGateAttribute` must be placed on the page
317309
The feature management library can be used to add application branches and middleware that execute conditionally based on feature state.
318310

319311
``` C#
320-
app.UseMiddlewareForFeature<ThirdPartyMiddleware>(nameof(MyFeatureFlags.FeatureU));
312+
app.UseMiddlewareForFeature<ThirdPartyMiddleware>("FeatureX");
321313
```
322314

323-
With the above call, the application adds a middleware component that only appears in the request pipeline if the feature "FeatureU" is enabled. If the feature is enabled/disabled during runtime, the middleware pipeline can be changed dynamically.
315+
With the above call, the application adds a middleware component that only appears in the request pipeline if the feature "FeatureX" is enabled. If the feature is enabled/disabled during runtime, the middleware pipeline can be changed dynamically.
324316

325317
This builds off the more generic capability to branch the entire application based on a feature.
326318

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT license.
33
//
4-
using Consoto.Banking.AccountService.FeatureFilters;
5-
6-
namespace Consoto.Banking.AccountService
4+
class AccountServiceContext : IAccountContext
75
{
8-
class AccountServiceContext : IAccountContext
9-
{
10-
public string AccountId { get; set; }
11-
}
12-
}
6+
public string AccountId { get; set; }
7+
}

examples/ConsoleApp/ConsoleApp.csproj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
55
<TargetFramework>net6.0</TargetFramework>
6-
<RootNamespace>Consoto.Banking.AccountService</RootNamespace>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
78
</PropertyGroup>
89

910
<ItemGroup>
Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,27 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT license.
33
//
4-
using Consoto.Banking.AccountService.FeatureFilters;
54
using Microsoft.Extensions.Configuration;
65
using Microsoft.FeatureManagement;
7-
using System;
8-
using System.Collections.Generic;
9-
using System.Threading.Tasks;
106

11-
namespace Consoto.Banking.AccountService.FeatureManagement
7+
/// <summary>
8+
/// A filter that uses the feature management context to ensure that the current task has the notion of an account id, and that the account id is allowed.
9+
/// This filter will only be executed if an object implementing <see cref="IAccountContext"/> is passed in during feature evaluation.
10+
/// </summary>
11+
[FilterAlias("AccountId")]
12+
class AccountIdFilter : IContextualFeatureFilter<IAccountContext>
1213
{
13-
/// <summary>
14-
/// A filter that uses the feature management context to ensure that the current task has the notion of an account id, and that the account id is allowed.
15-
/// This filter will only be executed if an object implementing <see cref="IAccountContext"/> is passed in during feature evaluation.
16-
/// </summary>
17-
[FilterAlias("AccountId")]
18-
class AccountIdFilter : IContextualFeatureFilter<IAccountContext>
14+
public Task<bool> EvaluateAsync(FeatureFilterEvaluationContext featureEvaluationContext, IAccountContext accountContext)
1915
{
20-
public Task<bool> EvaluateAsync(FeatureFilterEvaluationContext featureEvaluationContext, IAccountContext accountContext)
16+
if (string.IsNullOrEmpty(accountContext?.AccountId))
2117
{
22-
if (string.IsNullOrEmpty(accountContext?.AccountId))
23-
{
24-
throw new ArgumentNullException(nameof(accountContext));
25-
}
18+
throw new ArgumentNullException(nameof(accountContext));
19+
}
2620

27-
var allowedAccounts = new List<string>();
21+
var allowedAccounts = new List<string>();
2822

29-
featureEvaluationContext.Parameters.Bind("AllowedAccounts", allowedAccounts);
23+
featureEvaluationContext.Parameters.Bind("AllowedAccounts", allowedAccounts);
3024

31-
return Task.FromResult(allowedAccounts.Contains(accountContext.AccountId));
32-
}
25+
return Task.FromResult(allowedAccounts.Contains(accountContext.AccountId));
3326
}
3427
}
Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT license.
33
//
4-
namespace Consoto.Banking.AccountService.FeatureFilters
4+
public interface IAccountContext
55
{
6-
public interface IAccountContext
7-
{
8-
string AccountId { get; }
9-
}
6+
string AccountId { get; }
107
}

0 commit comments

Comments
 (0)