IoC Container & Auto Configuration
TimeLine IoC, Auto Configuration and IoC attributes documentation
This page explains how the TimeLine IoC container works, how to mark classes with IoC attributes, and how to work with automatically exported IoC configuration.
It is meant for developers and consultants who use the Framework and need to register, extend, or resolve IoC-based components.
What is IoC?
The abbreviation is "Inversion of Control", and basically means that objects will be resolved as the correct type automatically, you do not have to think about it. Only know what you need and ask the container for that type.
1. Core Concepts
1.1 Container & Auto Configuration
The global container is
TimeLine.Framework.Util.IocContainer.Instance.Auto configuration is handled by
IocAutoConfiguration, accessible via:var autoConfig = IocContainer.Instance.AutoConfiguration;At build time, a JSON config is generated per assembly. At runtime,
IocAutoConfiguration.Load()reads these files and wires types to the container.AutoConfiguration usually does not have to be called manually.
You usually don't interact with the JSON directly. You:
Mark types with IoC attributes →
IocConfigExporterpicks them up →IocAutoConfigurationwires them intoTinyIoC.
2. IoC Attributes
All IoC attributes inherit from IocBaseAttribute. Apply them to classes or interfaces to control how they are registered and used.
2.1 Overview
IocObject
class / interface
Mark type as IoC-managed object (base of most scenarios).
IocExplictImplementationFor
class
Explicit implementation for given interface/abstract base.
IocTag
class / interface
Categorization/tagging for lookup and grouping.
IocRunStatic
class / interface
Run static constructor at startup (without registering type as IoC object).
IocIndependentObject
class
Derived class that should not replace its base in the IoC inheritance chain.
IocDerivedObjectsOnly
class / interface
Base/template type: only concrete children become IoC objects, base itself isn't used.
2.2 IocObjectAttribute
IocObjectAttributeMarks a type as an IoC object.
Key behavior
For non-abstract classes:
Auto-config will register the type (or its most derived replacement) in the container.
For abstract classes / interfaces:
Used as base / template; concrete derived types will be picked up.
The attribute will automatically be inherited, and does not have to be applied to derived types. (But can, for clarity)
Properties
bool RunStaticIftrue, the static constructor is forced at startup.string ReasonOptional explanation for docs/logging.
Example
2.3 IocExplictImplementationForAttribute
IocExplictImplementationForAttributeMarks a concrete class as the explicit implementation for an interface or abstract base.
Key behavior
The container will resolve the interface/abstract type to this class.
Also implies IoC registration (similar to
IocObject).This can be applied to interfaces/types you do not have code access to, e.g. third-party types.
Example
2.4 IocTagAttribute
IocTagAttributeTags a type for later lookup.
Key behavior
Tags show up in auto config and can be queried via
GetTagged.Inherited by derived classes if applied on bases.
Example
Look up all tagged types:
2.5 IocRunStaticAttribute
IocRunStaticAttributeTriggers static construction without registering an IoC object.
Key behavior
Use it for static-only classes or global setup that runs during IoC auto-configuration.
This setup will run even though the type is not accessed/resolved anywhere in code up to that point.
It will automatically load the DLL if it hasn't been loaded yet.
Does not automatically register the type for
Create<T>.
Example
If you also want the type as an IoC object, use [IocObject(RunStatic = true) instead.
2.6 IocIndependentObjectAttribute
IocIndependentObjectAttributeMarks a derived class as an independent IoC root.
Problem it solves
Normally, the auto config treats inheritance chains as "replacement chains". The most derived type replaces the base in IoC registration. That's great for customizations, but sometimes a derived type should be completely separate, not a replacement.
Behavior
Applied to a non-abstract class.
Auto config will:
Register the class as its own IoC object.
Not chain it as a replacement of its base.
No inheritance-based "replaced X with Y" warnings.
Derived children of this type will be registered as replacements to this type.
Example
Result:
ImportHandlerBase→DefaultImportHandlerreplacement chain.DiagnosticsImportHandleris its own IoC root, not part of that chain.
2.7 IocDerivedObjectsOnlyAttribute
IocDerivedObjectsOnlyAttributeMarks a base type where only the derived concrete types are IoC objects.
Problem it solves
Sometimes a base type is just a template, and every concrete subclass should be its own IoC object, not "the latest replacement for the base".
Behavior
Applied to a base class or interface.
Auto config will:
Not register the base itself in IoC.
Treat each direct concrete subclass as its own IoC root (no replacement chain starting from the base).
Customizations of those subclasses still use normal replacement logic.
Example
Result:
ExportJobBase→ not registered in IoC.CustomerExportJob→ own IoC root.InvoiceExportJob→ own IoC root.
A further customization:
Now CustomerExportJob has its own replacement chain:
CustomerExportJob→ProjectXCustomerExportJob, without affectingInvoiceExportJob.
3. Creating Instances via IocContainer
IocContainer3.1 Basic creation
Use IocContainer.Instance.Create<T> to create IoC-managed objects.
This will:
Ensure the type is registered via auto configuration (if it was exported as such).
Resolve it from the underlying
TinyIoCContainer.
There are also TryCreate variants:
3.2 Singleton pattern with IoC
Typical pattern when you want a singleton that's IoC-aware:
Mark the type with
[IocObject].Use a static
Instanceproperty that resolves viaIocContainer.Keep the constructor
protected(orinternal) to discourage manualnew.
3.2.1 Base class 'Singleton'
There is a shared base class that can be used for the singleton pattern in TimeLine.Client.Framework, called TimeLine.Client.Framework.Singleton<T> that can be used and implements the singleton pattern and [IocObject] tag automatically.
4. IoC Objects with Constructor Arguments
Sometimes you need constructor arguments that can't be auto-resolved.
The container exposes overloads that take named parameters:
All of these end up as NamedParameters under the hood and are forwarded to TinyIoC.
Usage of the first or third variant is recommended, depending on preference.
Arguments will be passed in right into the constructor, no init step is required. This allows even get-only properties to be set correctly in an IoC object.
5. Working with Auto Configuration
Sometimes you need to inspect or work directly with the IoC metadata.
5.1 Accessing the auto configuration
The key parts:
Dictionary<string, Entry> TypesAll known IoC entries, keyed by fully qualified type name.Dictionary<string, HashSet<Entry>> TagsAll tagged entries.Entry this[Type type]Indexer bySystem.Type.Entry this[string typeName]Indexer by fully qualified name.
5.2 Getting all entries for a tag
GetTagged:
Cleans up inheritance chains (returns the most derived applicable entries).
Can filter by
onlyRegisteredif needed.
5.3 Inspecting inheritance / derived types
Each Entry exposes:
Entry BaseThe base entry (config-level, not necessarily the CLRBaseType).Entry DerivedThe configured replacement / derived entry.Entry MostDerivedThe last replacement in the chain.bool? RegisterWhether this entry is configured to be registered.bool IsRegisteredWhether the container registration actually happened.Type LoadType()Loads the actual CLR type for the entry.object CreateInstance()Convenience method to create an instance of the most derived implementation.
Example: find the effective implementation for a base type:
Special attributes:
IocIndependentObjecttypes will not appear asDerivedof their base.IocDerivedObjectsOnlybases will haveRegister != true, and their direct children will not haveExtendspointing to that base.
6. Guidelines & Best Practices
Use
[IocObject]for:Services, helpers, converters, behaviors, import/export handlers, etc.
Any class that is intended to be customizable in customizations.
Anything that should be resolved through the framework container instead of
new.
Use
[IocExplictImplementationFor]when:There is exactly one default implementation for an interface/abstract base.
You do not have code access to the base type.
Use
[IocTag]to:Group related components (controllers, jobs, strategies) and load them dynamically.
Use
[IocRunStatic]orIocObject.RunStatic = truefor:Types that need static setup during startup.
Use
[IocIndependentObject]when:A derived class must not override/replace the base in IoC.
Use
[IocDerivedObjectsOnly]when:You have a base template and want each concrete child to be its own independent IoC object.
When in doubt:
Resolve types via
IocContainer.Instance.Create<T>()orTryCreate. The container will even resolve if the type was not registered as an IoC object.Avoid
newon IoC types at all costs, only create them via the IocContainer.
This is enough for using IoC right in day-to-day work. For anything more exotic, inspect IocAutoConfiguration and the generated JSON, but for most use cases the attributes and the container APIs are all that is needed.
Last updated