第四節 註冊組件

組件,是數據與方法的封裝。也就是對象,所以除去標題外,其他地方用對象代替組件。

使用AutoFac,分爲兩步,註冊對象與創建對象,即放進來與拿出去。

註冊對象,分爲三步

  1. 創建容器
    var builder = new ContainerBuilder();
  2. 註冊對象
    builder.xxxxxx這一節,探究AutoFac提供註冊對象的各種方式
  3. 容器閉合
    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行。
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章