-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathindex.html
More file actions
244 lines (207 loc) · 23.3 KB
/
Copy pathindex.html
File metadata and controls
244 lines (207 loc) · 23.3 KB
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
231
232
233
234
235
236
237
238
239
240
241
242
243
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<meta name="generator" content="Bootply" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>StructureMap - Interception and Decorators</title>
<link href="/content/bootstrap.min.css" rel="stylesheet" type="text/css" />
<link href="/content/prism.css" rel="stylesheet" type="text/css" />
<link href="/content/theme.css" rel="stylesheet" type="text/css" />
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" />
<link rel="icon" type="image/x-icon" href="/content/images/structuremap-logo.ico">
<link rel="icon" type="image/png" href="/content/images/structuremap-logo-60x60.png">
<link rel="apple-touch-icon" type="image/png" href="/content/images/structuremap-logo-60x60.png">
<link rel="apple-touch-icon" type="image/png" sizes="72x72" href="/content/images/structuremap-logo-72x72.png">
<link rel="apple-touch-icon" type="image/png" sizes="114x114" href="/content/images/structuremap-logo-114x114.png">
<link rel="apple-touch-icon" type="image/png" sizes="144x144" href="/content/images/structuremap-logo-144x144.png">
<!-- CSS code from Bootply.com editor -->
<link href="/content/affix.css" rel="stylesheet" type="text/css" />
</head>
<!-- HTML code from Bootply.com editor -->
<body >
<a href="https://github.com/structuremap/structuremap"><img style="z-index: 5000; position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/e7bbb0521b397edbd5fe43e7f760759336b5e05f/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677265656e5f3030373230302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_green_007200.png"></a>
<nav class="navbar navbar-default navbar-fixed-top" role="banner">
<div class="container">
<div class="navbar-header">
<a href="/" class="navbar-brand">StructureMap 4.7.0</a>
</div>
<nav class="collapse navbar-collapse" role="navigation">
<ul class="nav navbar-nav pull-right">
<li>
<a href="/quickstart">A Gentle Quickstart</a>
</li>
<li>
<a href="/documentation">Documentation</a>
</li>
<li>
<a href="https://groups.google.com/forum/#!forum/structuremap-users">Google Group</a>
</li>
<li>
<a href="https://gitter.im/structuremap/structuremap?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"><img src="https://camo.githubusercontent.com/da2edb525cde1455a622c58c0effc3a90b9a181c/68747470733a2f2f6261646765732e6769747465722e696d2f4a6f696e253230436861742e737667" alt="Join the chat at https://gitter.im/structuremap/structuremap" data-canonical-src="https://badges.gitter.im/Join%20Chat.svg" style="max-width:100%;"></a>
</li>
<li><a href="/generics" title="Generic Types">Previous</a></li>
<li><a href="/interpreting-exceptions" title="Interpreting Exceptions">Next</a></li>
</ul>
<div class="navbar-form navbar-left" role="search">
<div class="form-group">
<input id="search" type="search" class="form-control" placeholder="Search">
</div>
</div>
</nav>
</div>
</nav>
<div class="container">
<nav class="navbar-inverse">
<ol class="breadcrumb"><li><a href="/">StructureMap</a></li><li class="active">Interception and Decorators</li></ol>
</nav>
</div>
<!--main-->
<div class="container">
<div class="row">
<!--left-->
<div class="col-md-3" id="leftCol">
<ul class="nav nav-stacked affix" id="sidebar">
<li><h3><img src="/content/images/structuremap-logo-210x210.png" alt="StructureMap 4.7.0" width="210" height="210"></h3></li>
<li><h3 class="no-margin">Next</h3><p><a href="/interpreting-exceptions">Interpreting Exceptions</a></p></li>
<li><h3 class="no-margin">Previous</h3><a href="/generics">Generic Types</a></p></li>
</ul>
</div><!--/left-->
<!--right-->
<div class="col-md-9">
<h1>Interception and Decorators <a href="https://github.com/structuremap/structuremap/blob/master/documentation/interception-and-decorators.md" class="text-muted small pull-right fa fa-github" style="margin-top: 10px"> Edit on GitHub</a></h1>
<hr />
<div id="main-pane">
<!--Title: Interception and Decorators-->
<!--Url: interception-and-decorators-->
<p>All of the samples from this topic are part of the <a href="https://github.com/structuremap/structuremap/blob/master/src/StructureMap.Testing/Acceptance/interception_acceptance_tests.cs">user acceptance tests</a> in the main codebase. There is also another example of using an interception policy with
open generic types at the bottom of <a href="/generics">Generic Types</a></p>
<p>Improving the interception facilities and the means of applying <a href="https://en.wikipedia.org/wiki/Decorator_pattern">decorators</a> during object construction was one of the primary
goals of the big 3.0 release and is significantly different than the older 2.5/2.6 model.</p>
<p>Interception in StructureMap really comes
in two basic flavors:</p>
<ol>
<li><em>Activates</em> - Do some action on or with an object just created or resolved by StructureMap</li>
<li><em>Decorates</em> - Wrap (or optionally replace) an object just created with either a dynamic proxy or some sort of decorator</li>
</ol>
<p>Interceptors can be configured explicitly on a single <code>Instance</code> registration, on all registrations to a <code>PluginFamily</code>, or conventionally
to any concrete type implementing an interface, inheriting from a certain base class, or by some sort of user-supplied
criteria.</p>
<p>Any type of <code>Instance</code> can be intercepted, meaning that even object literal values supplied to StructureMap at registration can be
intercepted when they are resolved as dependencies or through service location calls.</p>
<p>See the <a href="/glossary">Glossary</a> for a refresher on terms like <code>Instance</code> and <code>PluginFamily</code>.</p>
<p>Also see <a href="/the-container/working-with-the-icontext-at-build-time">Working with the IContext at Build Time</a> for more information about using the <code>Container</code> state within interception.</p>
<h2 id="activation-interceptors">Activation Interceptors</h2>
<p>For right now, all activation interceptors are either using or subclassing the <a href="https://github.com/structuremap/structuremap/blob/master/src/StructureMap/Building/Interception/ActivatorInterceptor.cs">ActivatorInterceptor<T></a> class.</p>
<p>This class has two constructor functions that interest us:</p>
<p>To create an activator interceptor that acts on an object that can be cast to type <code>T</code>:</p>
<pre><code class="language-csharp">
public ActivatorInterceptor(Expression<Action<T>> action, string description = null)
</code></pre>
<p>To create an activator interceptor that acts on an object that can be cast to type <code>T</code> and
also uses the <a href="/the-container/working-with-the-icontext-at-build-time">IContext</a> service supplied by StructureMap itself.</p>
<pre><code class="language-csharp">
public ActivatorInterceptor(Expression<Action<IContext, T>> action, string description = null)
{
 _action = action;
 _description = description;
}
</code></pre>
<p>In both cases, the <code>description</code> is optional and is only used for diagnostic purposes in the <a href="/diagnostics/build-plans">build plan visualization</a>. If
omitted, StructureMap tries to do a <code>ToString()</code> on the Expression for the description and that frequently suffices to understand what's going on in the build plan.</p>
<p><strong>Please note that the Lambda supplied to <code>ActivatorInterceptor<T></code> must be a .Net Expression so cannot be a multi-line Lambda.</strong>
You can get around this limitation for more complex activation needs by simply making a wrapping method and using that
to express the activation.</p>
<h2 id="decorators">Decorators</h2>
<p>To demonstrate a decorator in action, say that we have an interface called <code>IWidget</code>, and when we build any instance
of <code>IWidget</code> we want those objects decorated by another type of <code>IWidget</code> that I clumsily named <code>WidgetHolder</code> in the
acceptance tests:</p>
<pre><code class="language-csharp">
public class WidgetHolder : IWidget
{
 private readonly IWidget _inner;

 public WidgetHolder(IWidget inner)
 {
 _inner = inner;
 }

 public IWidget Inner
 {
 get { return _inner; }
 }
}

</code></pre>
<p>Now, to see the decorator mechanism in action:</p>
<pre><code class="language-csharp">
[Fact]
public void decorator_example()
{
 var container = new Container(_ =>
 {
 // This usage adds the WidgetHolder as a decorator
 // on all IWidget registrations and makes AWidget
 // the default
 _.For<IWidget>().DecorateAllWith<WidgetHolder>();
 _.For<IWidget>().Use<AWidget>();
 });

 container.GetInstance<IWidget>()
 .ShouldBeOfType<WidgetHolder>()
 .Inner.ShouldBeOfType<AWidget>();
}

</code></pre>
<p>In effect, doing a decorator this way has the same effect (and build plan) as:</p>
<pre><code class="language-csharp">
var container = new Container(_ =>
{
 _.For<IWidget>().Use<WidgetHolder>()
 .Ctor<IWidget>().Is<AWidget>();
});
</code></pre>
<h2 id="custom-decorator-interceptors">Custom Decorator Interceptors</h2>
<p>The simplest usage is to just declare a type that will be the decorating type like we did above, but if you need some
other mechanism for decorators like runtime AOP interception or you want to build the decorating object yourself, StructureMap
provides the <code>FuncInterceptor<T></code> type where <code>T</code> is the type you want to decorate.</p>
<p>These objects can be created in two ways, by a user-supplied <code>Expression<Func<T, T>></code> and optional description:</p>
<pre><code class="language-csharp">
public FuncInterceptor(Expression<Func<T, T>> expression, string description = null)
</code></pre>
<p>and by a user-supplied <code>Expression<Func<IContext, T, T>></code> and optional description.</p>
<pre><code class="language-csharp">
public FuncInterceptor(Expression<Func<IContext, T, T>> expression, string description = null)
</code></pre>
<p>In both cases, the <code>description</code> field is only used for diagnostic purposes.</p>
<h2 id="interception-policies">Interception Policies</h2>
<p>The <a href="/registration/registry-dsl">Registry DSL</a> includes shorthand methods for the most common ways of configuring decorators
and activators by an individual <code>Instance</code> or by matching on implementing types. For more customized interception policies
that don't fit these mechanisms, StructureMap allows you to directly define an interception policy with a class
implementing this interface below:</p>
<pre><code class="language-csharp">
public interface IInterceptorPolicy : IDescribed
{
 IEnumerable<IInterceptor> DetermineInterceptors(Type pluginType, Instance instance);
}
</code></pre>
<p>For a simple example, let's say that we want to decorate any <code>IWidget</code> object with the
<code>WidgetHolder</code> class from earlier. We could build a small custom interceptor policy
like this one:</p>
<pre><code class="language-csharp">
public class CustomInterception : IInterceptorPolicy
{
 public string Description
 {
 get { return "good interception policy"; }
 }

 public IEnumerable<IInterceptor> DetermineInterceptors(Type pluginType, Instance instance)
 {
 if (pluginType == typeof(IWidget))
 {
 // DecoratorInterceptor is the simple case of wrapping one type with another
 // concrete type that takes the first as a dependency
 yield return new DecoratorInterceptor(typeof(IWidget), typeof(WidgetHolder));
 }
 }
}

</code></pre>
<p>To use this custom interception policy, use the <code>Policies.Interceptor()</code> methods like this example:</p>
<pre><code class="language-csharp">
[Fact]
public void use_a_custom_interception_policy()
{
 var container = new Container(x =>
 {
 x.Policies.Interceptors(new CustomInterception());

 x.For<IWidget>().Use<AWidget>();
 });

 container.GetInstance<IWidget>()
 .ShouldBeOfType<WidgetHolder>()
 .Inner.ShouldBeOfType<AWidget>();
}

</code></pre>
<p>As a helper for creating your own interception policies, you can also use the <code>InterceptorPolicy<T></code> base class
to conventionally apply some sort of <code>IInterceptor</code> to any number of <code>Instance's</code>:</p>
<pre><code class="language-csharp">
public InterceptorPolicy(IInterceptor interceptor, Func<Instance, bool> filter = null)
</code></pre>
<p>Here's an example of <code>InterceptorPolicy<T></code> in usage from the acceptance tests:</p>
<pre><code class="language-csharp">
[Fact]
public void apply_policy_selectively_with_a_func()
{
 var activator1 = new ActivatorInterceptor<ITarget>(x => x.Activate());
 var policy = new InterceptorPolicy<ITarget>(activator1, i => i.Name.StartsWith("A"));

 var container = new Container(x =>
 {
 x.Policies.Interceptors(policy);
 x.For<ITarget>().AddInstances(targets =>
 {
 targets.Type<ATarget>().Named("A");
 targets.Type<ATarget>().Named("A1");
 targets.Type<ATarget>().Named("A2");
 targets.Type<ATarget>().Named("B");
 targets.Type<ATarget>().Named("C");
 targets.Type<ATarget>().Named("D");
 });
 });

 container.GetInstance<ITarget>("A").ShouldBeOfType<ATarget>().WasActivated.ShouldBeTrue();
 container.GetInstance<ITarget>("A1").ShouldBeOfType<ATarget>().WasActivated.ShouldBeTrue();
 container.GetInstance<ITarget>("A2").ShouldBeOfType<ATarget>().WasActivated.ShouldBeTrue();
 container.GetInstance<ITarget>("B").ShouldBeOfType<ATarget>().WasActivated.ShouldBeFalse();
 container.GetInstance<ITarget>("C").ShouldBeOfType<ATarget>().WasActivated.ShouldBeFalse();
 container.GetInstance<ITarget>("D").ShouldBeOfType<ATarget>().WasActivated.ShouldBeFalse();
}

public class ATarget : ITarget
{
 public void Activate()
 {
 WasActivated = true;
 }

 public bool WasActivated { get; set; }

 public void Debug()
 {
 throw new NotImplementedException();
 }
}

</code></pre>
<p>Some quick things to note:</p>
<ol>
<li>For decorator interceptors, <code>InterceptorPolicy<T></code> will only apply if the <code>pluginType</code> matches <code>T</code></li>
<li>For activation interceptors, <code>InterceptorPolicy<T></code> will apply to any concrete type returned by an <code>Instance</code> that can be
cast to <code>T</code></li>
</ol>
<h2 id="apply-activation-interception-by-type">Apply Activation Interception by Type</h2>
<p>Let's say that in your system you have a marker interface or in this case an abstract class that exposes a single
<code>Activate()</code> method to start up stateful, long-running services created within your container:</p>
<pre><code class="language-csharp">
public abstract class Activateable
{
 public bool Activated { get; set; }

 public void Activate()
 {
 Activated = true;
 }
}

</code></pre>
<p>An implementation of <code>Activateable</code> from StructureMap's unit tests is shown below:</p>
<pre><code class="language-csharp">
public class AWidget : Activateable, IWidget
{
}

</code></pre>
<p>If you decide that you'd like StructureMap to call the <code>Activate()</code> method on any object it creates as
part of its object creation and resolution process, we can register an interception policy in a <code>Registry</code>
like this:</p>
<pre><code class="language-csharp">
[Fact]
public void activate_by_action()
{
 var container = new Container(x =>
 {
 x.For<IWidget>().Use<AWidget>();
 x.For<Activateable>()
 .OnCreationForAll("Mark the object as activated", o => o.Activated = true);
 });

 container.GetInstance<IWidget>()
 .ShouldBeOfType<AWidget>()
 .Activated.ShouldBeTrue();
}

</code></pre>
<p>There are several overloads of <code>OnCreationForAll()</code> covering cases with and without <code>IContext</code></p>
<h2 id="apply-decoration-across-a-plugin-type">Apply Decoration across a Plugin Type</h2>
<p>As shown above, you can use the <code>Registry.For<T>().DecorateAllWith<TDecorator>()</code> to apply decorators to all <code>Instance's</code>
registered to a <code>Plugin Type</code>:</p>
<pre><code class="language-csharp">
[Fact]
public void decorate_with_type()
{
 var container = new Container(x =>
 {
 x.For<IWidget>().DecorateAllWith<WidgetHolder>();
 x.For<IWidget>().Use<AWidget>();
 });

 container.GetInstance<IWidget>()
 .ShouldBeOfType<WidgetHolder>()
 .Inner
 .ShouldBeOfType<AWidget>();
}

</code></pre>
<p>There are also several other overloads of <code>DecorateAllWith()</code> for user supplied expressions, filters, and descriptions. See the
<a href="https://github.com/structuremap/structuremap/blob/master/src/StructureMap.Testing/Acceptance/interception_acceptance_tests.cs">acceptance tests for interception</a> in the StructureMap codebase for many more sample usages.</p>
<h2 id="add-interception-to-a-single-instance">Add Interception to a Single Instance</h2>
<p>You can also define interceptors directly to individual <code>Instance's</code> inside of a StructureMap <code>Registry</code> using the
<code>OnCreation()</code> and <code>DecorateWith</code> methods or the more generic <code>Instance.AddInterceptor()</code> method. Here is some sample
usage from StructureMap's unit tests on interception:</p>
<pre><code class="language-csharp">
_container = new Container(r =>
{
 r.For<ContextRecorder>().Use(recorder);

 r.For<IService>().AddInstances(x =>
 {
 x.Type<ColorService>()
 // Registers an activation action on this Instance
 .OnCreation("last service", s => _lastService = s)
 .Named("Intercepted")
 .Ctor<string>("color").Is("Red");

 x.Type<ColorService>()
 // Activation using IContext
 .OnCreation("last touched", (c, s) => c.GetInstance<ContextRecorder>().WasTouched = true)
 .Named("InterceptedWithContext")
 .Ctor<string>("color").Is("Red");

 x.Type<ColorService>()
 .Named("NotIntercepted")
 .Ctor<string>("color").Is("Blue");

 x.Object(new ColorService("Yellow"))
 .Named("Yellow")
 .OnCreation("set the last service", s => _lastService = s);

 x.ConstructedBy(() => new ColorService("Purple")).Named("Purple")
 // Applies a decorator to this instance. Not sure *why*
 // you'd want to do it this way
 .DecorateWith(s => new DecoratorService(s));

 x.ConstructedBy(() => new ColorService("Purple")).Named("DecoratedWithContext")
 // Fancier decorator
 .DecorateWith("decorated with context", (c, s) =>
 {
 c.GetInstance<ContextRecorder>().WasTouched = true;
 return new DecoratorService(s);
 });

 x.Type<ColorService>().Named("Decorated").DecorateWith(
 s => new DecoratorService(s))
 .Ctor<string>("color").Is("Orange");

 x.Object(new ColorService("Yellow")).Named("Bad")
 .OnCreation("throw exception", obj => { throw new Exception("Bad!"); });
 });
</code></pre>
</div>
<hr />
<nav>
<span>
<strong>Previous: </strong><a href="/generics">Generic Types</a>
</span>
<span class="pull-right">
<strong>Next: </strong><a href="/interpreting-exceptions">Interpreting Exceptions</a>
</span>
</nav>
</div><!--/right-->
</div><!--/row-->
</div><!--/container-->
</body>
<foot>
<script type='text/javascript' src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type='text/javascript' src="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script type="text/javascript" src="/content/embed.js"></script>
<script type="text/javascript" src="/content/prism.js"></script>
<script type="text/javascript" src="/content/sidebar.js"></script>
<script type="text/javascript" src="/content/affix.js"></script>
<script>
$('#search').keyup(function(e){
if(e.keyCode == 13) {
var search = $('#search').val();
var url = 'https://www.google.com/#q=site:structuremap.github.io ' + search;
url = encodeURI(url);
//alert(url);
window.location.href = url;
e.stopPropagation();
if (e.cancelBubble!=null) e.cancelBubble = true;
return false;
}
});
</script>
</foot>
</html>