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();

猜你喜欢

转载自blog.csdn.net/hetoby/article/details/77839441
今日推荐