Naiad, a toy service container.

In the previous piece Service locator vs depdendency injection I had declared, “Service location is, and is provided via an interface on the context that can be implemented inside 10 minutes as a dictionary of lambdas if you had a pressing need.” Which risks being a throw-away comment comprising largely of hot air. So I thought I’d knock one up, the guts of which is…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private readonly ConcurrentDictionary<string, object> _ctors = new ConcurrentDictionary<string, object>();
public void RegisterService<T>(string name, Func<IServiceContainer, T> ctor) {
_lock.EnterWriteLock();
try {
_ctors[name] = ctor;
} finally {
_lock.ExitWriteLock();
}
}
public T GetService<T>(string name) {
_lock.EnterReadLock();
try {
Func<IServiceContainer, T> ctor = _ctors[name] as Func<IServiceContainer, T>;
return ctor(this);
} finally {
_lock.ExitReadLock();
}
}

It really is just a dictionary of lambdas, and wires up thus…

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
Naiad.ServiceContainer.Instance.RegisterService("request-behaviours",
container => {
return new List<IProcessBehaviour> {
new SimpleSequenceBehaviour("process-request", container.GetService<List<string>>("life-cycle")),
new BootstrapBehaviour("bootstrap",
new Dictionary<string,string> {
{"area", "default"},
{"concern", "default"},
{"action", "default"},
{"app-path", "/web.harness"}
}
),
new ParseRequestBehaviour("parse-request", "Inversion.Web.Harness.Site"),
new ViewStateBehaviour("view-state"),
new ProcessViewsBehaviour("process-views"),
new RenderBehaviour("render"),
new RazorViewBehaviour("rzr::view"),
new XmlViewBehaviour("xml::view", "text/xml"),
new JsonViewBehaviour("json::view", "text/json"),
new XsltViewBehaviour("xslt::view", "text/xml"),
new XsltViewBehaviour("xsl::view", "text/html"),
new HelloWorldBehaviour("work"){
MatchingAllParameters = new Dictionary<string,string> {
{"action", "hello"}
}
}
};
}
);

It isn’t much of any use except as a base-line “simplest possible thing that works” to measure more industrial strength implementations against. Just prodding it with apache bench for a ballpark it’s a whisker faster than Spring.NET, which considering the facilities Spring offers leaves me quite impressed with Spring.

There’s a lot of value in returning to the simplest implementation that works as it’s easy to lose track of the cost of components that become an integral part of our applications.

So there’s no misunderstanding, this is a toy. But sometimes all you need is a toy. Inversion started life as a toy, the initial prototype being a predicate/action dictionary, where the predicate acted on an event and the action acted upon a context. In scripting languages knocking prototypes up with the general purpose data structures lying around such as lists and dictionaries is very normal, and we could maybe do with it becoming more of a norm in .NET before we jump off into the deep-end with grand object-models.

As I’m proofing this I can see I need to move the exit from the read lock after looking up the constructor but before executing the constructor… as I say, it’s a toy.