組件,是數據與方法的封裝。也就是對象,所以除去標題外,其他地方用對象代替組件。
使用AutoFac,分爲兩步,註冊對象與創建對象,即放進來與拿出去。
註冊對象,分爲三步
- 創建容器
var builder = new ContainerBuilder();
- 註冊對象
builder.xxxxxx
這一節,探究AutoFac提供註冊對象的各種方式- 容器閉合
var container = builder.Build();
構造注入
單獨註冊
按類型註冊
公開註冊類型
//按類型註冊,公開該類型
builder.RegisterType<Person>();
//使用類型解析
var person = container.Resolve<Person>();
公開接口
//按類型註冊,使用As公開接口
builder.RegisterType<Person>().As<IPerson>();
//使用接口解析
var person = container.Resolve<IPerson>();
公開註冊類型與接口
//按類型註冊,使用AsSelf公開類型,用As公開接口
builder.RegisterType<Person>().AsSelf().As<IPerson>();
//可以使用類型/接口解析
var person1 = container.Resolve<Person>();
var person2 = container.Resolve<IPerson>();
公開多個接口
//按類型註冊,用As公開接口
builder.RegisterType<Person>().As<IPerson>().As<IPeople>();
//可以使用多種接口解析
var person = container.Resolve<IPerson>();
var people= container.Resolve<IPeople>();
按實例註冊
註冊普通對象
builder.RegisterInstance(new Student());
var student = new Student();
builder.RegisterInstance(student);
builder.Register(c => new Student());
按實例註冊後,實例的生命週期由AutoFac處理,如果想要自己來處理
builder.RegisterInstance(new Student()).ExternallyOwned();
註冊單例對象,同樣,單例的對象也是實例對象的一種
builder.RegisterInstance(Worker.CreateInstance());
builder.RegisterInstance(Worker.CreateInstance()).ExternallyOwned();
批量註冊
按程序集註冊
當前程序集中,非靜態的公開類型被批量註冊
var dataAccess = Assembly.GetExecutingAssembly();
builder.RegisterAssemblyTypes(dataAccess);
var worker= container.Resolve<Worker>();
var manager = container.Resolve<Manager>();
引用的相關程序集,也可以批量註冊非靜態的公開類型
var assembly = typeof(User).Assembly;
builder.RegisterAssemblyTypes(assembly);
//User與Fire在引用的其他工程中
var user = container.Resolve<User>();
var fire = container.Resolve<Fire>();
批量註冊並批量公開相應的接口
var assembly = typeof(User).Assembly;
//AsImplementedInterfaces很方便的將註冊類型公開爲各自繼承的接口
builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces();
//User繼承IEntities,Fire繼承IModel
var user = container.Resolve<IEntities>();
var fire = container.Resolve<IModel>();
根據程序集的屬性,使用Where過濾要註冊的類型
var dataAccess = Assembly.GetExecutingAssembly();
builder.RegisterAssemblyTypes(dataAccess).Where(t => t.Name.EndsWith("er"));
//student創建失敗,因爲對象的類型名稱不少以er結尾
var worker= container.Resolve<Worker>();
var manager = container.Resolve<Manager>();
var student = container.Resolve<Student>();
根據程序集的屬性,使用Except過濾要註冊的類型
var dataAccess = Assembly.GetExecutingAssembly();
builder.RegisterAssemblyTypes(dataAccess).Except<Worker>();
//worker創建失敗,因爲沒有註冊
var manager = container.Resolve<Manager>();
var student = container.Resolve<Student>();
var worker= container.Resolve<Worker>();
按模塊註冊
var builder = new ContainerBuilder();
builder.RegisterModule<UserModule>();
//1.調用UserModule類型中的Load方法
var container = builder.Build();
namespace Entities
{
//Module是AutoFac提供的實現IModule的類型
public class UserModule : Module
{
protected override void Load(ContainerBuilder builder)
{
//2.註冊User等類型
builder.RegisterType<User>();
builder.RegisterType<Student>();
}
}
}
//3.創建User對象
User user = container.Resolve<User>();
程序集中的模塊
//獲取UserModule所在程序集
var assembly = typeof(UserModule).Assembly;
var builder = new ContainerBuilder();
builder.RegisterAssemblyModules<UserModule>(assembly);
//調用UserModule所在程序集中所有實現IModule的類型的Load
var container = builder.Build();
默認註冊
註冊順序
多個組件公開相同的服務,AutoFac將使用最後一個註冊的組件
builder.RegisterType<Person>().AS<IPerson>();
builder.RegisterType<Student>().As<IPerson>();
//解析IPerson得到的是Student對象
使用PreserveExistingDefaults選擇默認註冊
builder.RegisterType<Person>().AS<IPerson>().PreserveExistingDefaults();
builder.RegisterType<Student>().As<IPerson>();
//解析IPerson得到的是Person對象
解析構造
容器在解析時,默認尋找到參數最多的構造函數
//Person類型重載3個構造
public Person(){}
public Person(Book book){}
public Person(Book book, Bag bag){}
//創建Person對象時,執行的構造根據註冊情況而定
var person = container.Resolve<Person>();
情景1
builder.RegisterType<Bag>();
builder.RegisterType<Book>();
builder.RegisterType<Person>();
public Person(Book book, Bag bag){}
情景2
builder.RegisterType<Book>();
builder.RegisterType<Person>();
public Person(Book book){}
情景3
設置執行僅有Book類型參數的構造
builder.RegisterType<Person>().UsingConstructor(typeof(Book));
設置執行沒有參數的構造
builder.RegisterType<Person>().UsingConstructor();
構造參數
internal class Worker : IPerson
{
public Worker(string name)
{
Console.WriteLine(name);
}
}
在註冊時確定參數
var builder = new ContainerBuilder();
// 方式1 按名稱匹配
builder.RegisterType<Worker>().As<IPerson>().WithParameter("name", "eden");
//方式2 按類型匹配
builder.RegisterType<Worker>().As<IPerson>().WithParameter(new TypedParameter(typeof(string), "eden"));
//方式3 Lambda
builder.RegisterType<Worker>().As<IPerson>().WithParameter(
new ResolvedParameter((pi, ctx) => pi.ParameterType == typeof(string) && pi.Name == "name", (pi, ctx) => "pp"));
var container = builder.Build();
在解析時確定參數
var builder = new ContainerBuilder();
builder.Register((c, p) =>new Worker(p.Named<string>("name"))).As<IPerson>();
var container = builder.Build();
var obj = container.Resolve<IPerson>(new NamedParameter("name", "pp"));
初始化屬性
用Lambda可以初始化屬性,c在這裏代表註冊的上下文IComponentContext
builder.Register(c=> new Student(){ Name = "eden", BookObj = new Book()});
c.ResolveOptional<Book>()意味着從註冊的中尋找Book對象反射創建來填充BookObj屬性
builder.RegisterType<Book>();
builder.Register(c=> new Student(){ Name = "eden", BookObj = c.ResolveOptional<Book>()});
選擇註冊
參數化
var builder = new ContainerBuilder();
builder.Register<IPerson>(
(c, p) =>
{
var id = p.Named<int>("id");
if (1 == id)
{
return new Student();
}
else //細節
{
return new Worker();
}
});
var container = builder.Build();
var obj = container.Resolve<IPerson>(new NamedParameter("id", 1));
在解析時,傳入一個臨時的參數key-value(id = 1),容器根據參數進行邏輯處理,決定選擇哪個對象實例化創建
細節:這裏必須return一個對象,由else改爲else if無法通過編譯
條件化
如果沒有註冊A才註冊B
var builder = new ContainerBuilder();
//②在這裏註冊了Worker類型並暴露爲IPerson類型
builder.RegisterType<Worker>().As<IPerson>();
//③如果沒有暴露的IPerson類型,註冊Student類型
builder.RegisterType<Student>().As<IPerson>().IfNotRegistered(typeof(IPerson));
var container = builder.Build();
//解析爲Worker類型
var obj = container.Resolve<IPerson>();
var builder = new ContainerBuilder();
//②如果沒有暴露的IPerson類型,註冊Student類型
builder.RegisterType<Student>().As<IPerson>().IfNotRegistered(typeof(IPerson));
var container = builder.Build();
//解析爲Student類型
var obj = container.Resolve<IPerson>();
如果註冊了A才註冊B
var builder = new ContainerBuilder();
//② 註冊了Worker類型,並暴露爲IPerson類型
builder.RegisterType<Worker>().As<IPerson>();
//③ 如果有暴露的IPerson類型,才註冊Manager類型,並暴露爲IManager類型
builder.RegisterType<Manager>().As<IManager>().OnlyIf(reg=>reg.IsRegistered(new TypedService(typeof(IPerson))));
var container = builder.Build();
var obj= container.Resolve<IManager>();
var builder = new ContainerBuilder();
//② 如果有暴露的IPerson類型,才註冊Manager類型,並暴露爲IManager類型
builder.RegisterType<Manager>().As<IManager>().OnlyIf(reg=>reg.IsRegistered(new TypedService(typeof(IPerson))));
var container = builder.Build();
//編譯通過,運行出錯
var obj= container.Resolve<IManager>();
泛型註冊
internal interface IRepository<T>
{
void Add(T item);
}
internal class EntityFrameworkRepository<T> : IRepository<T>
{
public void Add(T item)
{
Console.WriteLine(item);
}
}
var builder = new ContainerBuilder();
builder.RegisterGeneric(typeof(EntityFrameworkRepository<>)).As(typeof(IRepository<>));
var container = builder.Build();
var workerRepository = container.Resolve<IRepository<Worker>>();
workerRepository.Add(new Worker());
var studentRepository= container.Resolve<IRepository<Student>>();
studentRepository.Add(new Student());
屬性注入
internal class Worker : People, IPerson
{
public Student Student { get; set; }
public string Name { get; set; }
public Worker(string name)
{
Console.WriteLine(name);
}
}
Lambda + 對象初始化器
var builder = new ContainerBuilder();
builder.Register(c=> new Worker {Name = "eden"});
var container = builder.Build();
var obj = container.Resolve<Worker>();
名稱匹配
var builder = new ContainerBuilder();
builder.RegisterType<Worker>().WithProperty("Name", "eden");
var container = builder.Build();
var obj = container.Resolve<Worker>();
反射組件
var builder = new ContainerBuilder();
//student使用已註冊
builder.RegisterType<Student>();
//PropertiesAutowired將在解析創建Worker對象時自動創建屬性的Student對象
builder.RegisterType<Worker>().PropertiesAutowired();
var container = builder.Build();
//obj.Student != null
var obj = container.Resolve<Worker>();
注入循環依賴
public class ClassA
{
private readonly ClassB _b;
public ClassA(ClassB b)
{
_b = b;
}
}
public class ClassB
{
public ClassA ClassA { get; set; }
}
ClassA與ClassB已經造成循環依賴
var builder = new ContainerBuilder();
builder.Register(c => new ClassA(new ClassB()));
builder.Register(c => new ClassB()).OnActivated(e => e.Instance.ClassA = e.Context.Resolve<ClassA>());
var container = builder.Build();
var obj = container.Resolve<ClassB>();
方法注入
internal class Worker : People, IPerson
{
private Student _student;
private string _name;
public void SetName(string name)
{
_name = name;
}
public void SetStudent(Student student)
{
_student = student;
}
}
方式1
var name = "eden";
var builder = new ContainerBuilder();
builder.Register(c =>
{
var worker = new Worker();
worker.SetName(name);
return worker;
});
var container = builder.Build();
var obj = container.Resolve<Worker>();
方式2
var name = "eden";
var builder = new ContainerBuilder();
builder.RegisterType<Student>();
builder.RegisterType<Worker>()
.OnActivating(e =>
{
var stu = e.Context.Resolve<Student>();
e.Instance.SetStudent(stu);
});
var container = builder.Build();
var obj = container.Resolve<Worker>();
後記
花費了將近2個星期內的空餘時間,看官網中組件註冊這一章,按照自己的邏輯歸納出這篇博客。使用程序集註冊+自動公開接口的方式,將Services模塊註冊,一行代碼幹掉了140行。