[Prism] Composite Application Guidance for WPF (5) -- Dependency injection container
Zhou yinhui
The basic services of dependency injection containers and Prism have been mentioned many times in this series of articles. Today, we will separate them and talk about them in detail.
1. Why use the dependency injection container?
We know that in Composite Application, modules are loosely coupled, that is, the dependencies between modules are reduced as much as possible during design. However, from a business perspective, they always need to communicate and cooperate with each other. therefore, the dependency injection container plays a bridge-like role here. for example, when a component instance is created, it depends on another component or service. In this case, the dependency injection container will inject the required information into it, the injection method avoids coupling between direct references. in addition, using dependency injection containers has the following benefits:
Components do not have to locate their dependencies and maintain their lifecycles.
Replacing the dependency of a component does not affect the component.Code
Components become easier to test because dependencies are relatively easy to mock
Because the new services required by the system are easily added to the container, the system maintenance difficulty is also reduced.
2. Prism dependency injection container
Open the composite application library (CAL)Source code, We did not seem to find a dependency injection container implementation, but found a dependency injection container appearance interface icontainerfacade, it uses the appearance mode to abstract the most basic function "resolution" (RESOLVE) of a dependency injection container ).
///
// defines the appearance of the injection container used by the composite application library.
///
Public interface icontainerfacade
{< br> ///
// parse instance of the specified type
///
/// type of the object obtained from the container.
// an instance of the type.
Object resolve (type);
/// <Summary>
/// Try to parse the specified type of instance from the container
/// </Summary>
/// <Param name = "type"> type of the object retrieved from the container. </param>
/// <Returns>
/// A <paramref name = "type"/> type instance.
/// If the type cannot be parsed, the <see langword = "null"/> value is returned.
/// </Returns>
Object tryresolve (type );
}
In fact, the patterns & Practices Team designed to make prism compatible with more dependency injection containers (such as spring. net, Castle Windsor, unity, and so on). Therefore, the icontainerfacade interface is used in the prism source code instead of a specific container.
However, by default, Prism uses a lightweight and scalable dependency injection container in another open-source project "Unity" as the default container unitycontainer. the container implements three injection methods: constructor injection, property injection, and method injection.
For details about dependency injection containers in unity, refer to the following articles.Article:
Go deep into one of unity 1.x dependency injection containers: getting started
Go deep into unity 1.x dependency injection container 2: Initialize unity
Go deep into unity 1.x dependency injection container 3: Get object
Go deep into unity 1.x dependency injection container 4: dependency Injection
Since Unity does not support tryresolve (type); method, Prism uses an adapter unitycontaineradapter to solve this problem.
3. Performance overhead of dependency injection containers
Because the underlying mechanism of registering a type to a dependency injection container and then using the container to parse (RESOLVE) A type instance object is "reflection", the overhead is relatively large, if you need to frequently create a large number of instances, whether to use the dependency injection container requires careful consideration. A large number of dependencies and deep dependency nesting can also cause performance problems. in addition, if a component is neither dependent on other components or services nor dependent on other components, it is unwise to put it into the dependency injection container.
4. Whether to register as a singleton
When registering a default type ing or registering an instance with the dependency injection container, we have a question: whether to register in singleton mode. if yes, the objects parsed in the container every time are the same object instance; otherwise, the new instance is created every time. generally, for some global services and shared statuses, We will register them as the singleton mode, for those dependencies that require a new instance to be injected each time, we will register them in non-singleton mode.
5. Use iunitycontainer or icontainerfacade
They are located in Microsoft. practices. composite and Microsoft. practices. in the unity namespace, although they can all be used as high-level interfaces of containers, using iunitycontainer basically means to directly use the unity container (the dependency injection container in the Unity project ), the use of icontainerfacade means that you can be compatible with various containers. however, we know that icontainerfacade basically only provides one function "resolution", but in terms of a basic dependency injection container, it is often not just this function, for example, at least type registration and instance registration are required. Therefore, iunitycontainer is recommended in most cases. We can see this from the open source project stocktraderri, unless it is one of the following situations:
- As an independent software vendor, you write some services that provide multi-container support.
- Your service is used in a multi-container system.
6. Code configuration or configuration file configuration
I will reference this question.Martin FowlerFor your reference:
"
Code configuration vs. Another issue with the configuration file is relatively independent, but it is often associated with other issues: how to configure service assembly, through the configuration file or directly code assembly? For most applications that need to be deployed in multiple placesProgramA separate configuration file is more suitable. Configuration files are almost all XML files, and XML is indeed suitable for this purpose. However, sometimes it is easier to implement the Assembly directly in the program code. For example, a simple application does not have many deployment changes. Therefore, it is much clearer to use a few pieces of code for configuration than to use XML files.
In contrast, sometimes application assembly is very complex and involves a large number of conditional steps. OnceProgramming LanguageAs the configuration logic in becomes more complex, you should describe the configuration information in a proper language to make the program logic clearer. Then, you can write a constructor (builder) class to complete the assembly. If there are more than one constructor scenario, you can provide multiple constructor classes and select between them through a simple configuration file.
I often find that people are too eager to define configuration files. Programming Languages usually provide simple and powerful configuration management mechanisms. Modern programming languages can also compile programs into small modules and insert them into large systems. If the compilation process is laborious, the script language can also help.
Generally, configuration files should not be written in programming languages because they must be edited by system administrators who do not understand programming. However, what is the probability of such a situation? Do we really want programmers who do not know how to change the transaction isolation level of a complex server-side application? Only when it is very simple can non-Programming Language configuration files have the best effect. If the configuration information becomes complex, you should consider selecting a proper programming language to compile the configuration file.
"