This repository has been archived by the owner on Sep 2, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6
/
bootstrappertutorial.html
230 lines (203 loc) · 17.4 KB
/
bootstrappertutorial.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
---
layout: documentation
title: Bootstrapper
teaser: Appccelerates your application bootstrapping mechanism
navigation:
- name: Tutorial
link: bootstrappertutorial.html
- name: Configuration Sections
link: bootstrapperconfigurationsections.html
- name: Syntax
link: bootstrappersyntax.html
- name: Customization
link: bootstrappercustomizations.html
- name: Reports
link: bootstrapperreports.html
- name: Tips and Tricks
link: bootstrappertipsandtricks.html
- name: Specifications
link: bootstrapperspecifications.html
---
<h2><a name="Introduction"></a>Introduction<a href="#Introduction" class="section_anchor"></a></h2>
<p>To get started with the bootstrapper you need the following three things: <ol><li>Extension interface </li><li>Strategy </li><li>Bootstrapper setup </li></ol></p>
<h3><a name="Extension_interface"></a>Extension interface<a href="#Extension_interface" class="section_anchor"></a></h3><p>The extension interface defines the extension points which are called by the bootstrapper. The custom extension interface must inherit from <tt>IExtension</tt> and can only declare methods which return <tt>void</tt> as extension points. A very basic extension interface could look like the following: </p>
<script type="syntaxhighlighter" class="brush: csharp"><![CDATA[ public interface ICustomExtension : IExtension
{
void Start();
void Stop();
}]]>
</script>
<p>The extension interface above would indicate that the applications extension points are: <ul><li><tt>Start</tt> </li><li><tt>Stop</tt> </li></ul>That's basically it for the extension interface. Although we generally advice to build a base class which defines virtual members for the extension points. For example: </p>
<script type="syntaxhighlighter" class="brush: csharp"><![CDATA[ public abstract class CustomExtensionBase : ICustomExtension
{
public string Name
{
get { return this.GetType().FullNameToString(); }
}
public virtual void Start() { }
public virtual void Stop() { }
public abstract string Describe();
}]]></script>
<p>This allows inheritors to override only the extension points that they are interested in. </p>
<p><i>Note: <tt>IExtension</tt> inherits from <tt>IDescribable</tt> which defines a name and a description. The name and the description are consumed by the reporting capability. The more expressive your names and descriptions are the more meaningful your report will be!</i> </p><h3><a name="Strategy"></a>Strategy<a href="#Strategy" class="section_anchor"></a></h3><p>The strategy defines the order of execution for extension points. The custom strategy must inherit from <tt>IStrategy</tt>. For convenience there is a abstract base class <tt>AbstractStrategy{TExtension}</tt> which simplifies defining a custom strategy. A very basic strategy could look like the following: </p>
<script type="syntaxhighlighter" class="brush: csharp"><![CDATA[ public class CustomStrategy : AbstractStrategy<ICustomExtension>
{
protected override void DefineRunSyntax(ISyntaxBuilder<ICustomExtension> builder)
{
builder.Execute(extension => extension.Start());
}
protected override void DefineShutdownSyntax(ISyntaxBuilder<ICustomExtension> builder)
{
builder.Execute(extension => extension.Stop());
}
}]]></script>
<p>The strategy above would configure the applications startup and shutdown phase to: <ul><li>During startup call the extension point <tt>Start</tt> on all registered extensions </li><li>During shutdown call the extension point <tt>Stop</tt> on all registered extensions </li></ul>Now let's look how the custom extension interface and the strategy get combined together in the bootstrapper. </p>
<h3><a name="Bootstrapper_setup"></a>Bootstrapper setup<a href="#Bootstrapper_setup" class="section_anchor"></a></h3>
<p>The bootstrapper setup is simply and straight forward. You can always follow the following steps: <ol><li>Instantiate a new <tt>DefaultBoostrapper<TExtension></tt> </li><li>Instantiate a new strategy </li><li>Initialize the bootstrapper with the strategy </li><li>Add the required extensions </li><li>Call <tt>Run</tt> when the application starts </li><li>Call <tt>Shutdown</tt> and then <tt>Dispose</tt> when the application stops. </li></ol></p>
<script type="syntaxhighlighter" class="brush: csharp"><![CDATA[ using(var bootstrapper = new DefaultBootstrapper<ICustomExtension>())
{
var strategy = new CustomStrategy();
bootstrapper.Initialize(strategy);
bootstrapper.AddExtension(new FirstCustomExtension());
bootstrapper.AddExtension(...);
bootstrapper.AddExtension(new NthCustomExtension());
bootstrapper.Run();
// When application finished call
bootstrapper.Shutdown();
}]]></script>
<p><i>Note:<tt>DefaultBootstrapper{TExtension}</tt> and <tt>AbstractStrategy{TExtension}</tt> implement <tt>IDisposable</tt>. The bootstrapper takes care of disposing the strategy and the extensions if the correct behavior is attached. If you are using FxCop to check your compiled code it will produces warnings that you should dispose the strategy and the extensions which implement <tt>IDisposable</tt> before the references move out of scope. You can safely suppress these warnings.</i> </p>
<h2><a name="Advanced"></a>Advanced<a href="#Advanced" class="section_anchor"></a></h2>
<p>The bootstrapper can do more! Let us look into a more complex scenario. Often it is required to collect context information during the bootstrapping process and pass this information to the extension points. </p>
<p>Imagine you are using an inversion of control container which intakes <tt>IModule</tt> implementations to register dependencies during the bootstrapping process. All <tt>IModule</tt> implementations need to be passed into the <tt>IContainer</tt> implementation upon construction. For backward compatibility the bootstrapper must automatically check whether an extension implements <tt>IModuleProvider</tt> and register the provided modules on the <tt>IContainer</tt> implementation. </p>
<p>The application we are designing allocates both managed and unmanaged resources which need to be removed when the application is shutting down. To get this behavior some extension occasionally implement <tt>IDisposable</tt> which needs to be called when shutting down. </p><p>Furthermore the application uses configuration sections to be able to overwrite some default configurations without recompiling the application. These configuration entries must be parsed and passed to the extensions which rely on their existence. </p>
<h3><a name="Extension_interface"></a>Extension interface<a href="#Extension_interface" class="section_anchor"></a></h3>
<p>A custom extension interface for this example could look like the following: </p>
<script type="syntaxhighlighter" class="brush: csharp"><![CDATA[ public interface ICustomExtension : IExtension
{
void Start();
void ContainerInitializing(ICollection<IModule> modules);
void ContainerInitialized(IContainer container);
void Ready();
void Stop();
}]]></script>
<p>The extension interface above would indicate that the applications extension points are: <ul><li><tt>Start</tt> </li><li><tt>ContainerInitializing</tt> which allows to add <tt>IModule</tt> implementations </li><li><tt>ContainerInitialized</tt> which allows to resolve dependencies on the <tt>IContainer</tt> </li><li><tt>Ready</tt> </li><li><tt>Stop</tt> </li></ul> An abstract base class for this use case: </p>
<script type="syntaxhighlighter" class="brush: csharp"><![CDATA[ public abstract class CustomExtensionBase : ICustomExtension
{
public string Name
{
get { return this.GetType().FullNameToString(); }
}
protected IContainer Container { get; private set; }
public virtual void Start() { }
public virtual void ContainerInitializing(ICollection<IModule> modules) { }
public void ContainerInitialized(IContainer container)
{
this.Container = container;
this.ContainerInitializedCore(container);
}
public virtual void Ready() { }
public virtual void Stop() { }
public abstract string Describe();
protected virtual void ContainerInitializedCore(IContainer container) { }
}]]></script>
<p>This allows inheritors to override only the extension points that they are interested in. The extension interface does not include: <ul><li>the fact that the extensions must be checked whether they implement <tt>IModuleProvider</tt> </li><li>the fact that the extensions must be checked whether they implement <tt>IDisposable</tt> </li><li>the fact that configuration sections must be loaded for an extension if available </li></ul> How can we extend the bootstrapping mechanism without modifying the custom extension interface? We need behaviors! </p><h3><a name="Behaviors"></a>Behaviors<a href="#Behaviors" class="section_anchor"></a></h3><p>Behaviors allow extending the bootstrapping process in an aspect oriented style. Behaviors gain access to extensions which are participating in the bootstrapper process and can therefore influence them for example by injecting additional runtime information into an extension. Behaviors must implement <tt>IBehavior<TExtension></tt>. They automatically gain access to all extensions participating the bootstrapping process. Behaviors are executed before the corresponding extension point is called. </p><p>Let's us look into an example. Imagine you have the following module provider interface below: </p><script type="syntaxhighlighter" class="brush: csharp"><![CDATA[ public interface IModuleProvider : IEnumerable<IModule> { }]]></script><p>The behavior below demonstrates how extensions can be scanned whether they are module providers. If an extension implements <tt>IModuleProvider</tt> all provided modules are automatically collected in the module collection the behavior receives upon construction. </p><script type="syntaxhighlighter" class="brush: csharp"><![CDATA[ public class ModuleProvidingBehavior : IBehavior<ICustomExtension>
{
private readonly ICollection<IModule> modules;
public ModuleProvidingBehavior(ICollection<IModule> modules)
{
this.modules = modules;
}
public string Name { get { return this.GetType().FullNameToString(); } }
public void Behave(IEnumerable<ICustomExtension> extensions)
{
extensions.OfType<IModuleProvider>()
.SelectMany(provider => provider, (provider, module) => module)
.ToList()
.ForEach(module => this.modules.Add(module));
}
public string Describe() { return "Collects all IModule's on extensions which implement IModuleProvider during bootstrapping."; }
}]]></script><p>Below is a sample <tt>IModule</tt> implementation which uses a made up fluent syntax to register dependencies. </p><script type="syntaxhighlighter" class="brush: csharp"><![CDATA[ public class CustomModule : IModule
{
public void Load(IContainer container)
{
container.Register<DependencyUsingBehavior>().ToSelf();
container.Register<IDependency>().To<Dependency>();
container.Register<IBehavior<ICustomExtension>>().ToMethod(ctn => ctn.Resolve<DependencyUsingBehavior>());
}
}]]></script><p>Below an extension which implements IModuleProvider and yield returns a custom module. <i>Note:Of course the custom module could also have been added by overriding <tt>ContainerInitializing</tt> and adding the module to the collection. But the sample should demonstrate the power of behaviors.</i> </p><script type="syntaxhighlighter" class="brush: csharp"><![CDATA[ public class ExtensionWhichIsModuleProvider : CustomExtensionBase, IModuleProvider
{
public IEnumerator<IModule> GetEnumerator()
{
yield return new CustomModule();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
public override string Describe() { return "Extension which implements IModuleProvider"; }
}]]></script><p>The <tt>DependencyUsingBehavior</tt> is a special behavior which does not consume extensions but execute an operation on a dependency. The dependency is resolved by using constructor injection. To enable constructor injection when constructing the <tt>DependencyUsingBehavior</tt> the <tt>IContainer</tt> implementation must already be built up and the required dependencies registered. </p><script type="syntaxhighlighter" class="brush: csharp"><![CDATA[ public class DependencyUsingBehavior : IBehavior<ICustomExtension>
{
private readonly IDependency dependency;
public DependencyUsingBehavior(IDependency dependency)
{
this.dependency = dependency;
}
public string Name { get { return this.GetType().FullNameToString(); } }
public void Behave(IEnumerable<ICustomExtension> extensions)
{
this.dependency.Hello();
}
public string Describe() { "Uses IDependency and executes \"Hello()\" in the provided dependency."; }
}]]></script><p>The <tt>DisposeExtensionBehavior</tt> calls Dispose on all extensions which implement <tt>IDisposable</tt>. </p><script type="syntaxhighlighter" class="brush: csharp"><![CDATA[ public class DisposeExtensionBehavior : IBehavior<IExtension>
{
public string Name { get { return this.GetType().FullNameToString(); } }
public void Behave(IEnumerable<IExtension> extensions)
{
foreach (IDisposable extension in extensions.OfType<IDisposable>())
{
extension.Dispose();
}
}
public string Describe() { return "Disposes all extensions which implement IDisposable."; }
}]]></script><p>Now all these pieces must be sticked together in the strategy. Let's look how to define it. </p><h3><a name="Strategy"></a>Strategy<a href="#Strategy" class="section_anchor"></a></h3><p>The strategy defines the order of execution for extension points and behaviors. The custom strategy must inherit from <tt>IStrategy</tt>. For convenience, use the provided abstract base class <tt>AbstractStrategy{TExtension}</tt> which simplifies defining a custom strategy. The strategy could look like the following: </p><script type="syntaxhighlighter" class="brush: csharp"><![CDATA[ public class CustomStrategy : AbstractStrategy<ICustomExtension>
{
private readonly Collection<IModule> modules;
private readonly Lazy<IContainer> container;
public CustomStrategy()
{
this.modules = new Collection<IModule>();
this.container = new Lazy<IContainer>(() => new Container());
}
protected override void DefineRunSyntax(ISyntaxBuilder<ICustomExtension> builder)
{
builder
.Begin
.With(new ConfigurationSectionBehavior())
.With(new ExtensionConfigurationSectionBehavior())
.Execute(extension => extension.Start())
.Execute(() => this.modules, (extension, modules) => extension.ContainerInitializing(modules))
.With(modules => new ModuleProvidingBehavior(modules))
.Execute(() => this.BuildContainer(), (extension, container) => extension.ContainerInitialized(ctx))
.With(container => container.Resolve<IBehavior<ICustomExtension>>())
.Execute(extension => extension.Ready());
}
protected override void DefineShutdownSyntax(ISyntaxBuilder<ICustomExtension> builder)
{
builder
.Execute(extension => extension.Stop())
.End.With(new DisposeExtensionBehavior());
}
protected override void Dispose(bool disposing)
{
// Dispose the container
base.Dispose(disposing);
}
private IContainer BuildContainer()
{
var lazyContainer = this.container.Value;
foreach (IModule module in this.modules) {
module.Load(lazyContainer);
}
return lazyContainer;
}
}]]></script><p>The strategy above would configure the applications startup and shutdown phase to: <ul><li>During startup </li><ul><li>before calling any extension point load configuration sections </li><li>and then load extension configuration sections. </li><li>Call the extension point <tt>Start</tt> on all registered extensions </li><li>Initialize the context with <tt>this.modules</tt> and call the extension point <tt>ContainerInitializing</tt> with the context on all registered extensions </li><ul><li>Execute the behavior <tt>ModuleProvidingBehavior</tt> which gains access to the context. </li></ul><li>Initialize the context by calling <tt>BuildContainer</tt> and call the extension point <tt>ContainerInitialized</tt> with the context on all registered extensions </li><ul><li>Execute the lazy resolved <tt>IBehavior<ICustomExtension></tt> </li></ul><li>Call the extension point <tt>Ready</tt> on all registered extensions </li></ul><li>During shutdown </li><ul><li>call the extension point <tt>Stop</tt> on all registered extensions </li><li>dispose all extensions which implement <tt>IDisposable</tt> </li></ul></ul></p>