Samstag, 16. Dezember 2017

PantherDI - Part 5: Instance registration, Late processing, IgnoreAttribute

License and readme

First up a rather smaller change: The project now features a license file and a readme.
I chose the creative commons Attribution 4.0 international license, because it empowers others to build upon my work and use it in commercial projects too.

Instance registration

One of the features still missing is the ability to hand an instance of an object to the container and have it return the instance whenever a request matches its registration. The basis for the implementation is rather simple for it is just a factory that declares no dependencies and returns the given instance.

In the ContainerBuilder however there will be two new methods: WithInstance and RegisterInstance.
While WithInstance simply registers the instance and returns the ContainerBuilder, providing chainability, RegisterInstance returns an object which allows to modify the registration.

Late processing

The major philosophy of PantherDI is to put the time consuming stuff up front which stems from the philosophy found in medical projects. This ensures that it is always well known when the work will be done and that user interaction can't create a package of workload while the application runs and maybe even while time critical processes are executed.
Usually during normal application development this is not a requirement and thus this default behavior which leads to a slower application startup actually counteracts what the developer wants to achieve.
Thus I want to implement a way to revert that behavior and process registrations only when a type is requested that the registration handles. This removes processing time from the application startup (or container creation) and spreads it out over resolution requests. If a registration is never requested, it won't be processed at all.

The implementation of late processing is rather simple. First of all the container no longer holds the knowledge base. Instead it "only" holds a cache, which contains the result of resolutions for a specific request. If the cache already contains a result for the request, it will be used.
During normal operation the KB will be handed to the container as first resolver. As it was before, only if it does not contain a solution for the given request, the other resolvers are actually triggered. But instead of adding their results to the KB, they will also just be added to the cache. This will only be a slight change when using the default configuration, but when late processing should be used, the first resolver won't be a pre-filled KB, but a resolver that first processes any registrations that fulfil the first requested contract and then query the KB. This is the same behavior as it was in the ContainerBuilder when requesting to process a specific contract.

As you can see, separating the ContainerBuilder from the logic that does the actual processing was already a step towards implementing this feature: The change will be that the RegistrationConverter will be put into a resolver.

IgnoreAttribute

The IgnoreAttribute is meant to tell PantherDI to not process items via reflection it would otherwise process. Its detailed semantic changes depending where it is placed:

  • Used on a type, the Container won't contain this type even though it implements an interface or inherits from an abstract class marked as contract.
  • Used on a constructor, it will not be used as a factory for that type
  • Used on a parameter of a constructor, the parameter won't be resolved, even if it could, rendering the type only resolvable by Func<TIn, TOut> when using this constructor.
    This enables to create constructors for view-models that take the corresponding model, even when in non-strict mode and thus the model could be resolved.
While implementing ignoring a constructor parameter, I noticed a bug in the GenericResolver base class: The filter condition for when the GenericResolver was used always chose to use the resolver when the requested type was not a generic at all. system tests were added and the condition now does not use the GenericResolver when either the requested type is not a generic or the generic type definition does not match the implemented GenericResolver.

Code

The code after implementing this blogpost can be found on GitHub under the Tag v1.1.0: Link

Glossary

This chapter contains a cumulative list of terms that are used within this project.

Container
The central element of PantherDI. 
It offers a way to retrieve instances of objects with all their transitive dependencies resolved.

Registration
Information about a single type, enabling a container to create it, resolve its dependencies and use it as dependency.

Contract
Any object that serves as the promise that a registration can be used as dependency. A registration can be retrieved only by contract, so the consumer does not need to know its actual type.

Factory
Basically the object representation of a function which will create an instance of a registered type.

Dependency (of a Factory or Provider, see below)
A single dependency of a factory is the object representation of one a function parameter. It includes meta-information, for example the contracts the instance passed needs to fulfil.


Provider
Created from a factory by resolving all the dependencies that can be resolved using the given registrations.

Knowledge Base (KB)
The knowledge base contains all providers that can be used by the container.

Resolver
An algorithm that utilizes the knowledge base to return all providers for a given dependency.
It may create new providers from the ones given in the knowledge base, depending on its purpose.


Processing
Often means converting a Factory into a Provider by resolving all its dependencies that can be resolved.


Keine Kommentare:

Kommentar veröffentlichen