TechnicalDogsbody.Optimizely.FeatureFlagging 1.0.1

There is a newer version of this package available.
See the version list below for details.
dotnet add package TechnicalDogsbody.Optimizely.FeatureFlagging --version 1.0.1
                    
NuGet\Install-Package TechnicalDogsbody.Optimizely.FeatureFlagging -Version 1.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TechnicalDogsbody.Optimizely.FeatureFlagging" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TechnicalDogsbody.Optimizely.FeatureFlagging" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="TechnicalDogsbody.Optimizely.FeatureFlagging" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add TechnicalDogsbody.Optimizely.FeatureFlagging --version 1.0.1
                    
#r "nuget: TechnicalDogsbody.Optimizely.FeatureFlagging, 1.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package TechnicalDogsbody.Optimizely.FeatureFlagging@1.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=TechnicalDogsbody.Optimizely.FeatureFlagging&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=TechnicalDogsbody.Optimizely.FeatureFlagging&version=1.0.1
                    
Install as a Cake Tool

TechnicalDogsbody.Optimizely.FeatureFlagging

Optimizely Feature Flagging

NuGet NuGet Downloads License: MIT Build Code Quality CodeQL

Feature flag your Optimizely CMS content properties without code changes. Control property behaviour, validation, display settings, and metadata dynamically using Microsoft Feature Management.

Features

  • Full Property Control: Manage visibility, validation, editors, and metadata
  • Microsoft Feature Management: Built on industry-standard feature flagging
  • Type Safe: Compile-time validation of feature flag configurations
  • Performance Optimised: Scanner extensions run during content type initialisation

Installation

dotnet add package TechnicalDogsbody.Optimizely.FeatureFlagging

Or via Package Manager Console:

Install-Package TechnicalDogsbody.Optimizely.FeatureFlagging

Quick Start

1. Register Services

In your Program.cs or Startup.cs:

using TechnicalDogsbody.Optimizely.FeatureFlagging;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCms();
builder.Services.AddOptimizelyFeatureFlagging();

var app = builder.Build();

2. Configure Feature Flags

Add feature flags to your appsettings.json:

{
  "FeatureManagement": {
    "NewPropertyEditor": true,
    "BetaValidation": false,
    "ShowAdvancedOptions": true
  }
}

3. Use in Content Types

Apply feature-flagged attributes to your properties:

using EPiServer.Core;
using EPiServer.DataAnnotations;
using TechnicalDogsbody.Optimizely.FeatureFlagging;

[ContentType(DisplayName = "Article Page", GUID = "...")]
public class ArticlePage : PageData
{
    // Show/hide property based on feature flag
    [FeatureFlaggedScaffoldColumn("ShowAdvancedOptions", scaffoldWhenEnabled: true)]
    [Display(Name = "SEO Keywords", GroupName = "SEO")]
    public virtual string SeoKeywords { get; set; }

    // Change validation rules
    [FeatureFlaggedStringLength("BetaValidation",
        enabledMaximumLength: 500,
        disabledMaximumLength: 200)]
    [Display(Name = "Summary")]
    public virtual string Summary { get; set; }

    // Switch property editor
    [FeatureFlaggedClientEditor("NewPropertyEditor",
        EnabledClientEditor = "epi-cms/widget/HtmlEditor",
        DisabledClientEditor = "epi-cms/widget/Textarea")]
    [Display(Name = "Body Text")]
    public virtual string BodyText { get; set; }
}

Available Attributes

Display & Visibility

FeatureFlaggedDisplay

Change property name, description, tab, and order based on feature flags.

[FeatureFlaggedDisplay("RebrandFeature",
    EnabledName = "Hero Image",
    DisabledName = "Main Image",
    EnabledDescription = "Main hero banner image",
    DisabledDescription = "Primary content image",
    EnabledGroupName = "Content",
    DisabledGroupName = "Media")]
public virtual ContentReference Image { get; set; }
FeatureFlaggedScaffoldColumn

Show or hide properties in the CMS editor.

[FeatureFlaggedScaffoldColumn("ShowAdvancedSettings", scaffoldWhenEnabled: true)]
public virtual string AdvancedConfig { get; set; }
FeatureFlaggedIgnore

Completely ignore a property during content type scanning when the feature is disabled.

[FeatureFlaggedIgnore("ExperimentalFeatures")]
public virtual string ExperimentalProperty { get; set; }

Validation

FeatureFlaggedRequired

Toggle whether a property is required.

[FeatureFlaggedRequired("StrictValidation", requiredWhenEnabled: true)]
public virtual string AuthorName { get; set; }
FeatureFlaggedStringLength

Change string length validation rules.

[FeatureFlaggedStringLength("ExtendedContent",
    enabledMaximumLength: 1000,
    disabledMaximumLength: 500,
    EnabledMinimumLength = 10,
    DisabledMinimumLength = 5)]
public virtual string Description { get; set; }
FeatureFlaggedRange

Toggle numeric range validation.

[FeatureFlaggedRange("ExpandedLimits",
    enabledMinimum: 0,
    enabledMaximum: 1000,
    disabledMinimum: 0,
    disabledMaximum: 100)]
public virtual int Quantity { get; set; }
FeatureFlaggedRegularExpression

Switch validation patterns.

[FeatureFlaggedRegularExpression("InternationalFormat",
    enabledPattern: @"^\+?[1-9]\d{1,14}$",  // E.164 international
    disabledPattern: @"^\d{3}-\d{3}-\d{4}$")] // US format
public virtual string PhoneNumber { get; set; }

Editors & UI

FeatureFlaggedClientEditor

Switch between different property editors.

[FeatureFlaggedClientEditor("RichTextEditor",
    EnabledClientEditor = "epi-cms/widget/HtmlEditor",
    DisabledClientEditor = "epi-cms/widget/Textarea")]
public virtual string Content { get; set; }
FeatureFlaggedUIHint

Toggle UI hints for property rendering.

[FeatureFlaggedUiHint("DateTimePicker",
    EnabledUiHint = "epi-cms/widget/DateTimeEditor",
    DisabledUiHint = "epi-cms/widget/Textarea")]
public virtual string ScheduledDate { get; set; }
FeatureFlaggedEditable

Control whether properties are editable or read-only.

[FeatureFlaggedEditable("AllowEditing", editableWhenEnabled: true)]
public virtual string SystemGenerated { get; set; }

Content Model Settings

FeatureFlaggedContentType

Show or hide entire content types based on feature flags.

// Content type visible when feature is enabled
[ContentType(DisplayName = "New Article Page", GUID = "...")]
[FeatureFlaggedContentType("NewArticleFeature", VisibleWhenEnabled = true)]
public class NewArticlePage : PageData
{
    // Properties...
}

// Content type visible when feature is disabled (for deprecation)
[ContentType(DisplayName = "Old Article Page", GUID = "...")]
[FeatureFlaggedContentType("NewArticleFeature", VisibleWhenEnabled = false)]
public class OldArticlePage : PageData
{
    // Properties...
}

Use cases:

  • Gradual rollout of new content types
  • A/B testing different content structures
  • Deprecating old content types while maintaining data
  • Environment-specific content types
FeatureFlaggedBackingType

Switch the underlying PropertyData type.

[FeatureFlaggedBackingType("LongContent",
    typeof(PropertyLongString),
    typeof(PropertyString))]
public virtual string FlexibleContent { get; set; }
FeatureFlaggedCultureSpecific

Toggle whether properties are culture-specific.

[FeatureFlaggedCultureSpecific("MultiLanguage", cultureSpecificWhenEnabled: true)]
public virtual string LocalizedContent { get; set; }
FeatureFlaggedSearchable

Control property searchability.

[FeatureFlaggedSearchable("SearchOptimization", searchableWhenEnabled: true)]
public virtual string ProductDescription { get; set; }

Advanced Configuration

Custom Feature Management Setup

If you need custom Feature Management configuration, register extensions separately:

using Microsoft.FeatureManagement;
using TechnicalDogsbody.Optimizely.FeatureFlagging;

var builder = WebApplication.CreateBuilder(args);

// Custom Feature Management setup
builder.Services.AddFeatureManagement(builder.Configuration.GetSection("Features"))
    .WithTargeting<CustomTargetingContextAccessor>();

// Register only the scanner extensions
builder.Services.AddOptimizelyFeatureFlaggingExtensions();

Environment-Specific Configuration

Override feature flags per environment:

appsettings.Development.json:

{
  "FeatureManagement": {
    "ExperimentalFeatures": true,
    "BetaValidation": true
  }
}

appsettings.Production.json:

{
  "FeatureManagement": {
    "ExperimentalFeatures": false,
    "BetaValidation": false
  }
}

Best Practices

Feature Flag Naming

Use descriptive, purposeful names:

// Good
[FeatureFlaggedRequired("EnforceGdprCompliance", requiredWhenEnabled: true)]

// Avoid
[FeatureFlaggedRequired("Flag1", requiredWhenEnabled: true)]

Combining Attributes

Apply multiple feature-flagged attributes to the same property:

[FeatureFlaggedRequired("StrictMode", requiredWhenEnabled: true)]
[FeatureFlaggedStringLength("StrictMode",
    enabledMaximumLength: 100,
    disabledMaximumLength: 500)]
[FeatureFlaggedEditable("AllowEdits", editableWhenEnabled: true)]
public virtual string Title { get; set; }

Testing

Test content types with different feature flag configurations:

[Test]
public void Property_Should_Be_Required_When_Feature_Enabled()
{
    // Arrange
    var featureManager = new Mock<IFeatureManager>();
    featureManager.Setup(x => x.IsEnabledAsync("StrictValidation"))
        .ReturnsAsync(true);

    // Act & Assert
    var attribute = new FeatureFlaggedRequiredAttribute("StrictValidation");
    // Test validation logic
}

Performance

  • Scanner extensions run once during content type initialisation
  • Feature flag checks are cached by Microsoft Feature Management
  • No runtime performance impact on content rendering

Migration Guide

From Standard Attributes

Replace standard Optimizely attributes with feature-flagged versions:

Before:

[Required]
[Display(Name = "Title", GroupName = "Content")]
public virtual string Title { get; set; }

After:

[FeatureFlaggedRequired("RequireTitle", requiredWhenEnabled: true)]
[FeatureFlaggedDisplay("ContentRestructure",
    EnabledName = "Title",
    EnabledGroupName = "Content")]
public virtual string Title { get; set; }

Gradual Rollout

Introduce feature flags gradually:

  1. Add feature flag to appsettings.json (disabled)
  2. Apply feature-flagged attribute
  3. Test with flag enabled in development
  4. Enable flag in production when ready

Troubleshooting

Properties Not Updating

Problem: Changes to feature flags not reflected in CMS.

Solution: Content type metadata is cached. Restart the application or clear Optimizely's content type cache.

Feature Manager Not Found

Problem: IFeatureManager cannot be resolved.

Solution: Ensure AddOptimizelyFeatureFlagging() is called in service registration.

Attributes Not Applied

Problem: Feature-flagged attributes have no effect.

Solution: Check that scanner extensions are registered. Use AddOptimizelyFeatureFlagging() or manually register extensions.

Requirements

  • Optimizely CMS 12.0 or higher
  • .NET 8.0, 9.0, or 10.0
  • Microsoft.FeatureManagement.AspNetCore 3.0 or higher

Contributing

Contributions are welcome! Please submit issues and pull requests to the GitHub repository.

Development Setup

  1. Clone the repository: git clone https://github.com/technicaldogsbody/Optimizely.FeatureFlagging.git
  2. Restore packages: dotnet restore
  3. Build: dotnet build
  4. Run tests: dotnet test

License

This project is licensed under the MIT License. See the LICENSE file for details.

Support

For issues, questions, or feature requests:

Changelog

See CHANGELOG.md for version history and release notes.

Acknowledgements

Built on:


Made with ❤️ by TechnicalDogsbody

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.0 35 2/18/2026
1.0.2 84 2/5/2026
1.0.1 81 2/5/2026
1.0.0 90 2/5/2026

See CHANGELOG.md for release notes