-
Notifications
You must be signed in to change notification settings - Fork 0
Tutorial
Table of Contents
StackInjector is a dependency injection framework.
from Wikipedia:
dependency injection is a technique in which an object receives other objects that it depends on. These other objects are called dependencies. In the typical "using" relationship the receiving object is called a client and the passed (that is, "injected") object is called a service. The code that passes the service to the client can be many kinds of things and is called the injector.
Instead of the client specifying which service it will use, the injector tells the client what service to use. The "injection" refers to the passing of a dependency (a service) into the object (a client) that would use it.
Practically this means you as a developer don't worry about actually wiring and instantiating classes, you just have to design and implement them. The Injector will do the dirty work for you.
Another big point is that in this pattern, classes are services for other "user" classes.
In this framework there are 2 main important attributes: [Service] and [Served]. Those can be found in the StackWrapper.Attributes namespace.
Marks a class to be used as a service, and indicates important properties about the injection process.
It is used as in the following example:
[Service]
class MyClass
{
// implementation here
}You can also specify the service version, injection methods, etc. Those will be covered further in the tutorial.
Indicates a property or field should be injected. Works with private properties too.
It is used as in the following example:
[Service]
class MyClass
{
[Served]
AnotherService anotherService;
[Served]
FooService fooService {get; set;}
[Served]
IServiceInterface gooService {get; set;}
// other parts of the implementation here
}You can also specify a requested version and the versioning method. Those will be covered further in the tutorial.
The opposite attribute is the [Ignored] attributes, which marks a field or property to be ignored from injection. This is to suit a different coding style, and will be covered later in the tutorial.
This library in particular manages the injection process by wrapping the instantiated class structure into a StackWrapper. There are two wrappers, a synchronous and an asynchronous one which will be covered later in the tutorial.
The function of those classes (publicly exposed only as interfaces) is to manage resources and track the instantiated classes.
They also expose type-safe methods to initialize and work with you dependency-injected class structure.
This also means you can have a contained environment to run your class structure into, independently from other structures; or not, if you decide to share the instances: you have complete control over how those wrappers work.
Wrappers also implement IDisposable, meaning you can use the using keyword to automatically dispose of all instances and pending processes inside the wrapper.
To inject your classes you must use the Injector static class, which exposes methods to perform the injection process and wrap the structure into an handy StackWrapper, which is returned.
Injection is performed starting from a base class, the entry point of the injection and of your logic.
The most simple StackWrapper, simply wraps your class structure and exposes the entry point.
The following example shows how to inject starting from an example Application class:
var wrapper = Injector.From<Application>();You can then initialize your logic mainly in two ways (you can choose the style that most suits you):
using Start, which supports an overload returning the results of the delegate
wrapper.Start( app => app.MyLogic() );or by directly calling the method on the entry point.
wrapper.Entry.MyLogic();Asynchronous StackWrappers do offer a different approach: instead of calling a method, you submit an item to be elaborated by the stack asynchronously and wait for a result (still asynchronously).
This means that a CancellationToken must be managed in custom logic.
The following examples show how to inject starting from an example Application class (you can choose the style that most suits you):
The example class with all possible methods:
[Service]
class Application
{
// injected services here
public async Task<string> MyLogicAsync ( int item, CancellationToken token )
{
if (token.IsCancellationRequested)
return "";
// some logic with await
}
public string MyLogic ( int item )
{
// some simple logic here
}
}specify the types in the method
Injector.AsyncFrom<Application,int,string>( (app,item,token) => app.MyLogicAsync(item,token) );let the types be derived from the delegate
Injector.AsyncFrom
(
(Application app, int item, CancellationToken token)
=> app.MyLogicAsync(item,token)
);Injector.AsyncFrom<Application,int,string>
(
async (app,i,t) => await Task.Run( ()=>app.MyLogic(i), t )
);Remember that you can use any type you want, even a tuple to pass multiple parameters at once.
To best customize the injection process, the StackWrapperSettings class contains a series of utilities and options to customize the whole injection process.
You cannot directly instantiate a new StackWrapperSettings, you must modify one of the given ones (see default options under settings) with a builder-like pattern, as in the following example:
var settings =
StackWrapperSettings.Default
.TrackInstantiationDiff()
.VersioningMethod(ServedVersionTargetingMethod.LatestMajor)
.WhenNoMoreTasks(AsyncWaitingMethod.Timeout, 10000);The injection process requires an assembly to read classes from, if you are developing your application using multiple projects, then you must register all of the used assemblies for them to work.
Normally you don't have to worry about this if you use only one project and don't unset the RegisterEntryAssembly() option.
To register a whole assembly, you just need to know a class inside of that assembly:
StackWrapperSettings.Default
.RegisterAssemblyOf<MyClassInAnotherAssembly>()
.RegisterAssemblyOf<MyOtherClassInAnotherAssembly>();or you can lazily use:
StackWrapperSettings.Default
.RegisterDomain();to register every non-system assembly.
Some settings offer the option to override the preferences in the attributes of your classes.
Normally, if no preference is set in an attribute (for example the versioning method) the one specified in the settings will be used. If this attribute is set then this attribute will override the settings; but if you explicitly set the @override parameter to true, the preference in the StackWrapperSettings specified for the injection will be used anyway.
This framework has some cool features that are gonna help you develop your application.
You can find detailed explanations and a list of every feature available in the features page