Autofac tutorial 1

Autofac,它的本質是根據你註冊的類型,自動爲你創建相應的對象。

使用方法

1).在程序啓動時,創建ContainerBuilder,
2).用創建的builder註冊組件(component)
3).調用builder的Build方法,保存container後續用於resolve type

IContainer Container = builder.Build();

4).使用container resolve type

// Create the scope, resolve your IDateWriter,
// use it, then dispose of the scope.
using (var scope = Container.BeginLifetimeScope())
{
    var writer = scope.Resolve<IDateWriter>();
    writer.WriteDate();
}

註冊類型

見下面的Demo2/3/4.
Autofac通過ContainerBuilder註冊component,告訴builder各個component暴露的service. 註冊類型分3種:

// Create the builder with which components/services are registered.
var builder = new ContainerBuilder();

//1.註冊基於反射的component
// Register types that expose interfaces...
builder.RegisterType<ConsoleLogger>().As<ILogger>();

//2.註冊實例
// Register instances of objects you create...
var output = new StringWriter();
builder.RegisterInstance(output).As<TextWriter>();

//3.Lambda Expression
// Register expressions that execute to create objects...
builder.Register(c => new ConfigReader("mysection")).As<IConfigReader>();

//一個組件(component)可以暴露多個服務(service)
//builder.RegisterType<CallLogger>()
//       .As<ILogger>()
//       .As<ICallInterceptor>();

// Build the container to finalize registrations
// and prepare for object resolution.
var container = builder.Build();

// Now you can resolve services using Autofac. For example,
using (var scope = container.BeginLifetimeScope())
{//this line will execute the lambda expression registered to the IConfigReader service.
    var reader = container.Resolve<IConfigReader>();
    reader.Read("file");
}
using (var scope = container.BeginLifetimeScope())
{
    var logger = container.Resolve<ILogger>();
    logger.Write("log");
}
using (var scope = container.BeginLifetimeScope())
{
    var stringwriter = container.Resolve<TextWriter>();
    stringwriter.Write(100 + 100);
    var str = stringwriter.ToString();
}

Demo1-基本用法

// This interface helps decouple the concept of
// "writing output" from the Console class. We
// don't really "care" how the Write operation
// happens, just that we can write.
public interface IOutput
{
    void Write(string content);
}

// This interface decouples the notion of writing
// a date from the actual mechanism that performs
// the writing. Like with IOutput, the process
// is abstracted behind an interface.
public interface IDateWriter
{
    void WriteDate();
}
// This implementation of the IOutput interface
// is actually how we write to the Console. Technically
// we could also implement IOutput to write to Debug
// or Trace... or anywhere else.
public class ConsoleOutput : IOutput
{
    public ConsoleOutput()
    {
        Console.WriteLine("ConsoleOutput");
    }
    public void Write(string content)
    {
        Console.WriteLine(content);
    }
}

// This TodayWriter is where it all comes together.
// Notice it takes a constructor parameter of type
// IOutput - that lets the writer write to anywhere
// based on the implementation. Further, it implements
// WriteDate such that today's date is written out;
// you could have one that writes in a different format
// or a different date.
public class TodayWriter : IDateWriter
{
    private IOutput _output;
    public TodayWriter(IOutput output)
    {
        Console.WriteLine("TodayWriter");
        this._output = output;
    }

    public void WriteDate()
    {
        this._output.Write(DateTime.Today.ToShortDateString());
    }
}
public partial class MainWindow : Window
{
    private static IContainer Container { get; set; }

    //For our sample app, we need to register all of our components (classes) and 
    //expose their services (interfaces) so things can get wired up nicely.
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        //1.通常在程序啓動時,創建ContainerBuilder
        var builder = new ContainerBuilder();
        //2.使用builder註冊
        // Usually you're only interested in exposing the type via its interface:
        builder.RegisterType<ConsoleOutput>().As<IOutput>();
        builder.RegisterType<TodayWriter>().As<IDateWriter>();
        // However, if you want BOTH services, you can say so:
        //builder.RegisterType<TodayWriter>().AsSelf().As<IDateWriter>();
        //3.保存container後續用於resolve type
        Container = builder.Build();

        //4.使用container resolve type
        WriteDate();
    }
    //**** Demo1流程說明 ****
    //1)The “WriteDate” method asks Autofac for an IDateWriter.
    //2)Autofac sees that IDateWriter maps to TodayWriter so starts creating a TodayWriter.
    //下面3)到5)對剛接觸的人不太好理解,但對照Autofac的本質就很容易理解了。
    //3)Autofac sees that the TodayWriter needs an IOutput in its constructor.
    //4)Autofac sees that IOutput maps to ConsoleOutput so creates a new ConsoleOutput instance.
    //5)Autofac uses the new ConsoleOutput instance to finish constructing the TodayWriter.
    //6)Autofac returns the fully - constructed TodayWriter for “WriteDate” to consume.
    public static void WriteDate()
    {        
        // Create the scope, resolve your IDateWriter,
        // use it, then dispose of the scope.
        using (var scope = Container.BeginLifetimeScope())
        {
            var writer = scope.Resolve<IDateWriter>();
            writer.WriteDate();
        }
    }
}

Demo2 - 基於反射的組件Reflection Components

基於反射的組件(refection-based component)通常通過type註冊. Autofac自動使用匹配最大的構造函數創建對象。示例如下:

抽象 - 接口部分(service)

public interface ILogger
{
    void Write(string log);
}
public interface IConfigReader
{
    void Read(string file);
}

具體 - component

public class ConsoleLogger : ILogger
{
    public void Write(string log)
    {
        Console.WriteLine(log);
    }
}
public class ConfigReader : IConfigReader
{
    private string _section;
    public ConfigReader(string section)
    {
        _section = section;
    }
    public void Read(string file)
    {
        Console.WriteLine(file + ": " + _section);
    }
}
public class MyComponent
{
    private ILogger _logger;
    private IConfigReader _configReader;
    public MyComponent()
    {
        Console.WriteLine("MyComponent()");
    }
    public MyComponent(ILogger logger)
    {
        _logger = logger;
        Console.WriteLine("MyComponent(ILogger logger)");
    }
    public MyComponent(ILogger logger, IConfigReader reader)
    {
        _logger = logger;
        _configReader = reader;
        Console.WriteLine("MyComponent(ILogger logger, IConfigReader reader)");
    }
}

使用,Autofac自動使用匹配參數最多的構造函數創建對象

//註冊
var builder = new ContainerBuilder();
builder.RegisterType<ConsoleLogger>().As<ILogger>();
builder.RegisterType<MyComponent>();
var container = builder.Build();
//使用
using (var scope = container.BeginLifetimeScope())
{//自動選用帶ILogger的構造函數
    var component = container.Resolve<MyComponent>();
}

指定使用的構造函數(Specifying a Constructor)

//註冊
var builder = new ContainerBuilder();
//指定用帶ILogger & IConfigReader的構造函數
builder.RegisterType<MyComponent>()
    .UsingConstructor(typeof(ILogger), typeof(IConfigReader));
//但是ILogger & IConfigReader還是要註冊的,否則error
builder.RegisterType<ConsoleLogger>().As<ILogger>();
// Using a NAMED parameter:
builder.RegisterType<ConfigReader>()
        .As<IConfigReader>()
        .WithParameter("section", "sectionName");
// Using a TYPED parameter:
//builder.RegisterType<ConfigReader>()
//       .As<IConfigReader>()
//       .WithParameter(new TypedParameter(typeof(string), "sectionName"));
var container = builder.Build();
//使用
using (var scope = container.BeginLifetimeScope())
{                
    var component = container.Resolve<MyComponent>();
}

Demo3 - Register Instance Components

註冊實例通過ContainerBuilder.RegisterInstance實現,而且可以通過使用ExternallyOwned()指定是否由autofac自動是否實例。

//Create the builder with which components/services are registered.
var builder = new ContainerBuilder();
//Register instances of objects you create...
//Autofac automatically handles disposal of registered components and 
//you may want to control the lifetime yourself rather than having Autofac 
//call Dispose on your object for you.這時需要使用ExternallyOwned()
var output = new StringWriter();
//1)autofac自動釋放
// builder.RegisterInstance(output).As<TextWriter>();
//2)外部自己釋放
builder.RegisterInstance(output).As<TextWriter>().ExternallyOwned();
var container = builder.Build();

using (var scope = container.BeginLifetimeScope())
{
    var stringwriter = container.Resolve<TextWriter>();
    stringwriter.Write(100 + 100);
    var str = stringwriter.ToString();
}

特別的當實例是單件(singleton )的情況時,使用這種方法可以從程序中移除單件實例而使用container中註冊的實例對象。

builder.RegisterInstance(MySingleton.Instance).ExternallyOwned();

Demo4 - Lambda Expression Components

基於反射的創建方法是一個很好選擇。但是,當組件創建邏輯超出了一個簡單的構造函數調用時(有多個構造函數時autofac會自動選一個),事情就變得不那麼一目瞭然了。這時我們可以使用lambda表達式。

//The parameter c provided to the expression is the component context
//(an IComponentContext object) in which the component is being created. 
//You can use this to resolve other values from the container to assist in creating your component.
//c是component context(IComponentContext object),component就是在其內部創建的。
//我們可以用它來得到container裏面註冊的類型來輔助創建我們的組件。
var builder = new ContainerBuilder();
builder.RegisterType<ConsoleLogger>().As<ILogger>();
builder.RegisterType<ConfigReader>()
        .As<IConfigReader>()
        .WithParameter("section", "sectionName");
//lambda expression,直觀的知道使用哪個構造函數創建的
builder.Register(c => new MyComponent(c.Resolve<ILogger>(), c.Resolve<IConfigReader>()));
var container = builder.Build();

using (var scope = container.BeginLifetimeScope())
{
    var component = container.Resolve<MyComponent>();
}

示例-Complex Parameters(會話過期)

builder.Register(c => new UserSession(DateTime.Now.AddMinutes(25)));

示例-Property Injection(屬性注入)
這是不被推薦的用法。使用ResolveOptional 實現。The ResolveOptional method will try to resolve the value but won’t throw an exception if the service isn’t registered. (You will still get an exception if the service is registered but can’t properly be resolved.) This is one of the options for resolving a service.

builder.Register(c => new A(){ MyB = c.ResolveOptional<B>() });

示例-Selection of an Implementation by Parameter Value
隔離組件創建(isolating component creation)的最大好處之一是可以改變具體類型。這通常是在運行時完成的,而不僅僅是配置時,如下示例:

//In this example, CreditCard is implemented by two classes, 
//GoldCard and StandardCard - which class is instantiated 
//depends on the account ID provided at runtime.
//參數c同上,參數p是可選參數
//註冊
builder.Register<CreditCard>(
  (c, p) =>
  {
      var accountId = p.Named<string>("accountId");
      if (accountId.StartsWith("9"))
      {
          return new GoldCard(accountId);
      }
      else
      {
          return new StandardCard(accountId);
      }
  });
//使用
var card = container.Resolve<CreditCard>(new NamedParameter("accountId", "12345"));

Open Generic Components

open generic types也是支持的. 使用RegisterGeneric() 註冊

builder.RegisterGeneric(typeof(NHibernateRepository<>))
       .As(typeof(IRepository<>))
       .InstancePerLifetimeScope();
//Autofac will return an NHibernateRepository<Task>
var tasks = container.Resolve<IRepository<Task>>();

其它

1)大多數情況自暴露自己

// This exposes the service "CallLogger"
builder.RegisterType<CallLogger>();

// This will work because the component
// exposes the type by default:
scope.Resolve<CallLogger>();

// This will NOT work because we didn't
// tell the registration to also expose
// the ILogger interface on CallLogger:
scope.Resolve<ILogger>();

2).可以同時暴露組件的多個服務

builder.RegisterType<CallLogger>()
       .As<ILogger>()
       .As<ICallInterceptor>();

// These will both work because we exposed
// the appropriate services in the registration:
scope.Resolve<ILogger>();
scope.Resolve<ICallInterceptor>();

// This WON'T WORK anymore because we specified
// service overrides on the component:
scope.Resolve<CallLogger>();

3.) AsSelf
If you want to expose a component as a set of services as well as using the default service, use the AsSelf method

builder.RegisterType<CallLogger>()
       .AsSelf()
       .As<ILogger>()
       .As<ICallInterceptor>();

// These will all work because we exposed
// the appropriate services in the registration:
scope.Resolve<ILogger>();
scope.Resolve<ICallInterceptor>();
scope.Resolve<CallLogger>();

4).多個組件暴露相同的服務
If more than one component exposes the same service, Autofac will use the last registered component as the default provider of that service:

//In this scenario, FileLogger will be the default for ILogger
//because it was the last one registered.
builder.Register<ConsoleLogger>().As<ILogger>();
builder.Register<FileLogger>().As<ILogger>();
//To override this behavior, use the PreserveExistingDefaults() modifier
//In this scenario, ConsoleLogger will be the default for ILogger
//because the later registration for FileLogger used PreserveExistingDefaults()
builder.Register<ConsoleLogger>().As<ILogger>();
builder.Register<FileLogger>().As<ILogger>().PreserveExistingDefaults();
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章