简要介绍 My.Ioc 的用法

下面这段代码展示了 My.Ioc 的基本用法:

  1 using System;
  2 using System.Collections.Generic;
  3 
  4 namespace My.Ioc.Sample
  5 {
  6     public interface IConcurrency
  7     {
  8         int Code { get; }
  9     }
 10 
 11     public interface IConcurrencyService
 12     {
 13         string Name { get; }
 14         void AddConcurrency(IConcurrency concurrency);
 15         void RemoveConcurrency(IConcurrency concurrency);
 16     }
 17 
 18     public class ConcurrencyService : IConcurrencyService
 19     {
 20         readonly Dictionary<int, IConcurrency> _concurrencies = new Dictionary<int, IConcurrency>();
 21 
 22         public string Name
 23         {
 24             get { return GetType().Name; }
 25         }
 26 
 27         public void AddConcurrency(IConcurrency concurrency)
 28         {
 29             _concurrencies.Add(concurrency.Code, concurrency);
 30         }
 31 
 32         public void RemoveConcurrency(IConcurrency concurrency)
 33         {
 34             _concurrencies.Remove(concurrency.Code);
 35         }
 36     }
 37 
 38     public class NewConcurrencyService : IConcurrencyService
 39     {
 40         #region IConcurrencyService Members
 41 
 42         public string Name
 43         {
 44             get { return GetType().Name; }
 45         }
 46 
 47         public void AddConcurrency(IConcurrency concurrency)
 48         {
 49             throw new NotImplementedException();
 50         }
 51 
 52         public void RemoveConcurrency(IConcurrency concurrency)
 53         {
 54             throw new NotImplementedException();
 55         }
 56 
 57         #endregion
 58     }
 59 
 60     public interface ISimpleConsumer
 61     {
 62         IConcurrencyService ConcurrencyService { get; }
 63     }
 64 
 65     public class SimpleConsumer : ISimpleConsumer
 66     {
 67         readonly IConcurrencyService _concurrencyService;
 68 
 69         public SimpleConsumer(IConcurrencyService concurrencyService)
 70         {
 71             _concurrencyService = concurrencyService;
 72         }
 73 
 74         public IConcurrencyService ConcurrencyService
 75         {
 76             get { return _concurrencyService; }
 77         }
 78     }
 79 
 80     public interface IComplexConsumer : IDisposable
 81     {
 82         IConcurrencyService ConcurrencyService { get; }
 83     }
 84 
 85     public class ComplexConsumer : IComplexConsumer
 86     {
 87         readonly string _name;
 88         readonly IConcurrencyService _concurrencyService;
 89 
 90         public ComplexConsumer(string name, IConcurrencyService concurrencyService)
 91         {
 92             _name = name;
 93             _concurrencyService = concurrencyService;
 94         }
 95 
 96         public IConcurrencyService ConcurrencyService
 97         {
 98             get { return _concurrencyService; }
 99         }
100 
101         public string Name
102         {
103             get { return _name; }
104         }
105 
106         public string Address { get; set; }
107 
108         public void Print()
109         {
110             Console.WriteLine(_name + " who lives in " + (Address ?? "Fujian") + " is using the service " + _concurrencyService.Name);
111         }
112 
113         public void Dispose()
114         {
115             Console.WriteLine("ComplexConsumer is disposing...");
116         }
117     }
118 
119     class Program
120     {
121         static IObjectRegistration _concurrencyServiceRegistration;
122         static IObjectObserver<ISimpleConsumer> _simpleConsumerObserver;
123 
124         static void Main(string[] args)
125         {
126             // First, we need to create an instance of IObjectContainer.
127             IObjectContainer container = new ObjectContainer(true);
128 
129             // Then, we register some services
130             container.Register<IConcurrencyService, ConcurrencyService>()
131                 .WhenParentTypeIsAny(typeof(SimpleConsumer), typeof(ComplexConsumer))
132                 .In(Lifetime.Container())
133                 .Set("ConcurrencyService")
134                 .Return(out _concurrencyServiceRegistration);
135 
136             container.Register<ISimpleConsumer, SimpleConsumer>();
137 
138             var consumerName = Parameter.Positional("Johnny.Liu");
139             container.Register<IComplexConsumer, ComplexConsumer>()
140                 .WithConstructor(consumerName)
141                 .WithPropertyValue("Address", "Fujian")
142                 .WithMethod("Print")
143                 .In(Lifetime.Transient());
144 
145             // Finally, don't forget to commit the registrations to the registry.
146             container.CommitRegistrations();
147 
148             // Now you can ask the container to build instances for you.
149             var simpleConsumer1 = container.Resolve<ISimpleConsumer>();
150 
151             if (!container.TryGetObserver(out _simpleConsumerObserver))
152                 throw new Exception();
153             _simpleConsumerObserver.Changed += OnObjectBuilderChanged;
154             var simpleConsumer2 = container.Resolve(_simpleConsumerObserver);
155 
156             using (var scope = container.BeginLifetimeScope())
157             {
158                 var complexConsumer = scope.Resolve<IComplexConsumer>();
159             }
160 
161             // At last, we will unregister the current concurrency service to let the other concurrency 
162             // service implementations to have a chance to replace it.
163             container.Unregister(_concurrencyServiceRegistration);
164             container.Register(typeof(IConcurrencyService), typeof(NewConcurrencyService));
165             // As we said, don't forget to commit the registrations to the registry.
166             container.CommitRegistrations();
167 
168             using (var scope = container.BeginLifetimeScope())
169             {
170                 var complexConsumer = scope.Resolve<IComplexConsumer>();
171             }
172 
173             Console.ReadLine();
174         }
175 
176         static void OnObjectBuilderChanged(ObjectBuilderChangedEventArgs args)
177         {
178             Console.WriteLine(args.ChangeMode);
179         }
180     }
181 }
View Code

用法比较简单,跟大家熟悉的大多数 Ioc 容器差不多。我们这里来逐句解释一下:

IObjectContainer container = new ObjectContainer(true);

这一句创建了一个 ObjectContainer 对象。构造参数 true 表示容器默认将采用 Emit(动态生成代码)方式来构建对象。

container.Register<IConcurrencyService, ConcurrencyService>()
                .WhenParentTypeIsAny(typeof(SimpleConsumer), typeof(ComplexConsumer))
                .In(Lifetime.Container())
                .Set("ConcurrencyService")
                .Return(out _concurrencyServiceRegistration);

Register 方法将一个 ConcurrencyService 实现绑定到 IConcurrencyService 契约。WhenParentTypeIsAny 方法指定该服务只能用于 SimpleConsumer 或 ComplexConsumer 类。因此,如果您稍后在解析时写上以下这句:

var concurrencyService = container.Resolve<IConcurrencyService>();

则会收到一个 InvalidObjectBuilderException 错误,因为我们在上面的配置中已经指定了 IConcurrencyService 这个服务只能在构造 SimpleConsumer 或 ComplexConsumer 实例时由相应的 ObjectBuilder 向容器请求。

In 方法指定该服务注册项的生命周期,这里使用的是 Container 生命周期,即注册项的生命周期将同容器一样长,而且每次向容器请求该服务时,返回的都是相同的实例。也就是说,这是一个单例对象。Set 方法表明附加一个元数据 ("ConcurrencyService") 到该服务注册项中。Return 方法返回一个 IObjectRegistration 对象。该对象可以作为一个存根,用于在不再需要该服务的时候注销该服务。同时,它也可用于解析服务对象(调用 container.Resolve(IObjectRegistration registration) 方法重载),而不必像调用 container.Resolve(Type contractType) 时一样每次都从注册表中检索服务。

var consumerName = Parameter.Positional("Johnny.Liu");
container.Register<IComplexConsumer, ComplexConsumer>()
    .WithConstructor(consumerName)
    .WithPropertyValue("Address", "Fujian")
    .WithMethod("Print")
    .In(Lifetime.Transient());

上面这句代码指定将 ComplexConsumer 绑定到 IComplexConsumer,同时我们注意到这里还提供了一个默认构造参数。在这里,这个构造参数是必需的,因为 ComplexConsumer 构造函数的签名是:

public ComplexConsumer(string name, IConcurrencyService concurrencyService)

我们看到这个构造函数的第一个参数是 string 类型,在 My.Ioc 中这种类型的参数是不可自动装配的 (non-autowirable),用户必须为不可自动装配的依赖项提供一个默认值(不可自动装配的类型包括:所有值类型 + string + Type)。

WithPropertyValue 方法和 WithMethod 方法告诉容器,在构建好 ComplexConsumer 对象后,立即将该对象的 Address 属性赋值为“Fujian”,并调用该对象的“Print”方法。

配置(注册)好所有服务之后,此时这些服务并没有添加到注册表,而是被缓存到一个 RegistrationCommitter 中,因此我们需要显式调用下面这句代码将所有注册项提交到注册表中:

container.CommitRegistrations();

经过上面的配置,我们终于可以让容器为我们构建对象实例了。

var simpleConsumer1 = container.Resolve<ISimpleConsumer>();

这句正是向容器请求返回一个实现 ISimpleConsumer 接口的对象。

除了直接向容器请求对象之外,我们还可以向容器请求返回一个 IObjectObserver/IObjectCollectionObserver 对象,并通过该对象来解析服务实例,如下所示:

if (!container.TryGetObserver(out _simpleConsumerObserver))
    throw new Exception();
_simpleConsumerObserver.Changed += OnObjectBuilderChanged;
var simpleConsumer2 = container.Resolve(_simpleConsumerObserver);

这样做的好处是,当该对象 (SimpleConsumer) 依赖的其他子对象(这里是 IConcurrencyService 对象)注册/注销/激活/停用时, Observer 对象将会收到通知(需要订阅该 Observer 对象的 Changed 事件)。此外,IObjectObserver 和 IObjectCollectionObserver 对象与 IObjectRegistration 对象一样,也可以直接用于解析服务对象,而不必每次都向注册表检索服务,从而可以提高性能。

IComplexConsumer 对象的解析与 ISimpleConsumer 有点不一样,我们看下面这段代码:

using (var scope = container.BeginLifetimeScope())
{
    var complexConsumer = scope.Resolve<IComplexConsumer>();
}

这里,我们首先向容器请求获取一个 ILifetimeScope 对象,然后使用该对象来解析 IComplexConsumer 对象。这是因为 IComplexConsumer 实现了 IDisposable 接口,这表明该对象在使用完之后需要清理资源。在 My.Ioc 框架中,解析任何实现了 IDisposable 接口的对象时都需要先申请一个 ILifetimeScope,因为对象资源的清理是通过 ILifetimeScope 来完成的。ILifetimeScope 类似于我们通常所说的“变量作用域”的概念,但它还实现了对象共享的功能,关于这个话题我们还会在以后的文章中加以介绍,这里不再赘言。

使用完毕之后,我们可以将不再需要的服务注册项注销,如下所示:

container.Unregister(_concurrencyServiceRegistration);

运行这句之后,_simpleConsumerObserver 将被停用,而无法再用于解析服务对象,因为其依赖的服务 (IConcurrencyService) 已被注销。同时 OnObjectBuilderChanged 方法也会收到相应通知。不仅 ISimpleConsumer,此时所有直接和间接依赖于 IConcurrencyService 的服务都将被停用,而无法用于解析服务对象。如果我们想要让这些服务再次恢复功能,可以再注册一个实现了 IConcurrencyService 契约的服务并将其注册到容器中,如下代码所示:

container.Register(typeof(IConcurrencyService), typeof(NewConcurrencyService));
container.CommitRegistrations();

这样,那些依赖于 IConcurrencyService 契约的服务都将恢复功能。我们来再次运行下面的代码看一看:

using (var scope = container.BeginLifetimeScope())
{
    var complexConsumer = scope.Resolve<IComplexConsumer>();
}

如无意外,运行上面这段代码,控制台将会输出:

"Johnny.Liu who lives in Fujian is using the service NewConcurrencyService"
"ComplexConsumer is disposing..."

表明我们的新 NewConcurrencyService 服务已取代了原来的 ConcurrencyService 并正常工作,且 ComplexConsumer 对象在超出作用域后已被清理。

总结

上面,我们简单地介绍了 My.Ioc 的使用方法。由于篇幅的缘故,很多问题并未谈及,我们将在以后的文章中逐一向大家介绍。

原文地址:https://www.cnblogs.com/johnny-liu/p/3953411.html