Unity文档阅读 第三章 依赖注入与Unity
Introduction
简介
In previous chapters, you saw some of the reasons to use dependency injection and learned how dependency injection differs from other approaches to decoupling your application. In this chapter you’ll see how you can use the Unity dependency injection container to easily add a dependency injection framework to your applications. On the way, you’ll see some examples that illustrate how you might use Unity in a real-world application.
在前面章节中,你已经看到了使用依赖注入的一些原因,并了解了依赖注入与其他方法如何与解耦应用程序有所不同。在本章中你将看到如何使用Unity依赖注入容器轻松的添加依赖注入框架到你的应用程序中。在这个过程中,你将可以看到一些实例阐明在显示应用程序中中和使用Unity。
The Dependency Injection Lifecycle: Register, Resolve, Dispose
依赖注册生命周期:注册、解析、处理
In the previous chapter, you saw how the ManagementController class has a constructor that expects to be injected with an object of type ITenantStore. The application must know at run time which implementation of the ITenantStore interface it should instantiate before it can go ahead and instantiate a ManagementController object. There are two things happening here: something in the application is making a decision about how to instantiate an object that implements the ITenantStore interface, and then something in the application is instantiating both that object and the ManagementController object. We will refer to the first task as registration and the second as resolution. At some point in the future, the application will finish using the ManagementController object and it will become available for garbage collection. At this point, it may also make sense for the garbage collector to dispose of the ITenantStore instance if other client classes do not share the same instance.
The Unity container can manage this register, resolve, dispose cycle making it easy to use dependency injection in your applications. The following sections illustrate this cycle using a simple example. Later in this chapter you will see a more sophisticated real-world sample and learn about some alternative approaches.
在前面章节中,你已经看到ManagementController 类有一个构造函数希望被注入ITenantStore类型的对象。应用程序必须在运行时知道应该实例化ITenantStore接口的实现,然后才能继续实例化一个ManagementController对象。这里发生了两件事:一个是应用程序决定如何实例化实现了ITenantStore 接口的对象,另一个是应用程序实例化该对象和ManagementController 对象。我们将第一项任务称为注册,第二项作为决议。在将来的某个时候,应用程序将使用ManagementController对象完成,它将可用于垃圾回收。这时候,如果其他客户端类不共享同一个实例,垃圾收集器也可以处理ITenantStore实例。
Unity容器可以管理注册,解析,处理周期,使得在应用程序中轻松使用依赖注入。以下部分使用简单示例说明此周期。后面的章节你可以看到更多显示中复杂示例和了解一些替代方法。
Typically, you perform the registration of the types that require dependency injection in a single method in your application; you should invoke this method early in your application’s lifecycle to ensure that the application is aware of all of the dependencies between its classes. Unity also supports configuring the container declaratively from a configuration file.
通常,在应用程序中的单个方法中执行需要依赖注入的类型的注册,您应该在应用程序生命周期的早期调用此方法,以确保应用程序知道其类之间的所有依赖关系。Unity还支持从配置文件声明配置容器。
You should always try to write container-agnostic code (except for the one place at the root of the application where you configure the container) in order to decouple your application from the specific dependency injection container you are using.
您应该总是尝试编写容器无关的代码(除了在配置容器的应用程序的根目录中的一个位置),以便从您正在使用的特定依赖注入容器中分离您的应用程序。
Register
注册
Using the Unity container, you can register a set of mappings that determine what concrete type you require when a constructor (or property or method) identifies the type to be injected by an interface type or base class type. As a reminder, here is a copy of the constructor in the ManagementController class showing that it requires an injection of an object that implements the ITenantStore interface.
使用Unity容器,你可以注册一组映射,以确定在构造函数(或属性或方法)标识要通过接口类型或基类类型注入的类型时所需的具体类型。在此提醒,这里显示的是ManagementController 类中的构造函数的一个副本,它需要注入一个实现了ITenantStore 接口的对象。
public ManagementController(ITenantStore tenantStore)
{
this.tenantStore = tenantStore;
}
The following code sample shows how you could create a new Unity container and then register the concrete type to use when a ManagementController instance requires an ITenantStore instance.
下面代码示例显示了如何创建一个新的Unity容器,然后注册在ManagementController实例需要一个ITenantStore实例时使用的具体类型。
var container = new UnityContainer();
container.RegisterType<ITenantStore, TenantStore>();
The RegisterType method shown here tells the container to instantiate a TenantStore object when it instantiates an object that requires an injection of an ITenantStore instance through a constructor, or method, or property. This example represents one of the simplest types of mapping that you can define using the Unity container. As you continue through this chapter, you will see other ways to register types and instances in the Unity container, that handle more complex scenarios and that provide greater flexibility.
这里显示的RegisterType方法告诉容器在实例化需要通过构造函数、方法或属性注入ITenantStore实例的对象时实例化TenantStore对象。此示例代表您可以定义使用Unity容器的最简单的映射类型之一。当你看到本章,您将看到在Unity容器中注册类型和实例的其他方法,它可以处理更复杂的场景并提供更大的灵活性。
Resolve
解析
The usage of the RegisterType method in the previous section defines the mapping between the interface type used in the client class and the concrete type that you want to use in the application. To instantiate the Management-Controller and TenantStore objects, you must invoke the Resolve method.
在前面的章节中使用RegisterType 方法定义客户端使用的接口类型和你引用程序中想要使用的具体类型的映射。去实例化ManagementController和TenantStore 对象,你必须调用Resolve 方法。
You’ll see later that with Unity you can also register a class type directly without a mapping from an interface type.
稍后你将看到用Unity你也可以直接注册类类型,而不用映射到接口类型。
var controller = container.Resolve<ManagementController>();
Note that in this example, you do not instantiate the ManagementController object directly, rather you ask the Unity container to do it for you so that the container can resolve any dependencies. In this simple example, the dependency to resolve is for an ITenantStore object. Behind the scenes, the Unity container first constructs a TenantStore object and then passes it to the constructor of the ManagementController class.
注意,在这个实例中,你不用直接实例化ManagementController 对象,而是要求Unity容器去为你做,一遍容器可以解决任何依赖。在后面章节,Unity容器第一次构建TenantStore 对象,并将它传入ManagementController 类的构造函数中。
Dispose
处理
In the simple example shown in the previous two sections on registering and resolving types, the application stores a reference to the Management-
Controller object in the controller variable and the Unity container creates a new TenantStore instance to inject whenever you call the Resolve method. When the controller variable goes out of scope and becomes eligible for garbage collection, the TenantStore object will also be eligible for garbage collection.
在前两节中关于注册和解析类型的简单示例中,应用程序将对ManagementController对象的引用存储在controller 变量中,并且Unity容器将在每次调用Resolve方法时创建一个新的TenantStore实例。当控controller 变量超出作用域并且有资格进行垃圾回收时,Tenant-Store对象也将有资格进行垃圾回收。
By default, the Unity container doesn’t hold a reference to the objects it creates: to change this default behavior you need to use one of the Unity lifetime managers.
默认情况下,Unity容器不持有它创建的对象的引用:更改这个默认行为,你需要使用一个Unity生命周期管理器。
Registering and Resolving in your Code
在你的代码中注册和解析
One of the original motivations, discussed in Chapter 1, for a loosely coupled design and dependency injection was maintainability. One of the ways that dependency injection can help you to create more maintainable solutions is by describing, in a single location, how to compose your application from all of its constituent classes and components. From the perspective of Unity, this is the type registration information. Therefore, it makes sense to group all of the type registrations together in a single method that you invoke very early on in your application’s lifecycle; usually, directly in the application’s entry point. For example, in a web application, you could invoke the method that performs all of the registrations from within the Application_Start method in the global.asax.cs or global.asax.vb file, in a desktop application you invoke it from the Main method.
Typically, you can call the Resolve method when you need an instance of a particular type in your application. The section “Lifetime Management” later in this chapter discusses the options for controlling the lifetime of objects resolved from the container: for example, do you want the container return a new instance each time you resolve a particular type, or should the container maintain a reference to the instance.
在第一章讨论了原始动机之是松散耦合设计和依赖注入是可维护性的。依赖注入帮助你创建更加有可维护性的解决方案的方法之一是在一个位置描述如何从所成员类和组件构造应用程序。从Unity角度,这是类型注册信息。因此,将所有类型注册集中在一个方法中是有意义的,以便你在应用程序生命周期的早期调用该方法;通常是在你应用程序的直接入口点。例如,在Web应用程序,你可以在global.asax.cs 或global.asax.vb 文件中的Application_Start 方法中调用改方法执行所有注册,在桌面应用程序你从Main 方法中调用它。
通常,当你需要在应用程序中实例化一个特殊类型,你可以调用Resolve 方法。本章后面的“生命周期管理”一节讨论了控制从容器中解析的对象的生命周期的选项:例如,您希望容器在每次解析特定类型时返回新实例,或者容器应保持对实例的引用。
You should perform all the registrations in a single location in your code or in a configuration file. This makes it easy to manage the dependencies in your application. In a highly modular application, each module might be responsible for its own registration and manage its own container.
Using a configuration file for registrations can be a brittle and error prone solution. It can also lead to the illusion that this configuration can be changed without proper testing. Consider which settings, if any, need to be configurable after your solution is deployed.
你应该执行所有所有注册在你代码或配置文件的一个固定位置。这使得在你应用程序中容易管理依赖。在高度模块化的应用程序中,每个模块可能负责自己的注册和管理自己的容器。
使用配置文件注册可能是一个脆弱和容易出错的解决方案。它也会导致错误,该配置可以在没有正确测试的情况下更改。考虑在部署解决方案后,哪些设置(如果有)需要配置。
Adding Unity to Your Application
添加Unity到你的应用程序中
As a developer, before you can write any code that uses Unity, you must configure your Visual Studio project with all of the necessary assemblies, references, and other resources that you’ll need. For information about how you can use NuGet to prepare your Visual Studio project to work with Unity, see the topic “Adding Unity to Your Application .”
作为开发人员,在你使用Unity些任何代码之前,必须使用您需要的所有必需的程序集,引用和其他资源配置Visual Studio项目。有关如何使用NuGet来准备Visual Studio项目以使用Unity的信息,请参阅主题“Adding Unity to Your Application”。
NuGet makes it very easy for you to configure your project with all of the prerequisites for using Unity.
NuGet使您很容易配置您的项目与使用Unity的所有先决条件。
A Real-World Example
显示中的一个例子
The following example is taken from a web role implemented using ASP.NET MVC. You may find it useful to open the sample application, “DIwithUnitySample,” that accompanies this guide in Visual Studio while you read this section. At first sight the contents of this RegisterTypes method (in the ContainerBootstrapper class in the Surveys project) might seem to be somewhat complex; the next section will discuss the various type registrations in detail, and the following section will describe how the application uses these registrations to resolve the types it needs at runtime. This example also illustrates how you should perform all of the type registration in a single method in your application.
下面示例来自使用ASP.NET MVC实现的Web角色。在阅读本部分时,您可能会发现在Visual Studio中打开本指南附带的示例应用程序“DIwithUnitySample”非常有用。咋一看RegisterTypes 方法(在Surveys 项目中的ContainerBootstrapper 类中)中似乎是有些复杂;下面章节将会详细的讨论各种类型的注册,以下部分将描述在运行时应用程序如何使用这些注册去解析它需要的这些类型。此示例还说明了如何在应用程序的单个方法中执行所有类型注册。
public static void RegisterTypes(IUnityContainer container)
{
var storageAccountType = typeof(StorageAccount);
var retryPolicyFactoryType = typeof(IRetryPolicyFactory);
// 实例化注册
StorageAccount account = ApplicationConfiguration.GetStorageAccount("DataConnectionString");
container.RegisterInstance(account);
// 注册工厂
container.RegisterInstance<IRetryPolicyFactory>(new ConfiguredRetryPolicyFactory())
.RegisterType<ISurveyAnswerContainerFactory,SurveyAnswerContainerFactory>(new ContainerControlledLifetimeManager());
// 注册表格类型
container.RegisterType<IDataTable<SurveyRow>
,DataTable<SurveyRow>>(new InjectionConstructor(storageAccountType,retryPolicyFactoryType, Constants.SurveysTableName))
...
// 注册消息队列类型, 使用开发性泛型类型
container.RegisterType(typeof(IMessageQueue<>)
,typeof(MessageQueue<>)
,new InjectionConstructor(storageAccountType,retryPolicyFactoryType, typeof(String))
);
...
// 注册仓储类型
container.RegisterType<ISurveyStore, SurveyStore>()
.RegisterType<ITenantStore, TenantStore>()
.RegisterType<ISurveyAnswerStore, SurveyAnswerStore>(
new InjectionFactory((c, t, s) => new SurveyAnswerStore(
container.Resolve<ITenantStore>()
,container.Resolve<ISurveyAnswerContainerFactory>()
,container.Resolve<IMessageQueue<SurveyAnswerStoredMessage>>(
new ParameterOverride("queueName", Constants.StandardAnswerQueueName)
)
,container.Resolve<IMessageQueue<SurveyAnswerStoredMessage>>(
new ParameterOverride("queueName", Constants.PremiumAnswerQueueName)
)
,container.Resolve<IBlobContainer<List<String>>>()
)
)
);
}
It’s useful to adopt a standard name for the class that contains your type registration code; for example ContainerBootstrapper.
为包含您的类型注册码的类采用标准名称很有用; 例如ContainerBootstrapper。
To see the complete ContainerBootstrapper class, you can open the DIwithUnitySample sample application that accompanies this guidance.
Figure 1 illustrates the object graph that the container will generate if a client resolves the ISurveyAnswerStore type from the container with the type registrations shown in the previous code sample.
要查看完整的ContainerBootstrapper类,您可以打开随本指南附带的DIwithUnitySample示例应用程序。
图1示出了如果客户端从具有先前代码示例中所示的类型注册的容器解析ISurveyAnswerStore类型,容器将生成的对象图。
图1 解析ISurveyAnswerStore类型
Figure 1 illustrates the object graph that the container creates when you resolve the ISurveyAnswerStore type from the registrations shown in the previous code listing. There are some important points to note from Figure 1.
• The container injects the SurveyAnswerStore with five objects that the container itself resolves: a TenantStore object, a SurveyAnswerContainer-Factory object, an EntitiesBlobContainer object, and two MessageQueue objects. Note that an explicit factory delegate is used to determine what must be injected to create the store.
• The container also resolves additional objects such as an EntitiesBlob-Container object and a FilesBlobContainer object to inject into the TenantStore instance.
• Many of the objects instantiated by the container share the same instances of the RetryPolicyFactory and CloudStorageAccount objects which are registered using the RegisterInstance method. Instance registration is discussed in more detail later in this chapter.
• The container injects the SurveyAnswerContainerFactory instance with an instance of the Unity container. Note that as a general rule, this is not a recommended practice.
The following sections discuss all of these points (and more) in detail. Figure 1 is intended to give an idea of what you can achieve with dependency injection in your applications.
图1说明了当从前面代码列表中显示的注册中解析ISurveyAnswerStore类型时容器创建的对象图。 图1中有一些要点要注意。
- 容器向SurveyAnswerStore注入容器本身解析的五个对象:TenantStore 对象、SurveyAnswerContainerFactory 对象、EntitiesBlobContainer 对象和两个MessageQueue 对象。请注意,显式工厂委托用于确定创建存储时必须注入的内容。
- 容器也解析附加的对象,例如要注入到TenantStore 实体的EntitiesBlobContainer 对象和FilesBlobContainer 对象。
- 许多由容器实例化的对象共享使用RegisterInstance方法注册的RetryPolicyFactory和CloudStorageAccount对象的相同实例。本章后面更加详细讨论实例注册。
- 容器使用Unity容器的实例注入SurveyAnswerContainerFactory实例。 请注意,作为一般规则,这不是推荐的做法。
下面章节详细讨论这些要点(以及更多)。图1旨在说明在应用程序中可以通过依赖注入实现什么。
Type Registrations in the Example
实例中类型注册
The previous code listing gives examples of many of the different types of registration that you can perform with the Unity container. This section examines each part of the registration individually.
在前面的代码清单给出了你可以使用用Unity容器可执行许多不同类型注册的示例。本节单独检查注册的每个部分。
Instance Registration
实例化注册
The simplest type of registration is instance registration where the container is responsible for maintaining a reference to a singleton instance of a type. For example:
当容器负责映射一个引用到一个单例类型时,最简单的注册类型是实例注册。例如:
StorageAccount account = ApplicationConfiguration.GetStorageAccount("DataConnectionString");
container.RegisterInstance(account);
Bear in mind, that if your registrations start to become too complicated or fragile, you are probably doing it wrong.
记住,如果你的注册开始变得过于复杂或脆弱,你可能做错了。
Here, instead of registering a mapping for a type to resolved later, the application creates a CloudStorageAccount object and registers the instance with the container. This means that the CloudStorageAccount object is created at registration time, and that only a single instance of the object exists in the container. This single instance is shared by many of the other objects that the container instantiates. Figure 1 shows that many of the objects that the container creates when a client resolves the ISurveyAnswerStore type share this CloudStorageAccount object instance.
在这里,应用程序将创建一个CloudStorageAccount对象,并向容器注册该实例,来代后面注册映射类型解析。这种方法CloudStorageAccount 对象创建在注册的时候,而且它是单例的对象存在于容器中。此单个实例由容器实例化的许多其他对象共享。图1显示当客户解析ISurveyAnswerStore 类型时,容器创建的许多对象共享CloudStorageAccount 对象的实例。
You can also use the ContainerControlledLifetimeManager class with the RegisterType method to create a singleton instance where the container maintains a reference to the object. The section “Lifetime Management” later in this chapter covers this in more detail.
你也可以用RegisterType 方法创建ContainerControlledLifetimeManager 的单例,在容器中维护对象的引用。本章后面的“生命周期管理”部分更详细地介绍了这一点。
Simple Type Registration
简单类型注册
The most common type registration you will see maps an interface type to a concrete type. For example:
最常见的类型注册将看到一个接口类型映射到一个具体类型。例如:
container.RegisterType<ISurveyStore, SurveyStore>();
Later, you can resolve the ISurveyStore type as shown in the following example, and the container will inject any of the required dependencies into the Survey-Store object that it creates.
稍后,您可以解析ISurveyStore类型,如下面的示例所示,容器将注入任何所需的依赖关系到它创建的SurveyStore对象中。
var surveyStore = container.Resolve<ISurveyStore>();
If the SurveyStore class has multiple constructors with the same number of parameters you can use either the InjectionConstructor attribute, the API, or the configuration file to disambiguate between the different SurveyStore constructors. However, although InjectionConstructor attributes are easy to use, they do couple your code to the container.
In most cases, components should have a single constructor and the constructor defines the dependencies of that component.
如果SurveyStore 类有多个参数数量相同的构造函数,则可以使用InjectionConstructor属性API或配置文件来消除不同SurveyStore构造函数之间的歧义。然而,虽然Injection-Constructor属性很容易使用,他们组成你的代码到容器。
在大多数情况下,组件应该有一个构造函数,构造函数定义该组件的依赖关系。
Constructor Injection
构造函数注入
The following code sample shows the constructor for the DataTable class that takes three parameters.
下面代码示例显示DataTable 类构造函数需要三个参数。
public DataTable(StorageAccount account,IRetryPolicyFactory retryPolicyFactory, string tableName)
{
...
}
The registrations for the DataTable types in the container includes an Injection-Constructor that defines how the container should resolve the parameter types. The container passes to the constructor references to the registered Storage-Account and RetryPolicyFactory instances, and a string that specifies the name of the table to use.
容器中DataTable 类型的注册包含一个InjectionConstructor,它定义容器应该如何解析参数类型。容器通过构造函数引用注册的StorageAccount 和RetryPolicyFactory 的实例,以及指定要使用的表的名称的字符串。
container
.RegisterType<IDataTable<SurveyRow>, DataTable<SurveyRow>>(
new InjectionConstructor(storageAccountType
,retryPolicyFactoryType
,Constants.SurveysTableName
)
)
.RegisterType<IDataTable<QuestionRow>, DataTable<QuestionRow>>(
new InjectionConstructor(storageAccountType
,retryPolicyFactoryType
, Constants.QuestionsTableName
)
);
The sample application uses a similar approach to register the blob container types it uses:
示例应用程序使用类似的方法来注册它使用的blob容器类型:
container
.RegisterType<IBlobContainer<List<string>>,
,EntitiesBlobContainer<List<string>>>(
new InjectionConstructor(storageAccountType
, retryPolicyFactoryType
, Constants.SurveyAnswersListsBlobName
)
)
.RegisterType<IBlobContainer<Tenant>,
EntitiesBlobContainer<Tenant>>(
new InjectionConstructor(storageAccountType
, retryPolicyFactoryType
, Constants.TenantsBlobName
)
)
.RegisterType<IBlobContainer<byte[]>,
FilesBlobContainer>(
new InjectionConstructor(storageAccountType
, retryPolicyFactoryType
, Constants.LogosBlobName, "image/jpeg"
)
)
.RegisterType<IBlobContainer<SurveyAnswer>,
EntitiesBlobContainer<SurveyAnswer>>(
new InjectionConstructor(storageAccountType
, retryPolicyFactoryType
, typeof(string)
)
);
Unity supports property and method injection in addition to the constructor injection shown in this example. If you use property injection then, as with any property, you should ensure that any properties have a useful default value. It is easy to forget to set a property.
Unity支持除了此示例中所示的构造函数注入之外的属性和方法注入。如果你使用属性注入,与任何属性一样,你应该确保那些属性有有效的默认值。很容易忘记设置属性。
Registering Open Generics
注册开放式泛型
The example code uses a slightly different approach to register the message queue types: it uses an overload of the RegisterTypes method that takes types as standard parameters instead of using type parameters.
这个代码示例使用稍微不同的的方式注册消息队列类型:它使用RegisterTypes 的重载方法,它将类型作为标准参数,而不是使用类型参数。
container.RegisterType(
typeof(IMessageQueue<>)
, typeof(MessageQueue<>)
, new InjectionConstructor(
storageAccountType
, retryPolicyFactoryType
, typeof(string)
)
);
Both the storage account and retry policy factory are singletons. The storage account is registered using the RegisterInstance method, the retry policy factory is registered using the ContainerControlledLifetimeManager class that you’ll learn more about later in this chapter.
存储帐户和重试策略工厂都是单例。存储账户使用RegisterInstance 方法注册,重试策略工厂使用ContainerControlledLifetimeManager 类注册,您将在本章后面部分了解更多。
This approach enables you to resolve the message queue type with any type parameter. The example uses the SurveyAnswerStoredMessage type:
这个方法使你能够用任何参数类型来解析信息队列类型。这示例使用SurveyAnswer-StoredMessage 类型:
container.Resolve<IMessageQueue<SurveyAnswerStoredMessage>>(...);
Parameter Overrides
参数覆盖
The ContainerBootstrapper class contains several examples where one of the InjectionConstructor constructor parameters is typeof(string). For example, in the message queue registration and in the blob container for survey answers registration:
ContainerBootstrapper类包含几个示例,其中一个InjectionConstructor构造函数参数是typeof(string)。例如,在消息队列注册和blob容器中进行调查回答注册:
container.RegisterType(
typeof(IMessageQueue<>)
,typeof(MessageQueue<>)
,new InjectionConstructor(storageAccountType,
retryPolicyFactoryType, typeof(string)
)
);
...
container.RegisterType<IBlobContainer<SurveyAnswer>,EntitiesBlobContainer<SurveyAnswer>>(
new InjectionConstructor(storageAccountType
,retryPolicyFactoryType, typeof(string)
)
);
The container does not include a registration that it can use to resolve this type. Therefore, when you resolve either the IMessageQueue<> or IBlobContainer<SurveyAnswer> types, you must provide a value for this parameter otherwise the resolution will fail. This provides a convenient method to pass parameter values that aren’t known at registration time to instances created by the container using the ParameterOverride type.
容器没有包含可以用于解析这个类型的注册。因此当你解析IMessageQueue<> 或IBlobContainer<SurveyAnswer>的类型时,您必须为此参数提供一个值,否则解析将失败。这提供了一种方便的方法来将在注册时未知的参数值传递给由容器使用Parameter-Override类型创建的实例。
Resolving Types in the Example
示例中解析类型
The example solution performs the type registration described in the previous section in three locations: in a standalone application that initializes the storage, in the web application’s start-up phase, and in a factory class.
示例解决方案在三个位置执行前面部分中描述的类型注册:在初始化存储的独立应用程序中,在Web应用程序的启动阶段和工厂类中。
Simple Resolve
简单解析
The usage in this simple standalone application is straightforward: it calls the RegisterTypes method to perform all the registration, resolves a number of objects, and then invokes the Initialize method on each of them to perform some initialization work before the application terminates. The following code sample shows this.
在这个简单独立的应用程序的用法是直白的:它调用RegisterTypes 方法去执行所有注册,解析一些对象,然后在应用程序结束之前,他们每次执行一些初始化工作时调用Initialize 方法。下面代码示例演示了这个。
static void Main(string[] args)
{
using (var container = new UnityContainer())
{
ContainerBootstrapper.RegisterTypes(container);
container.Resolve<ISurveyStore>().Initialize();
container.Resolve<ISurveyAnswerStore>().Initialize();
container.Resolve<ITenantStore>().Initialize();
Console.WriteLine("Done");
Console.ReadLine();
}
}
In this example, after the Initialization methods have run, the container is disposed.
在这个示例中,Initialization 方法执行后,容器已处理好。
Resolving in an MVC Application
在MVC应用程序中解析
The usage in the MVC application is more sophisticated: the application configures a container that the application will use at start-up, and then resolves the various types as and when it needs them. Remember that this is an ASP.NET MVC application; therefore, the container must be able to inject the MVC controller classes with the various store and queue objects that they need. The “Unity bootstrapper for ASP.NET MVC” NuGet package (search for Unity3 in the NuGet package manager) simplifies this by adding libraries and source code to the project in Visual Studio. The following code sample shows the RegisterTypes method in the UnityConfig class that the NuGet package added to the project; you can choose to load the Unity configuration from your configuration file or add the registrations directly.
在MVC应用程序中使用时更加复杂的:应用程序将在启动时使用应用程序配置容器,并且解析各种类型,在应用程序需要他们时。记住,这是ASP.NET MVC引用程序;因此,容器必须能够注入具有所需的各种存储和队列对象的MVC控制器类。“Unity Bootstrapper for ASP.NET MVC”NuGet包(在NuGet包管理器中搜索Unity3)通过在Visual Studio中向项目添加库和源代码来简化此过程。下面代码示例显示UnityConfig 类中的RegisterTypes 方法添加NuGet 包到项目中,你可以选择从配置文件加载Unity 配置文件或者直接注册。
public static void RegisterTypes(IUnityContainer container)
{
// NOTE: To load from web.config uncomment the line below...
// container.LoadConfiguration();
// TODO: Register your types here
// container.RegisterType<IProductRepository, ProductRepository>();
}
The “Unity bootstrapper for ASP.NET MVC” provides a UnityDependency-Resolver class that resolves controllers from the container. If you need to configure injection for the controller classes then you need to add the registration manually or add injection attributes to the controller classes
The following code sample shows part of the ManagementController class that custom factory class can resolve along with its dependency on the ITenantStore type from the registration information.
“Unity bootstrapper for ASP.NET MVC”提供UnityDependency-Resolver类从容器中解析控制器,如果你需要为控制器类配置注入,你需要手动添加注入或添加注入属性到控制器类
以下代码示例显示了自定义工厂类可以解析的ManagementController类的一部分,以及它对注册信息的ITenantStore类型的依赖性。
public class ManagementController : Controller
{
private readonly ITenantStore tenantStore;
public ManagementController(ITenantStore tenantStore)
{
this.tenantStore = tenantStore;
}
...
}
Using the Per Request Lifetime Manager in MVC and WebAPI Application
在MVC和WebAPI应用程序中使用每个请求生命周期管理器
The previous example showed how to use the “Unity bootstrapper for ASP.NET MVC” NuGet package to handle registering and resolving controllers in an MVC application. The package also includes a PerRequestLifetime manager that you can use in an MVC application. This lifetime manager enables you to create instances of registered types that behave like singletons within the scope of an HTTP request.
前面的示例演示了在MVC应用中如何使用“Unity bootstrapper for ASP.NET MVC” NuGet包去处理注册和解析控制器。这个包也包含你可以在MVC应用程序中使用的PerRequest-Lifetime 管理器。这个生命周期管理器是你可以创建注册类型的实体,其行为像Http请求范围内的单例。
If you are working with an ASP.NET Web API project, there is a “Unity bootstrapper for ASP.NET WebApi” NuGet package that offers equivalent features (search for Unity3 in the NuGet package manager). You can use both the “Unity bootstrapper for ASP.NET WebApi” and “Unity bootstrapper for ASP.NET MVC” packages in the same project and they will share a single container configuration class.
如果您正在使用一个ASP.NET Web API项目,“Unity bootstrapper for ASP.NET WebApi”Nuget 包提供相同的特征(在NuGet包管理器中查询Unity3)。你可以在相同的项目中使用“Unity bootstrapper for ASP.NET WebApi” 和“Unity bootstrapper for ASP.NET MVC” 包,他们将共享一个容器配置类。
There are third-party solutions available that offer similar support for ASP.NET WCF applications.
有第三方解决方案可以为ASP.NET WCF应用程序提供类似的支持。
Resolving with Run Time Information
解析和运行时信息
You don’t always know the values that you need to construct a dependency at design time. In the example shown below, a user provides the name of the blob container that the application must create at run time. In this example, type resolution occurs in a factory class that determines the value of a constructor parameter at registration time. The following code sample shows this factory class.
你不总是知道在设计时需要构造依赖的值。下面的示例显示,用户提供应用程序在运行时必须创建的blob容器的名称。在示例中,类型解析发生在工厂类中,该类在注册时确定构造函数参数的值。下面代码示例显示了这个工厂类。
public class SurveyAnswerContainerFactory : ISurveyAnswerContainerFactory
{
private readonly IUnityContainer unityContainer;
public SurveyAnswerContainerFactory(IUnityContainer unityContainer)
{
this.unityContainer = unityContainer;
}
public IBlobContainer<SurveyAnswer> Create(string tenant, string surveySlug)
{
var blobContainerName = string.Format(
CultureInfo.InvariantCulture,
"surveyanswers-{0}-{1}",
tenant.ToLowerInvariant(),
surveySlug.ToLowerInvariant());
return this.unityContainer.Resolve<IBlobContainer<SurveyAnswer>>(
new ParameterOverride("blobContainerName", blobContainerName));
}
}
In this example, the Resolve method uses a parameter override to provide a value for the blobContainerName parameter to the constructor of the EntitiesBlob-Container class that is registered in the container instance injected into the SurveyAnswerContainerFactory object. Figure 1 shows how the SurveyAnswer-ContainerFactory object is injected with a Unity container instance when it is resolved.
在例子中,Resolve方法使用参数覆盖来向注入到SurveyAnswerContainerFactory对象中的容器实例中注册的EntitiesBlobContainer类的构造函数提供blobContainerName参数的值。图1显示了SurveyAnswerContainerFactory对象在解析时是如何注入一个Unity容器实例的。
You saw previously how the application registered the IBlobContainer<Survey-Answer> type using a string parameter in the injection constructor. Without the parameter override, this registration would fail because the container cannot resolve the string type.
你之前看到了应用程序注册IBlobContainer<Survey-Answer> 类型在注入构造函数中使用字符串参数。如果没有用参数重写,这个注册将失败,因为容器不能解析字符串类型。
You can also see the parameter overrides in use in the ContainerBootstrapper class as part of the registration of the survey answer store. In this example, the parameter overrides provide the name of the message queues to create. The parameter overrides are supplied at resolve time when the registered Injection-Factory executes.
您还可以查看ContainerBootstrapper类中正在使用的参数覆盖作为调查答案存储库注册的一部分。在此示例中,参数覆盖提供要创建的消息队列的名称。参数覆盖在注册的Injection-Factory执行解析时提供。
Because the application supplies the name of the blob container to create at run time, the factory class uses a parameter override to supply this value to the Resolve method.
因为应用程序提供在运行时要创建的blob 容器名称,工厂类使用参数覆盖提供这个值到Resolve 方法。
container
.RegisterType<ISurveyAnswerStore, SurveyAnswerStore>(
new InjectionFactory((c, t, s) => new SurveyAnswerStore(
container.Resolve<ITenantStore>(),
container.Resolve<ISurveyAnswerContainerFactory>(),
container.Resolve<IMessageQueue<SurveyAnswerStoredMessage>>(
new ParameterOverride(
"queueName", Constants.StandardAnswerQueueName)),
container.Resolve<IMessageQueue<SurveyAnswerStoredMessage>>(
new ParameterOverride(
"queueName", Constants.PremiumAnswerQueueName)),
container.Resolve<IBlobContainer<List<string>>>()
)
)
);
Registration
注册
In this section, you’ll learn more about how you can register types with the Unity container and the advantages and disadvantages of the different approaches. All of the examples you’ve seen so far have registered types with the Unity container programmatically by using methods such as RegisterType and RegisterInstance.
在本章中,你讲学到更多关于如何用Unity容器注册类型和不同的处理的优势和劣势。到目前为止,您所看到的所有示例都通过使用RegisterType和RegisterInstance等方法以编程方式注册了Unity容器。
Programmatically configuring a Unity container at runtime is convenient, and if you keep all of your registration code together, it makes it easy to change those registrations when you modify the application. However, it does mean that you must recompile the application if you need to change your registrations. In some scenarios, you may want a mechanism that enables you to change the registrations in a configuration file and cause your application to use a different set of concrete classes at run time.
在运行时以编程方式配置Unity容器是便捷的,如果你保持你所有的注册代码在一起,它使您可以在修改应用程序时轻松更改这些注册。然而,它意味着如果你需要更改你的注册,你必须重新编译应用程序。在一些情况中,你可能想要一个机制,它使你能在配置文件中更改注册,并使你的应用程序在运行时使用不同的实例类集合。
Named Type Registrations
命名类型注册
Previously, you saw an example of how you can use parameter overrides to provide the name of the message queue when you are resolving the IMessageQueue type. An alternative approach for this scenario is to use named type registrations. In this case the message queue registration looks like the following where the two alternative registrations are named “Standard” and “Premium”:
在此前,你看到示例,当你解析IMessageQueue 类型时,如何使用参数覆盖去提供信息队列名称。本章另一种方式是使用命名类型注册。在这种情况中信息队列注册如下所示,其中两个备用注册名为“Standard”和“Premium”:
container
.RegisterType<IMessageQueue<SurveyAnswerStoredMessage>
,MessageQueue<SurveyAnswerStoredMessage>>(
"Standard",
new InjectionConstructor(
storageAccountType, retryPolicyFactoryType
,Constants.StandardAnswerQueueName
)
)
.RegisterType<IMessageQueue<SurveyAnswerStoredMessage>
,MessageQueue<SurveyAnswerStoredMessage>>(
"Premium",
new InjectionConstructor(
storageAccountType, retryPolicyFactoryType,
Constants.PremiumAnswerQueueName
)
);
With this registration, you can now resolve the message queue parameters to the SurveyAnswerStore constructor as follows using the named registrations:
用这个注册,你现在可以解析消息队列参数到SurveyAnswerStore 构造函数如下面使用命名解析:
container
.RegisterType<ISurveyAnswerStore, SurveyAnswerStore>(
new InjectionFactory(
(c, t, s) => new SurveyAnswerStore(
container.Resolve<ITenantStore>(),
container.Resolve<ISurveyAnswerContainerFactory>(),
container.Resolve<IMessageQueue<SurveyAnswerStoredMessage>>("Standard"),
container.Resolve<IMessageQueue<SurveyAnswerStoredMessage>>("Premium"),
container.Resolve<IBlobContainer<List<string>>>()
)
)
);
Design-Time Configuration
设计时配置
Unity enables you to load a collection of registrations from a configuration file into a container. For example, you could add the following sections to your app.config or web.config file to register mapping from the ITenantStore interface to the TenantStore class.
Unity使你能够从一个配置文件加载一个注册集合到容器中。例如,你可以添加下面内容到你的app.config或web.config文件去注册从ITenantStore 接口到TenantStore 类的映射。
You cannot use design-time configuration for Unity containers in Windows Store apps. For more information, see Appendix A – Unity and Windows Store apps.
你不能在Windows 商店应用中使用Unity容器的设计时配置。更多信息,请看附录A – Unity 和Windows 商店应用。
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
</configSections>
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<namespace name="Tailspin.Web.Survey.Shared.Stores" />
<container>
<register type="ITenantStore" mapTo="TenantStore" />
</container>
</unity>
</configuration>
For more information about the structure of this configuration file, see the topic Design Time Configuration.
更多关于构造配置文件信息,请看设计时配置话题。
To load the registration details from the configuration file, you can use the following code. The LoadConfiguration extension method is defined in the Microsoft.Practices.Unity.Configuration namespace.
你可以使用下面代码从配置文件加载注册明细。LoadConfiguration 扩展方法在Microsoft.Practices.Unity.Configuration包中。
IUnityContainer container = new UnityContainer();
container.LoadConfiguration();
If your Unity configuration includes any sensitive information, you should encrypt it. For more information, see Encrypting Configuration Information Using Protected Configuration on MSDN
如果你的Unity配置包含任何敏感信息,你应该加密它。更多信息,请见MSDN中的使用保护配置加密配置信息。
Defining the registrations in a configuration file means that it’s possible to make changes without recompiling the application. This can be useful for customized deployments and troubleshooting. You can also adopt a hybrid approach.
在配置文件中定义注册意味着可以在不重新编译应用程序的情况下进行更改。这对自定义部署和排错是有益的。你也可以采用混合模式。
Registration by Convention
约定注册
This feature (also known as auto-registration) is intended to minimize the amount of type registration code that you need to write. You may find it useful to open the sample application, “OtherUnitySamples,” that accompanies this guide in Visual Studio while you read this section. Rather than specify each type mapping individually, you can direct the Unity container to scan a collection of assemblies and then automatically register multiple mappings based on a set of rules. If you only have a handful of simple registrations, it doesn’t make sense to use this feature, but if you have many types to register it will save you a considerable amount of effort.
这个特点(又称为自动注册)是最小化你需要写的类型注册代码数量。您可能会发现打开示例应用程序很有用,“OtherUnitySamples”,在您阅读本部分时,在Visual Studio中伴随本指南。而不是单独指定每个类型映射,您可以指示Unity容器扫描组合集合,然后基于一组规则自动注册多个映射。如果您只有几个简单的注册,使用此功能没有意义,但如果你有很多类型来注册它会为你节省大量的努力。
This feature is only supported when you are configuring Unity programmatically. You can’t specify registration by convention in the configuration file.
仅当以编程方式配置Unity时,才支持此功能。您不能在配置文件中按约定指定注册。
In many cases, you don’t need to register a type to resolve it because Unity’s auto-wiring will figure it out for you. You do need to explicitly configure mappings though, and for this the registration by convention feature may prove useful.
在许多情况中,你不需要注册一个类型来解析它,因为Unity的自动布线会为你找到。你需要明确的配置映射,因此, 这个约定注册功能可能证明是有用的。
The registration by convention feature can scan a collection of assemblies, and then create a set of mappings in a Unity container for some or all of the types it discovers in the assemblies. Additionally, you can specify the lifetime managers to use (these are described in more detail later in this chapter) and the details of any injection parameters. You can easily modify the set of rules included out-of-the-box to implement more sophisticated scenarios.
约定注册功能可以扫描程序集合,并且在Unity容器中为其在程序集中发现的一些或所有类型创建一组映射。此外,你可以指定生命周期管理器去使用(本章后面描写更多详情)和一些注册参数明细。您可以轻松修改开箱即用的规则集,以实现更复杂的场景。
The following samples illustrate how you can use the registration by convention feature to create mappings in the Unity container using the RegisterTypes method. These examples are based on the code in the OtherUnitySamples
Visual Studio solution included with this guidance. The RegisterTypes method has the following parameters:
下面实例阐明,如何使用约定注册功能在Unity容器中使用RegisterTypes 方法创建映射。这些示例基于Visual Studio解决方案指南中包含的Other Unity Samples 中的代码。Register-Types 方法有下列参数:
Parameter |
Description |
Types |
This parameter is an enumerable collection of types that you want to register with the container. These are the types that you want to register directly or create mappings to. You can create this collection by providing a list of types directly or by using one of the methods of the built-in AllClasses helper class: for example, the method FromLoadedAssemblies loads all of the available types from the currently loaded assemblies. |
getFromTypes | This optional parameter identifies the types you want to map from in the container. The built-in WithMappings helper class provides several options for this mapping strategy: for example, the MatchingInterface property creates mappings where there are interfaces and implementations that follow the naming convention ITenant and Tenant. |
getName | This optional parameter enables you to control whether to create default registrations or named registrations for the types. The built-in helper class WithName, enables you to choose between using default registrations or named registrations that use the type name. |
getLifeTimeManager | This optional parameter enables you to select from the built-in lifetime managers. |
getInjectionMembers | This optional parameter enables you to provide definitions for any injection members for the types that you are registering. |
overwriteExistingMappings | This optional parameter enables you to control how the method behaves if it detects an attempt to overwrite an existing mapping in the Unity container. By default, the RegisterTypes method throws an exception if it detects such an attempt. If this parameter is true, the method silently overwrites an existing mapping with a new one based on the values of the other parameters. |
参数 |
描述 |
Types |
此参数是要注册到容器的可枚举类型集合。这些是要直接注册或创建映射的类型。您可以通过直接提供类型列表或使用内置的AllClasses辅助类的方法之一来创建此集合:例如,方法FromLoadedAssemblies加载当前加载的程序集中的所有可用类型。 |
getFromTypes | 这个可选参数确定你想映射到容器的类型。内置的With-Mappings 帮助类提供几个映射策略选项:例如:MatchingInterface 属性创建映射, 其中存在遵循命名约定的ITenant和Tenant的接口和实现。 |
getName | 此可选参数使您能够控制是否为类型创建默认注册或命名注册。内置的帮助类WithName,使你能够在默认注册或者使用类型名称的命名注册之间进行选择。 |
getLifeTimeManager | 这个可选参数使你能够选择内置的生命周期管理器。 |
getInjectionMembers | 此可选参数使您能够为要注册的类型的任何注入成员提供定义。 |
overwriteExistingMappings | 此可选参数使您能够控制方法在检测到尝试覆盖Unity容器中现有映射时的行为。 默认情况下,如果RegisterTypes方法检测到这样的尝试,它将抛出异常。如果参数为true,则此方法将根据其他参数的值,以一个新的映射静默覆盖现有映射。 |
The first example shows how you can create registrations for all the types that implement an interface where the ITenant/Tenant naming convention is in use.
第一个示例显示如何为实现ITenant / Tenant命名约定的接口的所有类型创建注册。
var container = new UnityContainer();
container.RegisterTypes(
AllClasses.FromLoadedAssemblies(),
WithMappings.MatchingInterface,
WithName.Default
);
}
This creates a set of transient registrations that map interfaces to types in the container.
这将创建一组临时注册,将接口映射到容器中的类型。
Registration by convention is intended to simplify registering types with the Unity container when you have a large number of types that must be registered with similar settings.
通过约定注册旨在简化注册类型与Unity容器,当您有大量类型必须使用类似设置注册。
The following example creates named registrations and uses the Container-ControlledLifetimeManager type to ensure that resolving from the container results in singletons.
下面示例创建命名注册,并且使用 ContainerControlledLifetimeManager类型,以确保从容器中解析出单例。
var container = new UnityContainer();
container.RegisterTypes(
AllClasses.FromLoadedAssemblies(),
WithMappings.MatchingInterface,
WithName.TypeName,
WithLifetime.ContainerControlled
);
Because this example is using the lifetime manager, it registers all loaded types in addition to mapping any interfaces to their matching types.
Lifetime managers are discussed later in this chapter in the section “Lifetime Management.”
因为此示例使用生命周期管理器,除了将任何接口映射到其匹配类型之外,还注册所有加载的类型。
生命周期管理器本找后面的“Lifetime Management.”章节探讨。
In some scenarios, you may need to combine registration by convention with explicit registration. You can use registration by convention to perform the basic registration of multiple types, and then explicitly add information for specific types. The following example adds an InjectionConstructor to the type registration for the TenantStore class that was one of the types registered by calling the RegisterTypes method.
在一些情况中,你可能需要结合约定注册与显示注册。你可以使用约定注册去执行多种类型的基础注册,然后显式添加特定类型的信息。以下示例向通过调用RegisterTypes方法注册的类型之一的TenantStore类的类型注册添加了一个InjectionConstructor。
var container = new UnityContainer();
container.RegisterTypes(
AllClasses.FromLoadedAssemblies(),
WithMappings.MatchingInterface,
WithName.Default,
WithLifetime.ContainerControlled);
// 提供一些额外的注册信息
container.RegisterType<TenantStore>(new InjectionConstructor("Adatum"));
The examples you’ve seen so far use the FromLoadedAssemblies method to provide a list of assemblies to scan for types; you may want to filter this list so that you only register a subset of the types from these assemblies. The following sample shows how to create a filter based on the namespace containing the type. In this example, only types in the OtherUnitySamples namespace are registered in the container.
您到目前为止使用的示例使用FromLoadedAssemblies方法提供要扫描类型的程序集列表;你可能想过滤这个列表,以便你仅仅注册这些程序集中的一个子集的类型。下面示例演示如何基于包含类型的命名空间创建过滤器。在这个示例中,只有OtherUnitySamples命名空间中的类型才会在容器中注册。
var container = new UnityContainer();
container.RegisterTypes(
AllClasses.FromLoadedAssemblies().Where(
t => t.Namespace == "OtherUnitySamples"),
WithMappings.MatchingInterface,
WithName.Default,
WithLifetime.ContainerControlled);
The next example illustrates the use of the getInjectionMembers parameter: this enables you specify types that should be injected when the registered type is resolved from the container. Note that any types to be injected will be injected into all the types registered in the container by the call to the RegisterTypes method. The following example assumes that all of the types registered have a constructor with a string parameter: any attempt to resolve a type without such a constructor parameter will result in an exception being thrown from the container.
下个示例举例说明使用getInjectionMembers 参数:这使您能够指定在从容器解析注册类型时应该注入的类型。注意,要注入的任何类型都将通过调用RegisterTypes方法注入容器中注册的所有类型。以下示例假定所有注册的类型都具有带有字符串参数的构造函数:任何尝试解析没有这样的构造函数参数的类型将导致从容器抛出异常。
var container = new UnityContainer();
container.RegisterTypes(
AllClasses.FromLoadedAssemblies(),
WithMappings.MatchingInterface,
getInjectionMembers: t => new InjectionMember[]
{
new InjectionConstructor("Adatum")
}
);
A more practical use of the getInjectionMembers method is to use it to configure interception for all of the registered types (for more information about interception in Unity, see Chapter 5, “Interception with Unity”). In the following example, the registration by convention injects all of the registered types with the custom LoggingInterceptionBehavior type using virtual method interception.
更多实际使用getInjectionMembers 方法是使用它配置拦截所有注册类型(更多关于Unity中拦截的信息,请见章5“Interception with Unity”)。在以下示例中,约定注册会使用虚拟方法拦截将所有注册的类型注入到自定义LoggingInterceptionBehavior类型中。
var container = new UnityContainer();
container.AddNewExtension<Interception>();
container.RegisterTypes(
AllClasses.FromLoadedAssemblies().Where(
t => t.Namespace == "OtherUnitySamples"),
WithMappings.MatchingInterface,
getInjectionMembers: t => new InjectionMember[]
{
new Interceptor<VirtualMethodInterceptor>(),
new InterceptionBehavior<LoggingInterceptionBehavior>()
}
);
Note how this example uses a filter to ensure that only types in the Other-UnitySamples namespace are registered in the container. Without this filter, the container will try to inject the interceptor into all the types from the loaded assemblies: this includes the LoggingInterceptionBehavior type itself and this results in a stack overflow.
注意,这个示例如何使用过滤器去确保只有OtherUnitySamples 命名空间的类型注册到容器中。没有这个过滤器,容器将尝试将拦截器注入来自加载程序集的所有类型:这包括LoggingInterceptionBehavior类型本身,这导致堆栈溢出。
You can keep the list of classes in the OtherUnitySamples namespace to use in other calls to the RegisterTypes method to avoid the need to re-scan the assemblies.
您可以保留OtherUnitySamples 命名空间中的类列表,以在对RegisterTypes 方法的其他调用中使用,以避免重新扫描程序集。
The block provides helper classes such as the AllClasses class that you can use as parameters to the RegisterTypes method. You can create your own helper classes, or use a lambda such the one used for the getInjectionMembers parameter in the previous example, to customize the behavior of registration by convention to your own requirements.
该块提供了帮助类,例如AllClasses类,您可以将其用作RegisterTypes方法的参数。您可以创建自己的助手类,或使用前面示例中用于getInjectionMembers参数的lambda,以按照您自己的要求自定义注册的行为。
The following artificial example shows how the overwriteExistingMappings parameter prevents the RegisterTypes method from throwing an exception if you attempt to overwrite an existing mapping. The result is that the container contains a mapping of the ITenantStore interface type to the TenantStore2 type.
以下人工示例显示如果尝试覆盖现有映射,overwriteExistingMappings参数如何防止RegisterTypes方法抛出异常。 结果是容器包含ITenantStore接口类型到TenantStore2类型的映射。
var container = new UnityContainer();
container.RegisterTypes(
new[] { typeof(TenantStore), typeof(TenantStore2) },
t => new[] { typeof(ITenantStore) },
overwriteExistingMappings: true);
The mapping for the last type overwrites the previous mapping in the container.
You can extend the abstract RegistrationConvention class to define a reusable convention that you can pass to the RegisterTypes method as a single parameter. The following sample shows a how you can extend the RegistrationConvention class.
最后一个类型的映射将覆盖容器中的上一个映射。
您可以扩展抽象RegistrationConvention类以定义可重用的约定,您可以将其作为单个参数传递到RegisterTypes方法。下面示例演示如何扩展RegistrationConvention 类。
class SampleConvention : RegistrationConvention
{
public override Func<Type, IEnumerable<Type>> GetFromTypes()
{
return t => t.GetTypeInfo().ImplementedInterfaces;
}
public override Func<Type, IEnumerable<InjectionMember>> GetInjectionMembers()
{
return null;
}
public override Func<Type, LifetimeManager> GetLifetimeManager()
{
return t => new ContainerControlledLifetimeManager();
}
public override Func<Type, string> GetName()
{
return t => t.Name;
}
public override IEnumerable<Type> GetTypes()
{
yield return typeof(TenantStore);
yield return typeof(SurveyStore);
}
}
Registration by Convention and Generic Types
约定注册和泛型
You can use registration by convention to register generic types. The following interface and class definitions use a generic type and you can use the WithMappings.FromMatchingInterface helper method to create a mapping between these types in the container。
你可以使用约定注册泛型。以下接口和类定义使用泛型,您可以使用WithMappings.FromMatchingInterface帮助方法在容器中的这些类型之间创建映射。
public interface ICustomerStore<T>
{
...
}
public class CustomerStore<T> : ICustomerStore<T>
{
...
}
This registers the open generic types but enables you to resolve using a closed generic. For example, you could resolve instances using the following code.
这会注册打开的泛型类型,但允许您使用封闭的泛型解析。 例如,您可以使用以下代码解析实例。
var blobCustomerStore = container.Resolve<ICustomerStore<BlobStorage>>();
var tableCustomerStore = container.Resolve<ICustomerStore<TableStorage>>();
It’s also possible to combine using registration by convention to register mappings for open generic types with a specific registration a closed generic type as shown in the following sample. The closed type registration will always take priority.
还可以结合使用约定注册来将开放泛型类型的映射注册为具有注册的封闭泛型类型,如以下示例所示。关闭类型注册将始终优先。
container.RegisterTypes(
// Registers open generics
AllClasses.FromLoadedAssemblies(),
WithMappings.FromMatchingInterface,
WithName.Default);
// Add a registration for a closed generic type
container.RegisterType<ICustomerStore<TableStorage>,CustomerStore<TableStorage>>();
Using Child Containers
使用子容器
Although you can use named registrations to define different mappings for the same type, an alternative approach is to use a child container. The following code sample illustrates how to use a child container to resolve the IMessageQueue<SurveyAnswerStoredMessage> type with a different connection string.
虽然你可以使用命名注册为同一类型定义不同映射,另一种方法是使用子容器。以下代码示例说明如何使用子容器来解析具有不同连接字符串的IMessageQueue<SurveyAnswerStoredMessage>类型。
var storageAccountType = typeof(StorageAccount);
var storageAccount = ApplicationConfiguration.GetStorageAccount("DataConnectionString");
container.RegisterInstance(storageAccount);
container.RegisterType<IMessageQueue<SurveyAnswerStoredMessage>,MessageQueue<SurveyAnswerStoredMessage>>(
"Standard",
new InjectionConstructor(storageAccountType,"StandardAnswerQueue"),
retryPolicyFactoryProperty);
var childContainer = container.CreateChildContainer();
var alternateAccount = ApplicationConfiguration.GetStorageAccount("AlternateDataConnectionString");
childContainer.RegisterInstance(alternateAccount);
childContainer.RegisterType<IMessageQueue<SurveyAnswerStoredMessage>,MessageQueue<SurveyAnswerStoredMessage>>(
"Standard",
new InjectionConstructor(storageAccountType,"StandardAnswerQueue"),
retryPolicyFactoryProperty);
You can now resolve the type IMessageQueue<SurveyAnswerStoredMessage> from either the original parent container or the child container. Depending on which container you use, the MessageQueue instance is injected with a different set of account details.
你现在可以从原始的付容器或子容器解析IMessageQueue<SurveyAnswerStoredMessage>类型。根据您使用的容器,MessageQueue实例将注入一组不同的帐户详细信息。
The advantage of this approach over using different named registrations, is that if you attempt to resolve a type from the child container and that type is not registered in the child container, then Unity will automatically fall back to try and resolve the type from the parent container.
这种方法的优势是使用不同的命名注册,如果你试图从子容器解析类型,并且类型没有在子容器中注册,那么Unity会自动回退,尝试从父类中解析类型 容器。
You can also use child containers to manage the lifetime of objects. This use of child containers is discussed later in this chapter.
你也可以使用姿容去管理对象的生命周期。这个子容器的使用将在本章后面讨论。
Viewing Registration Information
查看注册信息
You can access the registration data in the container programmatically if you want to view details of the registration information. The following code sample shows a basic approach to viewing the registrations in a container.
如果要查看注册信息的详细信息,可以通过编程方式访问容器中的注册数据。下面示例代码演示在容器中查看注册的基本方式。
Console.WriteLine("Container has {0} Registrations:",container.Registrations.Count());
foreach (ContainerRegistration item in container.Registrations)
{
Console.WriteLine(item.GetMappingAsString());
}
This example uses an extension method called GetMappingAsString to display formatted output. The output using the registrations shown at the start of this chapter looks like the following:
此示例使用称为GetMappingAsString的扩展方法显示格式化的输出。使用本章开头所示注册的输出如下所示:
Output
Container has 14 Registrations:
+ IUnityContainer '[default]' Container
+ StorageAccount '[default]' ContainerControlled
+ IRetryPolicyFactory '[default]' ContainerControlled
+ IDataTable`1<SurveyRow> -> DataTable`1<SurveyRow> '[default]' Transient
+ IDataTable`1<QuestionRow> -> DataTable`1<QuestionRow> '[default]' Transient
+ IMessageQueue`1<SurveyAnswerStoredMessage> ->
MessageQueue`1<SurveyAnswerStoredMessage> 'Standard' Transient
+ IMessageQueue`1<SurveyAnswerStoredMessage> ->
MessageQueue`1<SurveyAnswerStoredMessage> 'Premium' Transient
+ IBlobContainer`1<List`1<String>> -> EntitiesBlobContainer`1<List`1<String>>
'[default]' Transient
+ IBlobContainer`1<Tenant> -> EntitiesBlobContainer`1<Tenant> '[default]' Transient
+ IBlobContainer`1<Byte[]> -> FilesBlobContainer '[default]' Transient
+ ISurveyStore -> SurveyStore '[default]' Transient
+ ITenantStore -> TenantStore '[default]' Transient
+ ISurveyAnswerStore -> SurveyAnswerStore '[default]' Transient
+ ISurveyAnswerContainerFactory -> SurveyAnswerContainerFactory '[default]'
Transient
The following code sample shows the extension method that creates the formatted output.
下面示例代码演示扩展方法创建格式化输出。
static class ContainerRegistrationsExtension
{
public static string GetMappingAsString(this ContainerRegistration registration)
{
string regName, regType, mapTo, lifetime;
var r = registration.RegisteredType;
regType = r.Name + GetGenericArgumentsList(r);
var m = registration.MappedToType;
mapTo = m.Name + GetGenericArgumentsList(m);
regName = registration.Name ?? "[default]";
lifetime = registration.LifetimeManagerType.Name;
if (mapTo != regType)
{
mapTo = " -> " + mapTo;
}
else
{
mapTo = string.Empty;
}
lifetime = lifetime.Substring(0, lifetime.Length - "LifetimeManager".Length);
return string.Format("+ {0}{1} '{2}' {3}", regType, mapTo, regName, lifetime);
}
private static string GetGenericArgumentsList(Type type)
{
if (type.GetGenericArguments().Length == 0) return string.Empty;
string arglist = string.Empty;
bool first = true;
foreach (Type t in type.GetGenericArguments())
{
arglist += first ? t.Name : ", " + t.Name;
first = false;
if (t.GetGenericArguments().Length > 0)
{
arglist += GetGenericArgumentsList(t);
}
}
return "<" + arglist + ">";
}
}
Resolving
解析
You have already seen some sample code that shows some simple cases of resolving types from the Unity container. For example:
你已经一些示例代码,他们演示了从Unity容器解析类型的一些简单情况。例如:
var surveyStore = container.Resolve<ISurveyStore>();
container.Resolve<IMessageQueue<SurveyAnswerStoredMessage>>("Premium");
The second of these two examples shows how to resolve a named type registration from the container.
You can override the registration information in the container when you resolve a type. The following code sample uses a dependency override to specify the type of controller object to create:
这两个例子中的第二个演示了如何从容器中解析命名类型注册。
当你解析一个类型,你可以在容器中覆盖注册信息。以下代码示例使用依赖项覆盖来指定要创建的控制器对象的类型:
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
return this.container.Resolve(controllerType,new DependencyOverride<RequestContext>(requestContext)) as IController;
}
This example assumes that there is no registration in the container for the controller type that is passed to the GetControllerInstance method, so the dependency override defines a registration as the type is resolved. This enables the container to resolve any other dependencies in the controller class. You can use a dependency override to override an existing registration in addition to providing registration information that isn’t registered with the container.
此示例假设传递给GetControllerInstance方法的控制器类型在容器中没有注册,所以依赖覆盖定义了一个注册类型被解析。这使得容器可以解析控制器类中的任何其他依赖关系。除了提供未向容器注册的注册信息之外,还可以使用依赖项覆盖来覆盖现有注册。
The SurveyAnswerContainerFactory class uses a parameter override to specify the value of a constructor parameter that the application cannot know until run time.
SurveyAnswerContainerFactory类使用参数覆盖来指定应用程序在运行时之前无法知道的构造函数参数的值。
Resolving in an ASP.NET Web Application
在ASP.NET Web应用程序中解析
In the example shown earlier in this chapter, you saw how to integrate Unity into an MVC application, so that you can use Unity to resolve any dependencies in your MVC controller classes by creating a custom MVC controller factory.
在本章前面所示的示例中,您了解了如何将Unity集成到MVC应用程序中,所以你可以使用Unity通过创建一个自定义的MVC控制器工厂来解决你的MVC控制器类中的任何依赖。
Standard ASP.NET web applications face a similar problem: how do you resolve any dependencies in your web page classes when you have no control over how and when ASP.NET instantiates your page objects. The aExpense reference implemen-tation demonstrates how you can address this issue.
标准的ASP.NET Web应用程序有着类似的难题:当您无法控制如何和何时ASP.NET实例化您的页面对象时,如何解析网页类中的任何依赖项。aExpense参考实现演示了如何解决这个问题。
The following code sample shows part of a page class in the aExpense web application.
以下示例代码显示aExpense Web应用程序中页面类部分代码。
public partial class Default : Page
{
[Dependency]
public IExpenseRepository Repository { get; set; }
protected void Page_Init(object sender, EventArgs e)
{
this.ViewStateUserKey = this.User.Identity.Name;
}
protected void Page_Load(object sender, EventArgs e)
{
var expenses = Repository.GetExpensesByUser(this.User.Identity.Name);
this.MyExpensesGridView.DataSource = expenses;
this.DataBind();
}
...
}
This example shows a property, of type IExpenseRepository decorated with the Dependency attribute, and some standard page life-cycle methods, one of which uses the Repository property. The Dependency attribute marks the property for property setter injection by the Unity container.
The following code sample shows the registration of the IExpenseRepository type.
此示例显示使用Dependency属性装饰的IExpenseRepository类型的属性和一些标准页面生命周期方法,其中一个方法使用Repository属性。Dependency属性标记由Unity容器注入的属性集合的特性。
以下示例代码演示IExpenseRepository 类型注册。
public static void Configure(IUnityContainer container)
{
container
.RegisterInstance<IExpenseRepository>(new ExpenseRepository())
.RegisterType<IProfileStore, SimulatedLdapProfileStore>()
.RegisterType<IUserRepository, UserRepository>(new ContainerControlledLifetimeManager());
...
}
The following code sample, from the Global.asax.cs file, shows how the web application performs the type registration in the Application_Start method, and uses the BuildUp method in the Application_PreRequestHandlerExecute method to perform the type resolution.
以下示例代码来自Global.asax.cs文件,演示Web应用程序如何在Application_Start方法中执行类型注册,并且在Application_PreRequestHandlerExecute方法中使用使用BuildUp 方法执行类型解析。
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
IUnityContainer container = Application.GetContainer();
ContainerBootstrapper.Configure(container);
}
...
protected void Application_PreRequestHandlerExecute(object sender, EventArgs e)
{
var handler = HttpContext.Current.Handler as System.Web.UI.Page;
if (handler != null)
{
var container = Application.GetContainer();
if (container != null)
{
container.BuildUp(handler.GetType(), handler);
}
}
}
}
The BuildUp method passes an existing object, in this case the ASP.NET page object, through the container so that the container can inject any dependencies into the object.
BuildUp 方法通过容器传递现有对象(在这种情况下是ASP.NET页面对象),以便容器可以将任何依赖项注入到对象中。
Using the BuildUp method, you can only perform property and method injection. You cannot perform constructor injection because the object has already been created.
使用BuildUp 方法,你可以只执行特性和方法注入。你不能执行构造函数注入,因为对象已创建。
Resolving in a WCF Service
在WCF 服务中解析
If you want to use Unity to automatically resolve types in a WCF service, you need to modify the way that WCF instantiates the service so that Unity can inject any dependencies. The example in this section is based on a simple WCF calculator sample, and the following example code shows the interface and part of the service class. You may find it useful to open the sample application, “UnityWCFSample,” that accompanies this guide in Visual Studio while you read this section. The example uses constructor injection, but you could just as easily use method or property setter injection if required.
如果你想在WCF服务中使用Unity自动解析类型,你需要修改WCF实例化服务的方式,以便Unity可以注入任何依赖项。本章中示例是一个简单的WCF计算器例子,下面示例代码演示接口和服务类部分代码。你可能会发现在阅读本部分时,在Visual Studio中打开本指南附带的示例应用程序“UnityWCFSample”非常有用。这个示例使用构造函数注入,但是如果需要,你能够很容易使用方法或属性设置注入。
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
public interface ICalculator
{
[OperationContract]
double Add(double n1, double n2);
[OperationContract]
double Subtract(double n1, double n2);
[OperationContract]
double Multiply(double n1, double n2);
[OperationContract]
double Divide(double n1, double n2);
}
public class CalculatorService : ICalculator
{
private ICalculationEngine calculatorEngine;
public CalculatorService(ICalculationEngine calculatorEngine)
{
this.calculatorEngine = calculatorEngine;
}
public double Add(double n1, double n2)
{
double result = calculatorEngine.Add(n1, n2);
Console.WriteLine("Received Add({0},{1})", n1, n2);
Console.WriteLine("Return: {0}", result);
return result;
}
...
}
To modify WCF to use Unity to instantiate the service, you must provide a custom ServiceHost class that can pass a Unity container instance into the WCF infrastructure as shown in the following example.
要修改WCF以使用Unity实例化服务,你必须提供自定义的ServiceHost 类,它可以将Unity容器实例传递到WCF基础结构中,如下面的示例所示。
public class UnityServiceHost : ServiceHost
{
public UnityServiceHost(IUnityContainer container,Type serviceType, params Uri[] baseAddresses): base(serviceType, baseAddresses)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
foreach (var cd in this.ImplementedContracts.Values)
{
cd.Behaviors.Add(new UnityInstanceProvider(container));
}
}
}
The following code sample shows the UnityInstanceProvider class that resolves the service type (the CalculatorService type in this example) from the Unity container.
下面示例代码演示UnityInstanceProvider 类从Unity容器解析服务类型(CalculatorService 类型在这个示例中)。
public class UnityInstanceProvider: IInstanceProvider, IContractBehavior
{
private readonly IUnityContainer container;
public UnityInstanceProvider(IUnityContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
this.container = container;
}
#region IInstanceProvider Members
public object GetInstance(InstanceContext instanceContext, Message message)
{
return this.GetInstance(instanceContext);
}
public object GetInstance(InstanceContext instanceContext)
{
return this.container.Resolve(instanceContext.Host.Description.ServiceType);
}
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
}
#endregion
#region IContractBehavior Members
public void AddBindingParameters(ContractDescription contractDescription,ServiceEndpoint endpoint,BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ContractDescription contractDescription,
ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ContractDescription contractDescription,ServiceEndpoint endpoint,DispatchRuntime dispatchRuntime)
{
dispatchRuntime.InstanceProvider = this;
}
public void Validate(ContractDescription contractDescription,ServiceEndpoint endpoint)
{
}
#endregion
}
Now that you have defined the new UnityServiceHost class that uses Unity to instantiate your WCF service, you must create an instance of the UnityService-Host class in your run time environment. How you do this for a self-hosted service is different from how you do it for a service hosted in IIS or WAS.
现在你定义了新的UnityServiceHost 类,它使用Unity去实例化你的WCF服务,你必须在你的允许环境中创建UnityServiceHost 类的实例。 对于自托管服务,如何执行此操作不同于在IIS或WAS中托管服务的操作。
Using the UnityServiceHost Class with a Self-hosted Service
使用UnityServiceHost类与自托管服务
If you are self-hosting the service, you can instantiate the UnityServiceHost class directly in your hosting application and pass it a Unity container as shown in the following code sample.
如果你是自托管服务,你可以直接在你托管应用程序实例化UnityServiceHost 类并且将它传入到Unity容器中,就像下面代码示例演示的一样。
class Program
{
static void Main(string[] args)
{
// 注册类型与Unity
using (IUnityContainer container = new UnityContainer())
{
RegisterTypes(container);
// Step 1 创建一个URI最为服务地址.
Uri baseAddress = new Uri("http://localhost:8000/GettingStarted/");
// Step 2 创建ServiceHost 实例
ServiceHost selfHost = new UnityServiceHost(container,
typeof(CalculatorService.CalculatorService), baseAddress);
try
{
// Step 3 添加服务端口
selfHost.AddServiceEndpoint(typeof(ICalculator), new WSHttpBinding(),"CalculatorService");
// Step 4 启用元数据交换
var smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
selfHost.Description.Behaviors.Add(smb);
// Step 5 开启服务
selfHost.Open();
Console.WriteLine("The service is ready.");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.WriteLine();
Console.ReadLine();
// 关闭ServiceHostBase以关闭服务。
selfHost.Close();
}
catch (CommunicationException ce)
{
Console.WriteLine("An exception occurred: {0}", ce.Message);
selfHost.Abort();
}
}
}
private static void RegisterTypes(IUnityContainer container)
{
container.RegisterType<ICalculationEngine, SimpleEngine>();
}
}
Using the UnityServiceHost Class with Service Hosted in IIS or WAS
使用IISService或WAS中托管的服务的UnityServiceHost类
If you are hosting your WCF service in IIS or WAS it is a little more complex because you can no longer directly create a service host and by default, IIS will create a ServiceHost and not a UnityServiceHost instance. To get around this problem, you must create a service host factory as shown in the following code sample.
如果你在IIS或WAS中托管你的WCF服务,它是一个更复杂一点,因为你不能再直接创建一个服务主机,默认情况下,IIS将创建一个ServiceHost而不是一个UnityServiceHost实例。要解决此问题,您必须创建一个服务主机工厂,如下面的代码示例所示。
class UnityServiceHostFactory : ServiceHostFactory
{
private readonly IUnityContainer container;
public UnityServiceHostFactory()
{
container = new UnityContainer();
RegisterTypes(container);
}
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return new UnityServiceHost(this.container,serviceType, baseAddresses);
}
private void RegisterTypes(IUnityContainer container)
{
container.RegisterType<ICalculationEngine, SimpleEngine>();
}
}
This factory class creates a Unity container instance and passes it in to the constructor of the new UnityServiceHost class.
The final step is to instruct IIS or WAS to use the factory to create a service host. You can do this in the .svc file for the service as shown in the following example.
这个工厂类创建一个Unity容器实例并将它传入新的UnityServiceHost 类构造函数中。
最后一个步骤是通知IIS或WAS去使用工厂去创建服务托管。您可以在服务的.svc文件中执行此操作,如以下示例所示。
<%@ ServiceHost Language="C#" Debug="True" Service="CalculatorService.CalculatorService" Factory="CalculatorService.UnityServiceHostFactory" %>
Automatic Factories
自动工厂
Sometimes, your application does not know all the details of the objects to construct until run time. For example, a class called SurveyAnswerStore uses one of two queues, depending on whether the tenant is a premium or standard tenant. A simple approach is to use Unity to resolve both queue types as shown in the following sample.
有些时候,您的应用程序不知道要构建的对象的所有细节,直到运行时。例如,一个名为SurveyAnswerStore的类使用两个队列之一,具体取决于租户是高级租户还是标准租户。一个简单的方式是使用Unity去解析两种队列类型,如下面的示例所示。
class SurveyAnswerStore : IsurveyAnswerStore
{
...
public class SurveyAnswerStore : IsurveyAnswerStore
{
...
private readonly IMessageQueue<SurveyAnswerStoredMessage> standardSurveyAnswerStoredQueue;
private readonly IMessageQueue<SurveyAnswerStoredMessage> premiumSurveyAnswerStoredQueue;
public SurveyAnswerStore(
ITenantStore tenantStore,
ISurveyAnswerContainerFactory surveyAnswerContainerFactory,
IMessageQueue<SurveyAnswerStoredMessage> standardSurveyAnswerStoredQueue,
IMessageQueue<SurveyAnswerStoredMessage> premiumSurveyAnswerStoredQueue,
IBlobContainer<List<string>> surveyAnswerIdsListContainer)
{
...
this.standardSurveyAnswerStoredQueue = standardSurveyAnswerStoredQueue;
this.premiumSurveyAnswerStoredQueue = premiumSurveyAnswerStoredQueue;
}
public void SaveSurveyAnswer(SurveyAnswer surveyAnswer)
{
var tenant = this.tenantStore.GetTenant(surveyAnswer.Tenant);
...
(
SubscriptionKind.Premium.Equals(tenant.SubscriptionKind)
? this.premiumSurveyAnswerStoredQueue
: this.standardSurveyAnswerStoredQueue
).AddMessage(new SurveyAnswerStoredMessage
{
...
});
}
}
...
}
In this example, when the container resolves the SurveyAnswerStore type it will inject two IMessageQueue<SurveyAnswerStoredMessage> instances. If you know that only one of these instances will be used, you might consider optimizing the solution to create only the instance you need.
在这个示例中,当容器解析SurveyAnswerStore 类型,它将注入两个IMessageQueue<SurveyAnswerStoredMessage>实体。如果您知道只有其中一个实例将被使用,您可以考虑优化解决方案以仅创建所需的实例。
One approach is to write a factory class that will instantiate the correct instance, and then take a dependency on the factory. The following code sample shows this approach.
一个方式是去编写工厂类,它将实例化正确的实体,然后依赖于工厂。下面示例代码演示了这个方式。
class SurveyAnswerStore : IsurveyAnswerStore
{
...
private readonly ISurveyAnswerQueueFactory surveyAnswerQueueFactory;
public SurveyAnswerStore(
ITenantStore tenantStore,
ISurveyAnswerContainerFactory surveyAnswerContainerFactory,
ISurveyAnswerQueueFactory surveyAnswerQueueFactory,
IBlobContainer<List<string>> surveyAnswerIdsListContainer)
{
...
this.surveyAnswerQueueFactory = surveyAnswerQueueFactory;
}
public void SaveSurveyAnswer(SurveyAnswer surveyAnswer)
{
var tenant = this.tenantStore.GetTenant(surveyAnswer.Tenant);
...
((tenant.SubscriptionKind == "Premium")
? this.surveyAnswerQueueFactory.GetPremiumQueue()
: this.surveyAnswerQueueFactory.GetStandardQueue())
.AddMessage(new SurveyAnswerStoredMessage
{
...
});
}
...
}
For this approach to work, in addition to writing the factory class, you must register the factory class with the container so that the container can inject it when it resolves the SurveyAnswerStore type.
为了这种方法工作,除了编写工厂类,您必须注册工厂类与容器,当解析SurveyAnswerStore类型时,以便容器可以注入它。
A further refinement is to use Unity’s automatic factory approach. Using this approach you do not need to write and register a factory class, Unity creates a lightweight factory and registers it on your behalf. The following code sample shows this approach.
进一步改进是使用Unity的自动工厂方法。使用这个方法你不需要去写和注册工厂类,Unity创建一个轻量级工厂,并代表您注册它。下面示例代码演示这个方法。
class SurveyAnswerStore : IsurveyAnswerStore
{
...
private readonly Func<IMessageQueue<SurveyAnswerStoredMessage>> standardSurveyAnswerQueueFactory;
private readonly Func<IMessageQueue<SurveyAnswerStoredMessage>> premiumSurveyAnswerQueueFactory;
public SurveyAnswerStore(
ITenantStore tenantStore,
ISurveyAnswerContainerFactory surveyAnswerContainerFactory,
[Dependency("Standard")]Func<IMessageQueue<SurveyAnswerStoredMessage>> standardSurveyAnswerQueueFactory,
[Dependency("Premium")]Func<IMessageQueue<SurveyAnswerStoredMessage>> premiumSurveyAnswerQueueFactory,
IBlobContainer<List<string>> surveyAnswerIdsListContainer)
{
...
this.standardSurveyAnswerQueueFactory = standardSurveyAnswerQueueFactory;
this.premiumSurveyAnswerQueueFactory = premiumSurveyAnswerQueueFactory;
}
public void SaveSurveyAnswer(SurveyAnswer surveyAnswer)
{
var tenant = this.tenantStore.GetTenant(surveyAnswer.Tenant);
...
((tenant.SubscriptionKind == "Premium")
? premiumSurveyAnswerQueueFactory()
: standardSurveyAnswerQueueFactory())
.AddMessage(new SurveyAnswerStoredMessage
{
...
});
}
...
}
In this example, the dependencies of the SurveyAnswerStore class are on values of the form Func<T>. This enables the container to generate delegates that perform the type resolution when they are invoked: in the sample code, the delegates are called premiumSurveyAnswerQueueFactory and standard-SurveyAnswerQueueFactory.
在这个示例中,SurveyAnswerStore 类的依赖关系在Func<T>的值上。这使得容器能够生成在调用时执行类型解析的代理:在示例代码中,代理称为premiumSurveyAnswer-QueueFactory和standardSurveyAnswerQueueFactory。
One drawback of this specific example is that because the two queues use named registrations in the container, you must use the Dependency attribute to specify which named registration to resolve. This means that the SurveyAnswerStore class has a dependency on Unity.
在这个例子中有一个缺陷,因为两个队列在容器中使用命名注册,你必须使用Dependency 属性去指定要解析的命名注册。这意味着SurveyAnswerStore类具有对Unity的依赖。
You don’t need to change the registrations in the container to make this approach work.
你不需要修改容器中的注册,使此方法工作。
Deferred Resolution
延时解析
Sometimes, you may want to resolve an object from the container, but defer the creation of the object until you need to use it. You can achieve this with Unity by using the Lazy<T> type from the .NET Framework; this type provides support for the lazy initialization of objects.
优势后,你可能想在容器中解析一个对象,但是延时创建对象,直到你需要使用它你可以。你可以使用Unity .NET框架中的Lazy<T>类型实现这一点;这个类型类型提供支持惰性初始化的对象。
To use this approach with Unity, you can register the type you want to use in the standard way, and then use the Lazy<T> type when you resolve it. The following code sample shows this approach.
在Unity中使用这种方法,你可以以标准方式注册要使用的类型,然后在解析它时使用Lazy<T>类型。下面示例代码演示这个方法。
// 注册类型
container.RegisterType<MySampleObject>(new InjectionConstructor("default"));
// 使用 Lazy<T>解析类型
var defaultLazy = container.Resolve<Lazy<MySampleObject>>();
// 使用解析对象
var mySampleObject = defaultLazy.Value;
Lazy<T> doesn’t work very well with value types, and it is better to avoid in this case. You should use the Lazy<T> type very cautiously.
Lazy<T>对值类型不能很好地工作,在这种情况下最好避免。 你应该非常谨慎地使用Lazy<T>类型。
This example is adapted from the sample application, “OtherUnitySamples,” included with this guidance.
此示例改编于本指南中包含的示例应用程序“OtherUnitySamples”。
You can use lazy resolution with the Unity lifetime managers. The following example, again adapted from the sample application illustrates this with the ContainerManagedLifetime class.
你一使用惰性解析用于Unity生命周期管理器。下面的示例再次从示例应用程序中进行了调整,使用ContainerManagedLifetime类进行了说明。
// 向生命周期管理器注册类型
container.RegisterType<MySampleObject>(
"other", new ContainerControlledLifetimeManager(),
new InjectionConstructor("other"));
// 解析惰性类型
var defaultLazy1 = container.Resolve<Lazy<MySampleObject>>("other");
// 第二次解析惰性类型
var defaultLazy2 = container.Resolve<Lazy<MySampleObject>>("other");
// defaultLazy1 == defaultLazy2 is false
// defaultLazy1.Value == defaultLazy2.Value is true
For more information about Lazy<T>, see the topic Lazy<T> Class on MSDN.
You can also use the Resolve method to resolve registered types by using Func<T> in a similar way.
关于Lazy<T> 更多详细信息请看MSDN中的Lazy<T> Class主题。
你也可以使用Resolve 方法通过使用Func <T>以类似的方式解析注册的类型。
Lifetime Management
生命周期管理器
When you resolve an object that you registered using the RegisterType method, the container instantiates a new object when you call the Resolve method: the container does not hold a reference to the object. When you create a new instance using the RegisterInstance method, the container manages the object and holds a reference to it for the lifetime of the container.
当您解析使用RegisterType方法注册的对象时,容器在调用Resolve方法时实例化一个新对象:容器没有保存容器的引用。当你使用RegisterInstance 方法创建一个新的实例,容器管理对象并在容器的生命周期中保持对它的引用。
Lifetime Managers manage the lifetimes of objects instantiated by the container. The default lifetime manager for the RegisterType method is the Transient-LifetimeManager and the default lifetime manager for the RegisterInstance method is the ContainerControlledLifetimeManager. If you want the container to create or return a singleton instance of a type when you call the
Resolve method, you can use the ContainerControlledLifetimeManager type when you register your type or instance. The following example shows how you could tell the container to create a singleton instance of the TenantStore.
生命周期管理器管理容器实例化的对象的生命周期。RegisterType 方法方法的默认生命周期管理器是TransientLifetimeManager,RegisterInstance 方法的莫仍生命周期管理器是ContainerControlledLifetimeManager。如果希望容器在调用Resolve方法时创建或返回类型的单例实例,则可以在注册类型或实例时使用ContainerControlledLifetimeManager类型。下面示例代码演示如何指示容器去创建TenantStore单例示例。
container.RegisterType<ITenantStore, TenantStore>(new ContainerControlledLifetimeManager());
The first time that you resolve the ITenantStore type the container creates a new TenantStore object and keeps a reference to it. On subsequent times when you resolve the ITenantStore type, the container returns a reference to the TenantStore object that it created previously. Some lifetime managers, such as the ContainerControlledLifetimeManager, are used to dispose the created objects when the container is disposed.
第一次解析ITenantStore 类型,容器创建一个新的TenantStore 对象并保持它的引用。当你以后解析ITenantStore 类型时,容器返回以前创建的TenantStore 对象的引用。一些生命周期管理器,比如ContainerControlledLifetimeManager,用于在容器处理时处理创建的对象。
Unity includes five other lifetime managers, described in the following sections, that you can use to address specific scenarios in your applications.
Unity包含5个其他生命周期管理器,下面章节中描述,你可以用来解决应用程序中的特定情况。
Lifetime managers enable you to control for how long the objects created by the container should live in your application. You can override the default lifetime managers that the RegisterType and RegisterInstance methods use.
生命周期管理是你能够控制容器创建的对象可以在你的应用程序中应该存在的时间。你可以覆盖RegisterType和RegisterInstance方法使用的默认生命周期管理器。
Hierarchical Lifetime Management
分层生命周期管理
This type of lifetime management is useful if you have a hierarchy of containers. Earlier in this chapter, you saw how to use child containers to manage alternative mappings for the same type. You can also use child containers to manage the lifetime of resolved objects. Figure 2 illustrates a scenario where you have created two child containers and registered a type using the ContainerControlled-LifetimeManager type to create a singleton.
如果你有一个分层的容器,这个生命周期管理器类型时有用的。在本章前面,你已经看到如何使用子容器管理相同类型的映射。你也可以使用子容器去管理解析对象的生命周期。图2演示了一个场景,你已创建了两个子容器,并且使用ContainerControlledLifetimeManager 类型注册了一个类型以创建单例。
Figure 2
Container hierarchy with ContainerControlledLifetimeManager lifetime manager
图2
ContainerControlledLifetimeManager生命周期管理器的容器层次结构
If the client object executes the following code that creates the containers, performs the registrations, and then resolves the types, the three variables (tenant1, tenant2, and tenant3) all refer to the same instance managed by the containers.
如果客户对象执行下面代码创建容器,执行注册,然后解析类型,3个变量(tenant1, tenant2, 和tenant3)都参考容器管理的同一个示例。
IUnityContainer container = new UnityContainer();
container.RegisterType<ITenantStore, TenantStore>(
new ContainerControlledLifetimeManager());
IUnityContainer child1 = container.CreateChildContainer();
IUnityContainer child2 = container.CreateChildContainer();
var tenant1 = child1.Resolve<ITenantStore>();
var tenant2 = child2.Resolve<ITenantStore>();
var tenant3 = container.Resolve<ITenantStore>();
However, if you use the HierarchicalLifetimeManager type, the container resolves the object as shown in Figure 3.
然而,如果你使用HierarchicalLifetimeManager 类型,容器解析对象就像图3所示。
Figure 3
Container hierarchy with HierarchicalLifetimeManager lifetime manager
图3,HierarchicalLifetimeManager 生命周期管理器的容器层次结构
If the client executes the following code, the three variables (tenant1, tenant2, and tenant3) each refer to different TenantStore instances.
如果客户执行下面代码,3个变量(tenant1, tenant2, 和tenant3)每个都参考不同的TenantStore 实例。
IUnityContainer container = new UnityContainer();
container.RegisterType<ITenantStore, TenantStore>(
new HierarchicalLifetimeManager());
IUnityContainer child1 = container.CreateChildContainer();
IUnityContainer child2 = container.CreateChildContainer();
var tenant1 = child1.Resolve<ITenantStore>();
var tenant2 = child2.Resolve<ITenantStore>();
var tenant3 = container.Resolve<ITenantStore>();
Although you register the type with the parent container, each child container now resolves its own instance. Each child container manages its own singleton instance of the TenantStore type; therefore, if you resolve the same type from container #1 a second time, the container returns a reference to the instance it created previously.
虽然你在父容器中注册的类型,每个子容器现在解析它自己的实例。每个子容器管理它自己TenantStore 类型单例实例;因此,如果你从容器#1第二次解析相同类型,容器返回容器以前创建的实例引用。
Per Resolve Lifetime Management
Per Resolve生命周期管理器
Figure 4 shows part of the dependency tree for an application: the Surveys-Controller type depends on the SurveyStore and SurveyAnswerStore types, both the SurveyStore and SurveyAnswerStore types depend on the Tenant-Store type.
图4显示了应用程序的依赖关系树的一部分:SurveysController类型与SurveyStore 和SurveyAnswerStore 类型,SurveyStore 和SurveyAnswerStore 类型双方都依赖于TenantStore类型。
Figure 4
Sample dependency tree
图4,简单的依赖关系树
If you use the default TransientLifetimeManager class when you register the SurveysController type, then when you resolve the SurveysController type, the container builds the object graph shown in Figure 5.
如果在注册SurveysController类型时使用默认的TransientLifetimeManager类,那么在解析SurveysController类型时,容器将构建如图5所示的对象图。
Figure 5
Object graph generated using TransientLifetimeManager lifetime manager
图5,使用TransientLifetimeManager 对象生命管理器产生的对象图。
However, if you use the PerResolveLifetimeManager class in place of the TransientLifetimeManager class, then the container builds the object graph shown in Figure 6. With the PerResolveLifetimeManager class, the container reuses any instances it resolves during a call to the Resolve method in any other types it resolves during the same call.
然而,如果你在TransientLifetimeManager 类使用PerResolveLifetimeManager 类的地方,然后容器创建对象图如图6。使用PerResolveLifetimeManager类,容器重用在调用Resolve方法期间解析的任何实例,即便是其他类型中相同的调用。
Figure 6
Object graph generated using the PerResolveLifetimeManager class
图6 使用PerResolveLifetimeManager 类的对象图
Externally Controlled Lifetime Management
外部控制生命周期管理
If you resolve a type that was registered using the ContainerControlledLifetime-Manager class, the container creates a singleton instance and holds a strong reference to it: this means that the instance lives at least as long as the container. However, if you use the ExternallyControlledLifetimeManager class, when you resolve the type, the container creates a singleton instance but holds only a weak reference to it. In this case, you can directly manage the lifetime of the object: because of the weak reference, you can dispose of the object when you no longer need it. This enables you to inject objects that are not owned by the container; for example you might need to inject objects whose lifetime is managed by ASP.NET into instances created by the container. Also, the Externally-ControlledLifetimeManager class does not dispose the instances it holds references to when the container is disposed.
如果你解析使用ContainerControlledLifetimeManager类注册的类型,容器创建一个单例实例和保存对象的强引用:这意味着实例一直存在容器中。然而,如果你使用Externally-ControlledLifetimeManager 类,当你解析类型时,容器创建一个单例实例,但是只会保存对象的弱引用。在这种情况中,你可以直接管理对象的什么周期,因为弱引用当你不再需要对象,你可以处理它。这使你能够注入不属于该容器的对象;例如,您可能需要将其生命周期由ASP.NET管理的对象注入到容器创建的实例中。另外,ExternallyControlledLifetimeManager类不处理容器引用的实例。
Per Request Lifetime Management
每个请求生命周期管理
This lifetime manager is only available for use in Web applications when you’ve added the “Unity bootstrapper for ASP.NET MVC” NuGet package to your project. The PerRequestLifetimeManager class enables the container to create new instances of registered types for each HTTP request in an ASP.NET MVC application or an ASP.NET Web API application. Each call to Resolve a type within the context of a single HTTP request will return the same instance: in effect, the Unity container creates singletons for registered types for the duration of the HTTP request.
这个生命走起管理器只会在Web应用程序中使用,当你讲添加“Unity bootstrapper for ASP.NET MVC”NuGet包到你的项目中时。PerRequestLifetimeManager 类也可以使容器创建一个新的注册类型实例到ASP.NET MVC应用或ASP.NET Web API应用程序中的每一个Http请求。每次调用在单个HTTP请求的上下文中解析类型将返回相同的实例:实际上,Unity容器在HTTP请求持续时间创建注册类型的单例。
Although the PerRequestLifetimeManager class works correctly and can help you to work with stateful or thread-unsafe dependencies within the scope of an HTTP request, it is generally not a good idea to use it if you can avoid it. Using this lifetime manager can lead to bad practices or hard to find bugs in the end user’s application code when used incorrectly. The dependencies you register with the Unity container should be stateless, and if you have a requirement to share common state between several objects during the lifetime of an HTTP request, then you can have a stateless service that explicitly stores and retrieves this state using the System.Web.HttpContext.Items collection of the System.Web.HttpContext.Current object.
虽然PerRequestLifetimeManager类工作正常,可以帮助您在HTTP请求的范围内使用状态或线程不安全依赖,如果你能避免它,它通常不是你使用它的一个好想法。使用这个生命周期管理器会导致不良做法,或者在使用不当时,很难在最终用户的应用程序代码中发现错误。您注册到Unity容器的依赖关系应该是无状态的,如果你有需要在HTTP请求生命周期期间共享多个对象的公共状态,您可以有一个无状态服务,使用System.Web.HttpContext.Current对象的System.Web.HttpContext.Items集合显式存储和检索此状态。
Think carefully about the implications for state management in your application if you plan to use the PerRequestLifetimeManager lifetime manager class.
如果你计划使用PerRequestLifetimeManager 生命周期管理器类,仔细思考你应用程序中对状态管理的影响。
Per Thread Lifetime Management
每个线程生命周期管理器
The final lifetime manger included with Unity enables you to resolve instances on a per thread basis. All calls to the Resolve method from the same thread return the same instance.
For more information about Unity’s lifetime managers, see the topic Understanding Lifetime Managers
Unity中包含的最后一个生命周期管理器使您能够在每个线程的基础上解析实例。所有从同一线程调用Resolve 方法都返回相同的实例。
更多关于Unity生命周期管理器信息,请见Understanding Lifetime Managers 话题。
Dependency Injection and Unit Testing
依赖注入和单元测试
In Chapter 1, one of the motivations for adopting a loosely coupled design was that it facilitates unit testing. In the example used in this chapter, one of the types registered with the container is the TenantStore class. The following code sample shows an outline of this class.
在第一章,采用松散耦合的一个动机是促进单元测试。在本章中使用的实例中,用容器注册的类型之一是TenantStore 类。以下代码示例显示了此类的概要。
public class TenantStore : ItenantStore
{
...
public TenantStore(IBlobContainer<Tenant> tenantBlobContainer,
IBlobContainer<byte[]> logosBlobContainer)
{
...
}
public Tenant GetTenant(string tenant)
{
...
}
public IEnumerable<string> GetTenantNames()
{
...
}
public void SaveTenant(Tenant tenant)
{
...
}
public void UploadLogo(string tenant, byte[] logo)
{
...
}
}
This class has dependencies on the IBlobContainer<Tenant> and IBlobContainer<byte[]> types which the container resolves when it instantiates a TenantStore object. However, to test this class in a unit test, you don’t want to have to create these blob containers: now it’s easy to replace them with mocks for the purpose of the tests. The following code sample shows some example tests.
此类具有对容器在实例化TenantStore对象时解析的IBlobContainer<Tenant>和IBlobContainer<byte []>类型的依赖性。然而在单元测试中测试这个类,你不想去创建这些blob容器:现在很容易用模拟器替换它们用于测试的目的。下面示例代码演示一些测试例子。
IUnityContainer container = new UnityContainer();
[TestMethod]
public void GetTenantReturnsTenantFromBlobStorage()
{
var mockTenantBlobContainer = new Mock<IBlobContainer<Tenant>>();
var store = new TenantStore(mockTenantBlobContainer.Object, null);
var tenant = new Tenant();
mockTenantBlobContainer.Setup(c => c.Get("tenant")).Returns(tenant);
var actualTenant = store.GetTenant("tenant");
Assert.AreSame(tenant, actualTenant);
}
[TestMethod]
public void UploadLogoGetsTenantToUpdateFromContainer()
{
var mockLogosBlobContainer = new Mock<IBlobContainer<byte[]>>();
var mockTenantContainer = new Mock<IBlobContainer<Tenant>>();
var store = new TenantStore(
mockTenantContainer.Object, mockLogosBlobContainer.Object);
mockTenantContainer.Setup(c => c.Get("tenant"))
.Returns(new Tenant() { Name = "tenant" }).Verifiable();
mockLogosBlobContainer.Setup(
c => c.GetUri(It.IsAny<string>())).Returns(new Uri("http://bloburi"));
store.UploadLogo("tenant", new byte[1]);
mockTenantContainer.Verify();
}
These two example tests provide mock objects that implement the IBlobContainer<Tenant> and IBlob-Container<byte[]> interfaces when they create the TenantStore instances to test.
这两个测试示例提供模拟对象实现IBlobContainer<Tenant> IBlob-Container<byte[]> 接口,当他们创建TenantStore 实例去测试时。
These examples use the Moq mocking library to create the mock objects. For more information,
see http://code.google.com/p/moq/. Moq is also available as a NuGet package.
这些示例使用Moq模拟库区创建mock对象。更多信息请看http://code.google.com/p/moq/ Moq㛑路成绩排名高NuGet包中获得。
Summary
概要
In this chapter, you saw how to use the Unity container to add support for dependency injection to a real-world application and how you can use a Unity container to register types, resolve types at runtime, and manage the lifetime of the resolved objects. In addition to seeing how the Unity container made it possible to build the application’s object graph at startup, you also saw how this approach facilitated designing and running unit tests.
在本章中,你已经看到如何使用Unity容器去添加现实应用程序的依赖注入支持,和如何使用Unity容器在运行时去注册类型、解析类型,并且管理解析的对象的生命周期。除了看看Unity容器如何使得在启动时构建应用程序的对象图,你还看到了这种方法如何促进设计和运行单元测试。
More Information
更多信息
All links in this book are accessible from the book’s online bibliography available at: http://aka.ms/unitybiblio
本书中的所有链接均可从本书的在线参考书目获得,网址为:http://aka.ms/unitybiblio
Unity文档阅读 第三章 依赖注入与Unity的更多相关文章
- Unity文档阅读 第一章 入门
Before you learn about dependency injection and Unity, you need to understand why you should use the ...
- Unity文档阅读 第二章 依赖注入
Introduction 介绍Chapter 1 outlines how you can address some of the most common requirements in enterp ...
- elasticsearch 文档阅读笔记(三)
文档 elasticsearch是通过document的形式存储数据的,个人理解文档就是一条数据一个对象 我们添加索引文档中不仅包含了数据还包含了元数据 比如我们为一个数据添加索引 文档中不仅有jso ...
- ASP.NET Core中使用GraphQL - 第三章 依赖注入
ASP.NET Core中使用GraphQL ASP.NET Core中使用GraphQL - 第一章 Hello World ASP.NET Core中使用GraphQL - 第二章 中间件 SOL ...
- Django文档阅读-Day2
Django文档阅读 - Day2 Writing your first Django app, part 1 You can tell Django is installed and which v ...
- 转:苹果Xcode帮助文档阅读指南
一直想写这么一个东西,长期以来我发现很多初学者的问题在于不掌握学习的方法,所以,Xcode那么好的SDK文档摆在那里,对他们也起不到什么太大的作用.从论坛.微博等等地方看到的初学者提出的问题,也暴露出 ...
- Node.js的下载、安装、配置、Hello World、文档阅读
Node.js的下载.安装.配置.Hello World.文档阅读
- 我的Cocos Creator成长之路1环境搭建以及基本的文档阅读
本人原来一直是做cocos-js和cocos-lua的,应公司发展需要,现转型为creator.会在自己的博客上记录自己的成长之路. 1.文档阅读:(cocos的官方文档) http://docs.c ...
- Keras 文档阅读笔记(不定期更新)
目录 Keras 文档阅读笔记(不定期更新) 模型 Sequential 模型方法 Model 类(函数式 API) 方法 层 关于 Keras 网络层 核心层 卷积层 池化层 循环层 融合层 高级激 ...
随机推荐
- RTMPdump(libRTMP) 源代码分析 5: 建立一个流媒体连接 (NetConnection部分)
===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...
- iOS自定义多参数类型方法
前几天做自定义UIAlertView的时候,想仿造系统自带的初始化方法做一个AlertView,里面涉及到不确定多参数的设置和使用问题.这里做一下记录. 我自定义了一个方法: - (instancet ...
- JNI技术简介-android学习之旅(92)
分为5步 !!!注意本地方法是java中的方法,本地函数指的是c语言中的对应函数 1.在java类中声明本地方法 2.使用javah命令,生成包含jni本地函数原型的头文件 3. 实现jni本地函数 ...
- 【作业2.0】HansBug的5-7次OO作业分析与小结,以及一些个人体会
不知不觉又做了三次作业,容我在本文胡言乱语几句2333. 第五次作业 第五次作业是前面的电梯作业的多线程版本,难度也有了一些提升.(点击就送指导书) 类图 程序的类图结构如下: UML时序图 程序的逻 ...
- 【模板】最近公共祖先(LCA)
题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...
- 2018 ACM-ICPC World Finals B.Comma Sprinkler
WF里面最简答一题,就是一个dfs就可以了,已经访问过的点可以不再访问 #include <algorithm> #include <cmath> #include <c ...
- SpringBoot的第一个例子
1. 安装springboot的开发IDE,IntelliJ IDEA 2016.3.1这个工具,在IDE的官网上可以下载最新版本.https://www.jetbrains.com/idea/#ch ...
- python字符串27种常见的方法
如有字符串 mystr = 'hello world itcast and itcastcpp' ,以下是常见的操作: <1>find 检测 str 是否包含在 mystr中,如果是返回开 ...
- centos 5.3 安装(samba 3.4.4)
centos 5.3 安装(samba 3.4.4) 博客分类: 操作系统 Linux 随着Linux的普及,如何共享Linux下的文件成为用户关心的问题.其实,几乎所有的Linux发行套件都提供 ...
- ORACLE数据库维护
ORACLE数据库维护(转)----一篇关于oracle的不错的文章 1. ORACLE数据库启动与关闭 1.1 打开和关闭数据库 (手工)1.1.1 sqlplus连接 1.1.2 打开数据 ...