最近在研究Nopcommerce,发现其内部IOC框架使用了autofac,简单的了解了一下,记录一下简单的一些用法。初浅了解下来,有一个很直观的感受,就是对代码几乎没什么侵入性,其他性能之类没有测试,但据说性能极佳。
1. 构造函数注入,如下示例(后面所有说明都是在以下的示例基础上进行的)
public interface IRepository
{
void Update();
}
public class FirstRepository : IRepository
{
public void Update()
{
Console.WriteLine("FirstRepository.Update");
}
}
public class SecondRepository : IRepository
{
public void Update()
{
Console.WriteLine("SecondRepository.Update");
}
}
public class ConcreteController
{
private IRepository _repository;
public ConcreteController(IRepository repository)
{
_repository = repository;
}
public void Update()
{
_repository.Update();
}
}
调用如下:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<ConcreteController>().AsSelf();//也可直接写成builder.RegisterType<ConcreteController>()
builder.RegisterType<FirstRepository>().As<IRepository>();//注册FirstRepository为IRespository默认实现
IContainer container = builder.Build();
//默认以类参数最多的构造方法进行构造,构造参数根据类型从注册列表中寻找注册的实现类,此处获取的是FirstRepository
ConcreteController controller = container.Resolve<ConcreteController>();
controller.Update();//FirstRepository.Update
对同一接口多次注册时,会以最后一次注册的实现类为默认实现类,如下:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<ConcreteController>().AsSelf();//也可直接写成builder.RegisterType<ConcreteController>()
builder.RegisterType<FirstRepository>().As<IRepository>();
builder.RegisterType<SecondRepository>().As<IRepository>();//IRepository的默认实现类为SecondRepository
IContainer container = builder.Build();
ConcreteController controller = container.Resolve<ConcreteController>();
controller.Update();//SecondRepository.Update
如果有这样的一个需求:如果前面注册过了接口的实现类,则以之前注册过的实现类为默认值,如果没有注册过则以此次注册的实现类为默认值,可以通过以下方式实现:
//如果之前存在默认值则以之前注册的值为默认值,否则以此次注册的实现类为默认值
builder.RegisterType<SecondRepository>().As<IRepository>().PreserveExistingDefaults();
...
controller.Update();//FirstRepository.Update
在注册时,可以通过委托的方式,预先指定一个接口的实现类。即采用ContainerBuilder.Register<T>(委托)的方式来进行实现,如下:
ContainerBuilder builder = new ContainerBuilder();
//通过Register方法,预先指定一个获取实现的委托来注册接口的实现类
builder.Register<IRepository>(r => new FirstRepository());
IContainer container = builder.Build();
IRepository repository = container.Resolve<IRepository>();//FirstRepository
repository.Update();//FirstRepository.Update
autofac构造注入时,在通过无参的IContainer.resolver获取实现类时,会默认通过实现类最多的参数进行构造,构造方法的参数值为该参数类型注册的实现类。如果该参数值的Type没有在autofac进行注册,则直接报错,如下:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<ConcreteController>();
IContainer container = builder.Build();
//ConcreteController的构造方法中需要一个IRepository参数,但autofac检索不到IRepository的实现类,注入失败,报错
//此处不光是针对参数值是引用类型,对于值类型同样存在这样的问题,例如把参数换成int i,同样注入失败,报错
ConcreteController controller = container.Resolve<ConcreteController>();//error
对于这样的问题,可以通过在Resolve 时,自行装配,如下:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<ConcreteController>();
IContainer container = builder.Build();
//之前没有对ConcreteController构造方法中的参数repository类型IRepository进行注册,但可在解析提取时自动注入
//通过NamedParameter,第一个参数指定参数名称,第二个参数为传递的参数值
ConcreteController controller = container.Resolve<ConcreteController>(new NamedParameter("repository", new FirstRepository()));
controller.Update();//FirstRepository.Update
除了在Resolver时指定参数值,也可以注册该类时默认指定构造方法的参数值。
ContainerBuilder builder = new ContainerBuilder();
//在注册时就先指定构造方法参数的默认值,即为SecondRepository
builder.RegisterType<ConcreteController>().WithParameter(new NamedParameter("repository", new SecondRepository()));
IContainer container = builder.Build();
ConcreteController controller = container.Resolve<ConcreteController>();
controller.Update();//SecondRepository.Update
如果在注册时指定了参数默认值,在Resolver时指定了参数值,则以Resolver指定的参数值为准,如下:
ContainerBuilder builder = new ContainerBuilder();
//在注册时就先指定构造方法参数的默认值,即为SecondRepository
builder.RegisterType<ConcreteController>().WithParameter(new NamedParameter("repository", new SecondRepository()));
IContainer container = builder.Build();
ConcreteController controller = container.Resolve<ConcreteController>(new NamedParameter("repository", new FirstRepository()));
controller.Update();//FirstRepository.Update
我们之前谈的一直是一个构造方法的情况,假如现在一个实现类有多个构造方法时,autofac会以构造方法参数最多的那个进行注入构造,如下,我们在ConcreteController中新增一个构造方法:
public ConcreteController(IRepository repository, string name)
{
Console.WriteLine("two args contructor was invoked");
_repository = repository;
}
ConcreteController controller = container.Resolve<ConcreteController>();//没有调用两个参数的构造方法
运行后,发现autofac并没有调用ConcreteController中的两个参数的构造方法进行构造,奇怪了?仔细一瞧,问题就出现在第二个参数string name,还记得之前我们说过,在构造注入时,会检索string在autofac注册的实现类,但此时显示是检索不到(且该参数没有设置默认值例如 string name=null),如果ConcreteController只存在这个构造方法,则会报错,但是还有另一个单个参数的构造方法,因此在试图通过最多参数方法注入不成功时,会转向另一个构造方法,依次下去,直至成功注入为止。我们再换一个方法,新增一个接口,一个类,同时修改ConcreteController第二个构造方法,如下:
public interface IAnotherRepository
{
}
public class AnotherRepository : IAnotherRepository
{
}
public class ConcreteController
{
...
public ConcreteController(IRepository repository, IAnotherRepository another)
{
Console.WriteLine("two args contructor was invoked");
_repository = repository;
}
...
}
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<ConcreteController>();
builder.RegisterType<SecondRepository>().As<IRepository>();
builder.RegisterType<AnotherRepository>().As<IAnotherRepository>();
IContainer container = builder.Build();
ConcreteController controller = container.Resolve<ConcreteController>();//此处调用了两个参数的构造方法
autofac默认调用实现类参数最多的构造方法进行构造,但如果实现类中有好几个参数个数相同的构造方法,autofac如何调用? 例如ConcreteController中有两个如下的构造方法:
public class ConcreteController
{
...
public ConcreteController(IRepository repository, string name = null)
{
}
public ConcreteController(IRepository repository, IAnotherRepository another)
{
Console.WriteLine("two args contructor was invoked");
_repository = repository;
}
...
}
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<ConcreteController>();
builder.RegisterType<SecondRepository>().As<IRepository>();
builder.RegisterType<AnotherRepository>().As<IAnotherRepository>();
IContainer container = builder.Build();
ConcreteController controller = container.Resolve<ConcreteController>();//error报错,autofac不知道调用哪个构造方法
这种情况如何解决?可以通过在注册实现类时,显示指定哪个构造方法(当存在不同参数的构造方法时,亦可通过此方法来指定不同参数的构造方法作为默认值),如下:
ContainerBuilder builder = new ContainerBuilder();
//默认指定了public ConcreteController(IRepository repository, string name = null){} 为注入时调用的构造方法
builder.RegisterType<ConcreteController>().UsingConstructor(typeof(IRepository), typeof(string));
builder.RegisterType<SecondRepository>().As<IRepository>();
builder.RegisterType<AnotherRepository>().As<IAnotherRepository>();
IContainer container = builder.Build();
//调用public ConcreteController(IRepository repository, string name = null){}
ConcreteController controller = container.Resolve<ConcreteController>();
2. 一个接口存在多个实现类时
a、ContainerManager.Register<实现类>().Named<接口>(名称) / IContainer.ResolveNamed<接口>(名称)
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<FirstRepository>().Named<IRepository>("first");
builder.RegisterType<SecondRepository>().Named<IRepository>("second");
IContainer container = builder.Build();
IRepository first = container.ResolveNamed<IRepository>("first");//FirstRepository
IRepository second = container.ResolveNamed<IRepository>("second");//SecondRepository
当普通注册和Name方式共同存在时,如下:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<ThirdRepository>().As<IRepository>();
builder.RegisterType<FirstRepository>().Named<IRepository>("first");
builder.RegisterType<SecondRepository>().Named<IRepository>("second");
IContainer container = builder.Build();
IRepository first = container.ResolveNamed<IRepository>("first");//FirstRepository
IRepository second = container.ResolveNamed<IRepository>("second");//SecondRepository
//普通方式和Named方式并不矛盾
IRepository third = container.Resolve<IRepository>();//ThirdRepository
b、ContainerManager.Register<实现类>().Keyed<接口>(object) / IContainer.ResolveKeyed<接口>(object)
public enum RepositoryStyle : int
{
First,
Seond,
Third
}
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<ThirdRepository>().As<IRepository>();
builder.RegisterType<FirstRepository>().Keyed<IRepository>(RepositoryStyle.First);
builder.RegisterType<SecondRepository>().Keyed<IRepository>(RepositoryStyle.Seond);
IContainer container = builder.Build();
IRepository first = container.ResolveKeyed<IRepository>(RepositoryStyle.First);//FirstRepository
IRepository second = container.ResolveKeyed<IRepository>(RepositoryStyle.Seond);//SecondRepository
IRepository third = container.Resolve<IRepository>();//ThirdRepository
3. 属性注入
autofac不提倡属性注入,属性注入意味着该属性的set必须向外公开,同时就意味着属性可人为的在外部进行修改。这样就可能造成一些隐患(被外部非本意的更改),autofac提倡构造方法进行注入,注入后在外部都不可更改。意味着干净,无污染
把ConcreteController的类修改一下,如下:
public class ConcreteController
{
public IRepository Repository { get; set; }
public void Update()
{
Repository.Update();
}
}
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<ConcreteController>().AsSelf();
builder.RegisterType<FirstRepository>().As<IRepository>();
IContainer container = builder.Build();
//autofac默认不会进行属性注入
ConcreteController controller = container.Resolve<ConcreteController>();
controller.Update();//NullReferenceException,提示Repository为null
方法1:在注册实现类时,通过委托指定实现类的构造(构造过程中显示进行注入),如下:
ContainerBuilder builder = new ContainerBuilder();
//在注册ConcreteController时,通过委托创建实现类时显式的对属性进行注入
builder.Register<ConcreteController>(r => new ConcreteController() { Repository = r.Resolve<IRepository>() });
builder.RegisterType<FirstRepository>().As<IRepository>();
IContainer container = builder.Build();
ConcreteController controller = container.Resolve<ConcreteController>();
controller.Update();//FirstRepository.Update
方法2:使用PropertiesAutowired为所有属性自动注入
ContainerBuilder builder = new ContainerBuilder();
//自动注入所有属性
builder.RegisterType<ConcreteController>().PropertiesAutowired();
builder.RegisterType<FirstRepository>().As<IRepository>();
IContainer container = builder.Build();
ConcreteController controller = container.Resolve<ConcreteController>();
controller.Update();//FirstRepository.Update
方法3:手工为指定属性注入
ContainerBuilder builder = new ContainerBuilder();
//手工显示为指定属性名称的属性注入
builder.RegisterType<ConcreteController>().WithProperty("Repository", new FirstRepository());
builder.RegisterType<FirstRepository>().As<IRepository>();
IContainer container = builder.Build();
ConcreteController controller = container.Resolve<ConcreteController>();
controller.Update();//FirstRepository.Update
4. 非泛形的Register/RegisterType/Resolve方法,RegisterType<接口>() 相当于 RegisterType(typeof(接口))
ContainerBuilder builder = new ContainerBuilder();
//作用和builder.RegisterType<FirstRepository>().As<IRepository>();一样的
builder.RegisterType(typeof(FirstRepository)).As(typeof(IRepository));
IContainer container = builder.Build();
//作用和IRepository obj = container.Resolve<IRepository>();
IRepository obj = container.Resolve(typeof(IRepository)) as IRepository; //FirstRepository
5. 开放的泛型类型
public interface IRepository<T>
{
void Update(T t);
}
public class FirstRepository<T> : IRepository<T>
{
public void Update(T t)
{
Console.WriteLine("FirstRepository.Update " + t.ToString());
}
}
public class SeondRepository<T> : IRepository<T>
{
public void Update(T t)
{
Console.WriteLine("SeondRepository.Update " + t.ToString());
}
}
ContainerBuilder builder = new ContainerBuilder();
//注册泛形接口时,使用RgisterGeneric方法
builder.RegisterGeneric(typeof(FirstRepository<>)).As(typeof(IRepository<>));
IContainer container = builder.Build();
IRepository<string> repository = container.Resolve<IRepository<string>>();
repository.Update("hehe");
6. 自动装配
a、自动为某个接口注册实现类
ContainerBuilder builder = new ContainerBuilder();
//自动扫描当前程序集中实现了IRepository的实现类,将其中一个实现了IRepository的实现类进行装配
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).As<IRepository>();
IContainer container = builder.Build();
IRepository r = container.Resolve<IRepository>();//返回其中一个实现了IRepository的类实例
b、自动为所有接口注册实现类
ContainerBuilder builder = new ContainerBuilder();
//自动扫描当前程序集,并将所有实现了接口的类注册为该接口的实现类(选取其中的一个实现类)
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsImplementedInterfaces();
IContainer container = builder.Build();
IRepository obj1 = container.Resolve<IRepository>();//返回其中一个实现了IRepository的类实例,此处返回ThirdRepository
IAnotherRepository obj2 = container.Resolve<IAnotherRepository>();//返回其中一个实现了IAnotherRepository的类实例,此处返回AnotherRepositor
c、筛选注册
ContainerBuilder builder = new ContainerBuilder();
//自动扫描当前程序集,搜索所有实现了IRepository且类名包含"Second"的类,并将其中的一个注册为IRepository的实现类
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).Where(t => t.Name.Contains("Second")).As<IRepository>();
IContainer container = builder.Build();
IRepository obj1 = container.Resolve<IRepository>();//返回SecondRepository
IAnotherRepository obj2 = container.Resolve<IAnotherRepository>();//error,因为没有为IAnotherRepository注册实现类
7. 实例生命周期
a、ContainerBuilder.Register<实现类>().As<接口>().InstancePerDependency() 用于控制对象的生命周期,每次加载实例时都是新建一个实例,默认就是这种方式
ContainerBuilder builder = new ContainerBuilder();
//返回的都是新建的实例
builder.RegisterType<FirstRepository>().As<IRepository>().InstancePerDependency();
IContainer container = builder.Build();
IRepository obj1 = container.Resolve<IRepository>();
IRepository obj2 = container.Resolve<IRepository>();
Console.WriteLine(ReferenceEquals(obj1, obj2));//false
b、ContainerBuilder.Register<实现类>().As<接口>().InstancePerDependency()
ContainerBuilder builder = new ContainerBuilder();
//返回的都是同一个实例
builder.RegisterType<FirstRepository>().As<IRepository>().SingleInstance();
IContainer container = builder.Build();
IRepository obj1 = container.Resolve<IRepository>();
IRepository obj2 = container.Resolve<IRepository>();
Console.WriteLine(ReferenceEquals(obj1, obj2));//true
8. MVC中的应用
MVC用于DI的一个类是DependencyResolver,其中的Current和CurrentCache的静态属性是一个IDependencyResolver。通过该接口的两个方法 object GetService(Type serviceType); IEnumerable<object> GetServices(Type serviceType)来获取实现类。DependencyResolver默认的IDependencyResolver是DefaultDependcyResolver其内部简单的通过反射创建Type的实例。现在我们利用autofac来代替DefaultDependencyResolver。
首先先添加Autofac.Integration.Mvc程序集,其中定义了个实现了IDependencyResolver的类AutofacDependencyResolver,其内部就是使用autofac来实现控制反转的
/// <summary>
/// 仓储接口
/// </summary>
public interface IRepository
{
IEnumerable<object> Get(string id);
bool Update(object entity);
bool Save(object entity);
bool Delete(object entity);
}
/// <summary>
/// 仓储实现
/// </summary>
public class ConcreteRepository : IRepository
{
public IEnumerable<object> Get(string id)
{
return new List<object>();
}
public bool Update(object entity)
{
return true;
}
public bool Save(object entity)
{
return true;
}
public bool Delete(object entity)
{
return true;
}
}
/// <summary>
/// 控制器
/// </summary>
public class HomeController : Controller
{
IRepository repository;
public HomeController(IRepository repository)
{
this.repository = repository;
}
public ActionResult Index()
{
var list = repository.Get("");//repository为ConcreteRepository
return View();
}
}
/// <summary>
/// Global
/// </summary>
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
ContainerBuilder builder = new ContainerBuilder();
RegisterContainer(builder);
//Autofac.Integration.Mvc中定义的扩展方法,注册所有控制器本身
//类似一个个去注册Controller,例如 builder.RegisterType<HomeController>().AsSelf();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
IContainer container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
/// <summary>
/// 注册
/// </summary>
/// <param name="builder"></param>
private void RegisterContainer(ContainerBuilder builder)
{
//可以为所有接口注册实现类builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsImplementedInterfaces();
builder.RegisterType<ConcreteRepository>().As<IRepository>();
}
}