-
-
Notifications
You must be signed in to change notification settings - Fork 5
Extending FeatureSwitch library
Currently FeatureSwitch comes with some built-in strategies. However it's natural that there is a need to extend FeatureSwitch and add new strategies. This is really easy to implement. FeatureSwitch recognizes two types of strategies:
- Initializable: these strategies take part only during FeatureSwitch initialization routine and are called while feature set context is built-up. To create initializable strategy you need to implement
FeatureSwitch.Strategies.IStrategy
interface. - Read-only: read-only strategies are strategies that are able to read form the underlying storage initial value of the feature but not able to store or save it back. There will be disabled checkbox for features that are marked with these strategies in Asp.Net control panel. To create initializable strategy you need to implement
FeatureSwitch.Strategies.IStrategyStorageReader
interface. - Writeable: these strategies are able to update underlying storage and save new state of the feature. Features marked with these strategies will have enabled checkbox in Asp.Net control panel. To create initializable strategy you need to implement
FeatureSwitch.Strategies.IStrategyStorageWriter
interface.
In order to implement your new initializable strategy it’s enough to implement IStrategy
interface.
public class EmptyStrategyImpl : IStrategy
{
public void Initialize(ConfigurationContext configurationContext)
{
}
}
We will get back to ConfigurationContext
a bit later. So as you can see this strategy is called when feature set context is built by passing configuration context to the Initialize
method.
In order to implement your new read-only strategy it’s enough to implement IStrategyStorageReader
interface.
public class SampleReaderImpl : IStrategyStorageReader
{
public void Initialize(ConfigurationContext configurationContext)
{
throw new System.NotImplementedException();
}
public bool Read()
{
throw new System.NotImplementedException();
}
}
As you can see there is no feature identification mentioned in Read
method. Identification key of the feature (one that is used in attribute definition).
[AppSettings(Key = "MySampleFeatureKey")]
public class MySampleFeature : BaseFeature
{
You can access feature key via ConfigurationContext.Key
(that is passed in Initialize
method).
To get rid of Initialize
method and not bothering yourself to store configurationContext
somewhere, you can inherit from FeatureSwitch.Strategies.Implementations.BaseStrategyReaderImpl
. It will take care of this.
public class SampleReaderImpl : BaseStrategyReaderImpl
{
public override bool Read()
{
throw new System.NotImplementedException();
}
}
To create writable strategy you need to either implement IStrategyStorageWriter
:
public class SampleWriterImpl : IStrategyStorageWriter
{
public void Initialize(ConfigurationContext configurationContext)
{
throw new System.NotImplementedException();
}
public bool Read()
{
throw new System.NotImplementedException();
}
public void Write(bool state)
{
throw new System.NotImplementedException();
}
}
Or you can inherit from FeatureSwitch.Strategies.BaseStrategyImpl
to reduce code a bit:
public class SampleWriterImpl : BaseStrategyImpl
{
public override bool Read()
{
throw new System.NotImplementedException();
}
public override void Write(bool state)
{
throw new System.NotImplementedException();
}
}
Strategies are constructed using dependency injection services which means that strategies with constructor injections are supported out-of-box. So strategies like these will be constructed as long as necessary dependencies will be available in IoC (inversion of control) container.
private readonly ISampleInjectedInterface _sample;
public StrategyWithParameterReader(ISampleInjectedInterface sample)
{
_sample = sample;
}
When new strategy has been implemented it’s important that you enable it. To enable new strategy you need to:
- Create associated attribute that will be used to map your new strategy to particular feature marking that this feature is backed up by your strategy
- Register this mapping in FeatureSwitch context.
So, to create mapping attribute – you need just new class inheriting from FeatureStrategyAttribute
:
public class MyNewStrategy : FeatureStrategyAttribute
{
}
And you need to register new strategy implementation with particular attribute (if you are in EPiServer context – you can use EPiServer’s InitializableModule, or if you are on your own – you can use custom made initializable module to register this mapping):
var builder = new FeatureSetBuilder(new StructureMapDependencyContainer());
builder.Build(ctx =>
{
ctx.ForStrategy<MyNewStrategy>().Use<MyNewStrategyImpl>();
});
And now you can use your new strategy on feature:
[MyNewStrategy(Key = "MySampleFeatureKey")]
public class MySampleFeature : BaseFeature
{
Starting from v1.3.1 there is no need to register strategy mapping inside FeatureSetContext. Now you can define mapping directly inside attribute
:
public class MyNewStrategy : FeatureStrategyAttribute
{
public override Type DefaultImplementation
{
get
{
return typeof(MyNewStrategyImpl);
}
}
}
Strategy registration in FeatureSetContext is still supported and is intended to be used for swapping already registered strategy implementation with new one.
ConfigurationContext
is something that is needed for the strategy to initialize properly and get some info about feature “attached” to.
ConfigurationContext
is passed when strategy is initialized during feature set context build process and passed to Initialize
method:
public interface IStrategy
{
void Initialize(ConfigurationContext configurationContext);
}
Currently there is no built-in support for customizing configuration context (providing ways to extend and hook into configuration context creation process), but this is something I’m looking into in future releases of FeatureSwitch library.
So in summary let's implement IsLocalRequest
feature to check either we need to disable or enable some sort of functionality for local requests (one sample could be - that you don't need to include tracking script inside your markup if request is local).
This is source code for feature itself:
[IsLocalRequestAttribute]
public class IsLocalRequest : BaseFeature
{
}
You need to provide a bit of metadata to map from strategy to its implementation. This is done via attribute
's DefaultImplementation
property (or via FeatureSetContext
registration):
public class IsLocalRequestAttribute : FeatureStrategyAttribute
{
public IsLocalRequestAttribute()
{
Key = "...";
}
public override Type DefaultImplementation
{
get
{
return typeof(IsLocalRequestStrategyImpl);
}
}
}
Using DefaultImplementation
you can now more quickly add required mapping (previously override using ForStrategy<TStrategy>().Use<TImplementation>()
was needed inside IoC container).
And strategy implementation is pretty straight forward:
public class IsLocalRequestStrategyImpl : BaseStrategyReaderImpl
{
public override bool Read()
{
return HttpContext.Current != null && HttpContext.Current.Request.IsLocal;
}
}