Usage
Sample interfaces/classes used can be found at the bottom of this page.
The Crosser Resolver contains 3 different ways to map interfaces to conrecte classes.
- Singleton<T>
- One<T>
- Many<T>
The resolver is static so you do not need to create any instance of the container.
All samples assumes that you have included the namespace for the static Crosser.Resolver container.
// include the namespace
using Crosser.Resolve;
Singleton<T>
Use the Singleton<T> to map and get singleton objects.
Register
// set `IChuckNorris` to be a singleton that always returns the same instance of the implementation `TheOnlyChuck`
Singleton<IChuckNorris>.As(()=> new TheOnlyChuck());
Note that the As method returns a boolean telling if the registration was a success or not.
If you configure the ThrowErrorOnDeniedMapping to true the As method will throw an exception if registration fails.
Resolve
// get the mapping for `IChuckNorris`
var theRealChuck = Singleton<IChuckNorris>.Get();
One<T>
Use One<T> to map and get transient objects
Register
// set IChuckNorris to map as transient (always return a new instance).
One<IChuckNorris>.As(()=> new TheOnlyChuck());
Note that the As method returns a boolean telling if the registration was a success or not.
If you configure the ThrowErrorOnDeniedMapping to true the As method will throw an exception if registration fails.
Resolve
// get a new IChuckNorris
var chuck1 = One<IChuckNorris>.Get();
// get another chuck.
var chuck2 = One<IChuckNorris>.Get();
Many<T>
Many<T> is grouped/enumerable transient mapping where you can map several instances to one interface.
Register
// map TheOnlyChuck and the FakeChuck to the IChuckNorris interface
Many<IChuckNorris>.Add(()=>new TheOnlyChuck());
Many<IChuckNorris>.Add(()=>new FakeChuck());
Note that the Add method returns a boolean telling if the registration was a success or not.
If you configure the ThrowErrorOnDeniedMapping to true the Add method will throw an exception if registration fails.
Resolve
//Iterate over the IChuckNorris collection
foreach(var chuck in Many<IChuckNorris>.GetAll())
{
Console.WriteLine(chuck.GetFact());
}
Named instance
When working with collections of Many<T> you migth wanna get a specific instance from the collection
Register
// map TheOnlyChuck to a named instance 'realchuck'
Many<IChuckNorris>.Add(() => new TheOnlyChuck(), namedInstance: "realchuck");
// add another instance, this time fake chuck without being named
Many<IChuckNorris>.Add(() => new FakeChuck());
Resolve
// get the named instance
var chuck = Many<IChuckNorris>.GetNamedInstance("realchuck");
Console.WriteLine(chuck.GetFact());
SingletonCollection<T>
SingletonCollection<T> is grouped/enumerable singleton mapping where you can map several singletons to one interface.
Register
// map TheOnlyChuck and the FakeChuck to the IChuckNorris interface
SingletonCollection<IChuckNorris>.Add(()=>new TheOnlyChuck());
SingletonCollection<IChuckNorris>.Add(()=>new FakeChuck());
Note that the Add method returns a boolean telling if the registration was a success or not.
If you configure the ThrowErrorOnDeniedMapping to true the Add method will throw an exception if registration fails.
Resolve
//Iterate over the IChuckNorris collection
foreach(var chuck in SingletonCollection<IChuckNorris>.GetAll())
{
Console.WriteLine(chuck.GetFact());
}
Named instance
When working with collections of SingletonCollection<T> you migth wanna get a specific instance from the collection
Register
// map TheOnlyChuck to a named instance 'realchuck'
SingletonCollection<IChuckNorris>.Add(() => new TheOnlyChuck(), namedInstance: "realchuck");
// add another instance, this time fake chuck without being named
SingletonCollection<IChuckNorris>.Add(() => new FakeChuck());
Resolve
// get the named instance
var chuck = SingletonCollection<IChuckNorris>.GetNamedInstance("realchuck");
Console.WriteLine(chuck.GetFact());
Properties
The Crosser Resolver let you add properties to your mappings by passing in a IDictionary<string,object>.
You may look at this as setting metadata to a mapping type.
One<T> & Singleton<T>
When mapping properties to One<T>, Singleton<T> the properties is mapped directly on the interface of T.
Register
/// Some properties to attach to a mapping
var props = new Dictionary<string, object>() { { "num", 123 }, { "str", "hello world" } };
// map TheOnlyChuck to IChuckNorris and pass in the properties
One<IChuckNorris>.As(() => new TheOnlyChuck(), properties: props);
Resolve
// get properties for IChuckNorris
var chuckProperties = One<IChuckNorris>.Properties;
Console.WriteLine("IChuckNorris has properties num:{0}, str:{1}", chuckProperties["num"], chuckProperties["str"]);
Many<T> & SingletonCollection<T>
When mapping properties to Many<T> & SingletonCollection<T> the properties is mapped to each mapping by type or named instance.
Creating some properties for TheOnlyChuck and FakeChuck
Register
// some properties for real chuck
var realChuckProps = new Dictionary<string, object>() { { "TheRealDeal", true } };
// some properties for fake chuck
var fakeChuckProps = new Dictionary<string, object>() { { "IAmFake", true } };
Map TheOnlyChuck and FakeChuck with properties
// map TheOnlyChuck to a named instance 'realchuck' with properties
Many<IChuckNorris>.Add(() => new TheOnlyChuck(), namedInstance: "realchuck", properties : realChuckProps);
// add another instance, this time fake chuck without being named but with properties
Many<IChuckNorris>.Add(() => new FakeChuck(), properties : fakeChuckProps);
Resolve
Get properties by type of mapping.
var realChuckProperties = Many<IChuckNorris>.Properties<TheOnlyChuck>();
Console.WriteLine("TheOnlyChuck has property TheRealDeal:{0}", realChuckProperties["TheRealDeal"]);
var fakeChuckProperties = Many<IChuckNorris>.Properties<FakeChuck>();
Console.WriteLine("FakeChuck has property IAmFake:{0}", chuckProperties["IAmFake"]);
Since TheOnlyChuck was mapped as a named instance realchuck we can get the properties by name as well
var realChuckProperties = Many<IChuckNorris>.Properties("realchuck");
Console.WriteLine("TheOnlyChuck has property TheRealDeal:{0}, realChuckProperties["TheRealDeal"]);
Rewritable
Having this feature might feel odd since you normally just remap the interface to a new concrete type. In Crosser the default is that mappings IS NOT rewritable.
The reason for this is that not all of our modules is supposed to be rewritable. Also if developers create new custom modules they might wanna mark the mapping as not being rewritable.
You might look at this as using the sealed keyword but on mappings between interfaces and concrete types instead of a class.
You can rewrite both Singleton<T> and Transient (One<T> and Many<T>) mappings.
Register
Register a rewritable instance of Singleton<IChuckNorris> to FakeChuck
Singleton<IChuckNorris>.As(() => new FakeChuck(), rewritable : true);
Whenever we ask for IChuckNorris we will get the FakeChuck, but since it is rewritable we can change this by just adding a new mapping for IChuckNorris.
Singleton<IChuckNorris>.As(() => new TheOnlyChuck());
Resolve
Now we will get TheOnlyChuck when asking for IChuckNorris
Since we did not mark the new mapping as rewritable this mapping is now sealed and cant be changed.
var theOnlyChuck = Singleton<IChuckNorris>.Get();
Enable/Disable
If you want to disable/enable mappings you can do so for all type of mappings. Singleton<T> and One<T> works in the same way, but Many<T> has a different approach.
If a mapping is disabled it will return null.
Singleton<T> One<T>
Since both Singleton<T> and One<T> returns a single instance the enable/disable is straight forward.
One<IChuckNorris>.Enable();
One<IChuckNorris>.Disable();
And Singleton<T> look the same
Singleton<IChuckNorris>.Enable();
Singleton<IChuckNorris>.Disable();
Many<T>
Many<T> looks a little bit different since you can get 0-n instances back. You can choose to enable/disable ALL at once or do it individually by using the type of the concrete implementation.
Disable/Enable All
Many<IChuckNorris>.EnableAll();
Many<IChuckNorris>.DisableAll();
Disable/Enable by Type
Many<IChuckNorris>EnableAllOf<TheOnlyChuck>();
Many<IChuckNorris>DisableAllOf<FakeChuck>();
Disable by NamedInstance
This is currently not supported, although easy to add we have not yet seen the need for it in Crosser Framework
Reset
If you for some reason need to reset the resolver on a specific type you do so by calling
Singleton<T>.Reset();
or
One<T>.Reset();
or
Many<T>.Reset();
where T is the interface to reset the mappings for.
Configuration
The Crosser Resolver has a static class for managing settings.
ThrowErrorOnDeniedMapping
This setting enables/disables exceptions to be thrown when registering One<T>.As(Expression<Func<T>> f) and Singleton<T>.As(Expression<Func<T>> f)
This property is default set to false
Resolve.ResolverConfig.ThrowErrorOnDeniedMapping = false;
Sample Code
interfaces and classes used in this page
interface IChuckNorris
{
string GetFact();
}
public class TheOnlyChuck : IChuckNorris
{
public string GetFact()
{
return "Chuck Norris doesn't call the wrong number. You answer the wrong phone.";
}
}
public class FakeChuck : IChuckNorris
{
public string GetFact()
{
return "When Chuck Norris looks in the mirror nothing appears. There can never be a second Chuck Norris.";
}
}