A few people have asked me about contributing and extending the Siege.ServiceLocation library. My preference is to keep the Siege.ServiceLocation namespace itself as the main engine for enabling a wide variety of syntaxes and not grow the default syntax beyond what comes pre-packaged (the given-when-then syntax). However, I do want the project to grow and expand to meet the needs of anyone that uses it. Therefore, I’ve added a new project to the Siege.ServiceLocation umbrella: Siege.ServiceLocation.Extensions

Users can download this as an optional library that works in conjunction with Siege.ServiceLocation. Contributors can contribute to this library to create new syntax and new use cases to enable users to solve problems. The only restrictions I will put on this is that it should only reference Siege.ServiceLocation and not any particular IoC or external framework. If you want to integrate with a specific IoC, or another specific framework, I do encourage you to do so — only in a seperate project. I want to make sure that people consuming this framework don’t have to add dependencies to frameworks and systems they do not intend to use.

As these particular cases come up we will address them as necessary.

So what changed?

Not a whole lot. There were some assumptions in the system around conditions and how they were evaluated that I changed to defer through an implementation via interface. What that means is that anyone extending the framework can now determine on their own how a conditions should be determined as valid for a particular use case rather than explicitly comparing against context. Additionally, I extended the framework to track the types it has been resolving. Finally, I simplified some interfaces, particularly around supporting new IoC containers. I expect that I will make a few more modifications before releasing a 1.0 version — the execution context is a primary candidate, as right now it is fairly immature in it’s implementation. The other change I anticipate is to enable the IoC to work with other cross-cutting steps during resolution. Specifically, I’ve been working on incorporating AOP support into Siege.ServiceLocation and it’s almost complete, but I’m still pondering how to incorporate it. It may require a slight refactory, it may not. We’ll see.

Once I have made these two changes I expect I’ll do a feature freeze for Siege.ServiceLocation and release it as a 1.0 version.

New Features in Siege.ServiceLocation.Extensions

Included already in this project are two new types of usages: Factory Methods and the ability to resolve dependencies based on the resolution hierarchy. That is to say, you can conditionally specify types to be used when the container has already decided to use other types. As always, you can additionally refer to the unit tests for detailed examples.

Factory Methods:

You can now specify a factory method to be used when constructing an object, both by default or when a condition is met. This works nearly identically to your standard Given-When-Then functionality, except instead of “Then” you use “ConstructWith”, making it a Given-ConstructWith or Given-When-ConstructWith syntax.

Type Resolution based on parent types

I’m not really sure what to call this. The syntax is like this – Given<BaseType>.WhenInjectingInto<ParentType>.Then<ImplementingType>. Basically it works like this: if you have ‘BaseType1′ which has a constructor dependency ‘IDependency’ and is implemented by ‘TypeA’ and ‘TypeB’, you can say “When you decide to resolve as ‘TypeA’, use the ‘DependencyA’ implementation of ‘IDependency’, otherwise use the default (if one is specified).”

By combining this with normal contextual resolution, you can set up rules for your types to be resolved based on arbitrary context and for their dependencies to be resolved either based on arbitrary context as well, or resolved based on which type is currently being resolved, or a combination thereof. It is important to note that when you specify multiple satisfiable criteria for resolution — for example arbitrary context that is satisfied AND resolution based on a type being resolved that is ALSO true, the system will choose the first applicable use case (the first one registered). Additionally, factory method support is compatible with the aforementioned case of resolving dependencies based on the parent’s type. You can tell it to use a factory when the ‘WhenInjectionInto<T>’ scenario is satisfied.

In Closing

The goal with this system was to be able to quickly and easily extend it to support new syntaxes and usages. When I added these new syntaxes, each of them took me roughly 30 minutes from beginning to all tests passing at the end. While I am the author of this system, I expect that the barrier for other users would not be too much higher given a very basic understanding of the interfaces involved. For example, the amount of code to support Factory Methods was maybe all told about 100 lines of code between syntax and implementation while the amount of code to support dependency resolution based on the type its injected into was less than 150 lines.