理解 IOC 容器
沒有使用 IOC 容器時:
使用 IOC 容器時:
去掉 IOC 容器時:
IOC容器又像一個插座,將電輸送到需要的每一處。需要充電的話,就連接,不需要就不連接,節省資源,不用時時刻刻連上電源了。
使用IOC容器的好處:
1) 可維護性比較好
2) 便於單元測試,調試程序和診斷故障
2) 可複用性好
實現組件之間的解耦,提高程序的靈活性和可維護性
AutoFac使用方法總結:
AutoFac 是 .net 平臺下的 IOC 容器產品,它可以管理類之間的複雜的依賴關係。在使用方面主要是 register 和 resolve 兩類操作。 這裏用單元測試的形式列舉了 AutoFac 的常用使用方法:
一、註冊
使用 RegisterType 進行註冊
[Fact]
public void can_resolve_myclass()
{
var builder = new ContainerBuilder();
builder.RegisterType<MyClass>();
IContainer container = builder.Build();
var myClass = container.Resolve<MyClass>();
Assert.NotNull(myClass);
}
註冊爲接口
[Fact]
public void register_as_interface()
{
var builder = new ContainerBuilder();
builder.Register(c => new MyClass()).As<MyInterface>();
IContainer container = builder.Build();
Assert.NotNull(container.Resolve<MyInterface>());
Assert.Throws(typeof (ComponentNotRegisteredException), () => container.Resolve<MyClass>());
}
使用lambda表達式進行註冊
[Fact]
public void can_register_with_lambda()
{
var builder = new ContainerBuilder();
builder.Register(c => new MyClass());
IContainer container = builder.Build();
var myClass = container.Resolve<MyClass>();
Assert.NotNull(myClass);
}
帶構造參數的註冊
[Fact]
public void register_with_parameter()
{
var builder = new ContainerBuilder();
builder.Register(c => new MyParameter());
builder.Register(c => new MyClass(c.Resolve<MyParameter>()));
IContainer container = builder.Build();
Assert.NotNull(container.Resolve<MyClass>());
}
帶屬性賦值的註冊
[Fact]
public void register_with_property()
{
var builder = new ContainerBuilder();
builder.Register(c => new MyProperty());
builder.Register(
c => new MyClass()
{
Property = c.Resolve<MyProperty>()
});
IContainer container = builder.Build();
var myClass = container.Resolve<MyClass>();
Assert.NotNull(myClass);
Assert.NotNull(myClass.Property);
}
Autofac分離了類的創建和使用,這樣可以根據輸入參數(NamedParameter)動態的選擇實現類。
[Fact]
public void select_an_implementer_based_on_parameter_value()
{
var builder = new ContainerBuilder();
builder.Register<IRepository>((c, p) =>
{
var type = p.Named<string>("type");
if (type == "test")
{
return new TestRepository();
}
else
{
return new DbRepository();
}
}).As<IRepository>();
IContainer container = builder.Build();
var repository = container.Resolve<IRepository>(new NamedParameter("type", "test"));
Assert.Equal(typeof(TestRepository), repository.GetType());
}
AufoFac也可以用一個實例來註冊,比如用在單例模式情況下:
[Fact]
public void register_with_instance()
{
var builder = new ContainerBuilder();
builder.RegisterInstance(MyInstance.Instance).ExternallyOwned();
IContainer container = builder.Build();
var myInstance1 = container.Resolve<MyInstance>();
var myInstance2 = container.Resolve<MyInstance>();
Assert.Equal(myInstance1, myInstance2);
}
註冊open generic類型
[Fact]
public void register_open_generic()
{
var builder = new ContainerBuilder();
builder.RegisterGeneric(typeof(MyList<>));
IContainer container = builder.Build();
var myIntList = container.Resolve<MyList<int>>();
Assert.NotNull(myIntList);
var myStringList = container.Resolve<MyList<string>>();
Assert.NotNull(myStringList);
}
對於同一個接口,後面註冊的實現會覆蓋之前的實現
[Fact]
public void register_order()
{
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterType<DbRepository>().As<IRepository>();
containerBuilder.RegisterType<TestRepository>().As<IRepository>();
IContainer container = containerBuilder.Build();
var repository = container.Resolve<IRepository>();
Assert.Equal(typeof(TestRepository), repository.GetType());
}
如果不想覆蓋的話,可以用PreserveExistingDefaults,這樣會保留原來註冊的實現。
[Fact]
public void register_order_defaults()
{
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterType<DbRepository>().As<IRepository>();
containerBuilder.RegisterType<TestRepository>().As<IRepository>().PreserveExistingDefaults();
IContainer container = containerBuilder.Build();
var repository = container.Resolve<IRepository>();
Assert.Equal(typeof(DbRepository), repository.GetType());
}
可以用Name來區分不同的實現,代替As方法
[Fact]
public void register_with_name()
{
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterType<DbRepository>().Named<IRepository>("DB");
containerBuilder.RegisterType<TestRepository>().Named<IRepository>("Test");
IContainer container = containerBuilder.Build();
var dbRepository = container.ResolveNamed<IRepository>("DB");
var testRepository = container.ResolveNamed<IRepository>("Test");
Assert.Equal(typeof(DbRepository), dbRepository.GetType());
Assert.Equal(typeof(TestRepository), testRepository.GetType());
}
如果一個類有多個構造函數的話,可以在註冊時候選擇不同的構造函數
[Fact]
public void choose_constructors()
{
var builder = new ContainerBuilder();
builder.RegisterType<MyParameter>();
builder.RegisterType<MyClass>().UsingConstructor(typeof(MyParameter));
IContainer container = builder.Build();
var myClass = container.Resolve<MyClass>();
Assert.NotNull(myClass);
}
AutoFac可以註冊一個Assemble下所有的類,當然,也可以根據類型進行篩選
[Fact]
public void register_assembly()
{
var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).
Where(t => t.Name.EndsWith("Repository")).
AsImplementedInterfaces();
IContainer container = builder.Build();
var repository = container.Resolve<IRepository>();
Assert.NotNull(repository);
}
二、事件
AutoFac支持三種事件:OnActivating,OnActivated,OnRelease。OnActivating在註冊組件使用之前會被調用,此時可以替換實現類或者進行一些其他的初始化工作,OnActivated在實例化之後會被調用,OnRelease在組件釋放之後會被調用。
public class MyEvent : IDisposable
{
public MyEvent(string input)
{
Console.WriteLine(input);
}
public MyEvent()
{
Console.WriteLine("Init");
}
public void Dispose()
{
Console.WriteLine("Dispose");
}
}
public void test_event()
{
var builder = new ContainerBuilder();
builder.RegisterType<MyEvent>().
OnActivating(e => e.ReplaceInstance(new MyEvent("input"))).
OnActivated(e => Console.WriteLine("OnActivated")).
OnRelease(e => Console.WriteLine("OnRelease"));
using (IContainer container = builder.Build())
{
using (var myEvent = container.Resolve<MyEvent>())
{
}
}
}
此時的輸出爲:
Init
input
OnActivated
Dispose
OnRelease
利用事件可以在構造對象之後調用對象的方法:
[Fact]
public void call_method_when_init()
{
var builder = new ContainerBuilder();
builder.RegisterType<MyClassWithMethod>().OnActivating(e => e.Instance.Add(5));
IContainer container = builder.Build();
Assert.Equal(5, container.Resolve<MyClassWithMethod>().Index);
}
public class MyClassWithMethod
{
public int Index { get; set; }
public void Add(int value)
{
Index = Index + value;
}
}
三、循環依賴
循環依賴是個比較頭疼的問題,在AutoFac中很多循環依賴的場景不被支持:
public class ClassA
{
private readonly ClassB b;
public ClassA(ClassB b)
{
this.b = b;
}
}
public class ClassB
{
public ClassA A { get; set; }
}
[Fact]
public void circular_dependencies_exception()
{
var builder = new ContainerBuilder();
builder.Register(c => new ClassB() { A = c.Resolve<ClassA>() });
builder.Register(c => new ClassA(c.Resolve<ClassB>()));
IContainer container = builder.Build();
Assert.Throws(typeof(DependencyResolutionException), () => container.Resolve<ClassA>());
}
可以部分的解決這種循環依賴的問題,前提是ClassA和ClassB的生命週期不能都是InstancePerDependency
[Fact]
public void circular_dependencies_ok()
{
var builder = new ContainerBuilder();
builder.RegisterType<ClassB>().
PropertiesAutowired(PropertyWiringFlags.AllowCircularDependencies).SingleInstance();
builder.Register(c => new ClassA(c.Resolve<ClassB>()));
IContainer container = builder.Build();
Assert.NotNull(container.Resolve<ClassA>());
Assert.NotNull(container.Resolve<ClassB>());
Assert.NotNull(container.Resolve<ClassB>().A);
}
四、生命週期
AutoFac中的生命週期概念非常重要,AutoFac也提供了強大的生命週期管理的能力。
AutoFac定義了三種生命週期:
Per Dependency
Single Instance
Per Lifetime Scope
Per Dependency爲默認的生命週期,也被稱爲’transient’或’factory’,其實就是每次請求都創建一個新的對象
[Fact]
public void per_dependency()
{
var builder = new ContainerBuilder();
builder.RegisterType<MyClass>().InstancePerDependency();
IContainer container = builder.Build();
var myClass1 = container.Resolve<MyClass>();
var myClass2 = container.Resolve<MyClass>();
Assert.NotEqual(myClass1, myClass2);
}
Single Instance也很好理解,就是每次都用同一個對象
[Fact]
public void single_instance()
{
var builder = new ContainerBuilder();
builder.RegisterType<MyClass>().SingleInstance();
IContainer container = builder.Build();
var myClass1 = container.Resolve<MyClass>();
var myClass2 = container.Resolve<MyClass>();
Assert.Equal(myClass1, myClass2);
}
Per Lifetime Scope,同一個Lifetime生成的對象是同一個實例
[Fact]
public void per_lifetime_scope()
{
var builder = new ContainerBuilder();
builder.RegisterType<MyClass>().InstancePerLifetimeScope();
IContainer container = builder.Build();
var myClass1 = container.Resolve<MyClass>();
var myClass2 = container.Resolve<MyClass>();
ILifetimeScope inner = container.BeginLifetimeScope();
var myClass3 = inner.Resolve<MyClass>();
var myClass4 = inner.Resolve<MyClass>();
Assert.Equal(myClass1, myClass2);
Assert.NotEqual(myClass2, myClass3);
Assert.Equal(myClass3, myClass4);
}
[Fact]
public void life_time_and_dispose()
{
var builder = new ContainerBuilder();
builder.RegisterType<Disposable>();
using (IContainer container = builder.Build())
{
var outInstance = container.Resolve<Disposable>(new NamedParameter("name", "out"));
using (var inner = container.BeginLifetimeScope())
{
var inInstance = container.Resolve<Disposable>(new NamedParameter("name", "in"));
}//inInstance dispose here
}//out dispose here
}