feat(variants): allow in-memory objects - #615
Степан (Stepami) wants to merge 11 commits into
Conversation
|
Hey, Zhiyuan Liang (@zhiyuanliang-ms)! could you take a look please? |
|
My understanding is that this enhancement is intended specifically for the custom feature definition provider scenario. Is that correct? |
yes it is. right now i am implementing a custom provider. i need to do something like this in order to put data from an external storage into the definition: new VariantDefinition
{
Name = "...",
ConfigurationValue =
new ConfigurationBuilder()
.AddInMemoryCollection([new("Value", variantValue.ToString())])
.Build()
.GetSection("Value")
} |
|
And the idea came to me from the |
3ba89be to
90fe16e
Compare
|
Hey, Степан (@Stepami) We discussed this scenario internally, and we agree that supporting in-memory variant values is useful for custom One concern we have is that: for customers using the built-in Before adding a new public property, we would like to think through what the story should be for those customers as well. Ideally, this should feel like a generally useful part of the variant configuration model, rather than an API surface that only applies when a custom provider is used. For example, one question we are considering is whether the built-in provider should also be able to populate an object representation from the configured variant value, or whether there is another API shape that gives both built-in and custom provider users a consistent way to consume variant configuration. |
|
I have been thinking about the following options:
I used to think this is the best option. But after a second look, I realize the conversion will not be lossless. For example, a numeric value could end up being reconstructed as a string.
For example Variant variant = await featureManager.GetVariantAsync("MyFeature");
MySettings settings = variant.Configuration.Get<MySettings>();I am thinking about adding a new extension method to Variant variant = await featureManager.GetVariantAsync("MyFeature");
MySettings settings = variant.GetConfiguration<MySettings>();Or we can even add a new extension method to MySettings settings = await featureManager.GetVariantConfigurationAsync<MySettings>("MyFeature");This can unify the story that people who uses custom feature definition provider with in-memory object and built-in configuration feature defition provider. |
|
hey Zhiyuan Liang (@zhiyuanliang-ms) ! I agree that I'll take a few days to think this through |
|
hey Zhiyuan Liang (@zhiyuanliang-ms) I decided to do both populate the data supplied by the buil-in configuration provider and create the common API used to consume variant configuration through extension method. ConfigurationObjectNow GetConfiguration
I placed the method on |
96d51b8 to
1e8b756
Compare
|
hey Zhiyuan Liang (@zhiyuanliang-ms)! have you seen the update describe above? |
|
hey, Степан (@Stepami) I am looking into it. |
| }; | ||
| } | ||
|
|
||
| private static IReadOnlyDictionary<string, string> CreateConfigurationObject(IConfigurationSection section) |
There was a problem hiding this comment.
I’m concerned about populating ConfigurationObject with a flattened IReadOnlyDictionary<string, string>.
For example, the following configuration
{
"configuration_value": {
"Layout": {
"Width": 100,
"Theme": "dark"
},
"Regions": [
{
"Name": "header"
},
{
"Name": "footer"
}
]
}
}will be converted to
new Dictionary<string, string>
{
["Layout:Width"] = "100",
["Layout:Theme"] = "dark",
["Regions:0:Name"] = "header",
["Regions:1:Name"] = "footer"
};There was a problem hiding this comment.
reverted object population from config
|
I am currently leaning to keep the two representations provider-specific rather than trying to populate both. For a custom To unify the customer experience, we can offer Variant variant = await featureManager.GetVariantAsync("MyFeature");
MySettings settings = variant.GetConfiguration<MySettings>();The method name can be either |
hey Zhiyuan Liang (@zhiyuanliang-ms) ! i can keep the two representations provider-specific. that's what i originally suggested. the extension method |
|
Hey, Степан (@Stepami) I discussed this PR with Jimmy Campbell (@jimmyca15) yesterday. We have not reached a conclusion yet, but I wanted to share some of our thinking. One concern with the current We are considering whether the feature manager could know the expected configuration type for each variant and bind the configuration earlier, ideally when the feature definition is created. Since feature definitions are cached, the typed configuration could then be cached as part of the variant definition instead of being rebound on every consumption. The unresolved question is how to associate a variant configuration with its expected type. The configuration provider does not naturally have that type information when it creates the feature definition. |
|
hey Zhiyuan Liang (@zhiyuanliang-ms) ! SummaryI made the change that avoids repeated configuration binding in Each provider-created variant definition with a The cache stores entries by exact The holder is transferred from MotivationThe provider owns feature-definition lifetime and reload handling, but it does not know the type requested by the caller. The provider already caches FeatureDefinition instances and replaces them after configuration reloads. Attaching the typed cache to that object graph gives it the same natural lifetime:
Under the hood
The warm path is reduced to a dictionary lookup, |
Why this PR?
Implementing
IFeatureDefinitionProvidermakes it inconvinient to fill variant bound values if they don't come from configuration.Similarly to feature filters I decided to introduce an object that can be used as an alternative
ConfigurationValue. CustomIFeatureDefinitionProviderimplementations can populate this property directly instead of constructing anIConfigurationSectioninstance.Visible Changes
Microsoft.FeatureManagement.VariantDefinition.ConfigurationObjectMicrosoft.FeatureManagement.Variant.ConfigurationObject