楔子:吃东西的故事
Autofac是一款IOC容器,适用于.NET开发的高效管理依赖关系的组件。
在面向对象的软件开发概念中,有个依赖倒置原则,包含如下的三层含义:
1)高层模块不应该依赖低层模块,两者都应该依赖其抽象
2)抽象不应该依赖细节
3)细节应该依赖抽象
这个可能理解起来可能不是很容易,打个比方说,你饿了要吃东西,去店里点餐,告诉店老板只要能填饱肚了的就可以,那可以吃米饭、面食、甜点、大饼、包子……。总之选择很多,适应性强。但如果你说只吃面,不好意思,我这里只卖包子。去下一家,不好意思,我们只做客家菜……。可能走了一条街都没吃上东西。
软件的开闭原则告诉我们“软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的”。如果一个模块一个对象可以预料是变化的,那就要能适应变化,所依赖的不能是具体的实现,否则需求变化了就不得不修改这个模块,违反了开闭原则,
如果按上面的需求写个代码应该写成:
namespace WebApp.Code
{
public interface IFood
{
String FoodName { get;}
}
public class People
{
IFood _something;
public People(IFood something)
{
this._something = something;
}
public void Eat()
{
Console.Write("吃" + _something.FoodName);
}
}
public class 包子 : IFood
{
public string FoodName => "包子";
}
public class 拉面 : IFood
{
public string FoodName => "拉面";
}
}
传统的实例化对象是new 一个对象
var p = new People(new 包子());
p.Eat();
也许有比包子更好吃的东西呢?何不学学梁山好汉“好酒好肉尽管上”!这就是注入的概念,
二、正文
Autofac官方文档:https://autofaccn.readthedocs.io/zh/latest/
其实文档已经很详细了,首先在项目中右键打开NuGet添加Autofac组件:
Autofac是主要的引用。
Autofac.Mvc5是对MVC的扩展引用。
Autofac的使用步骤包括三步:
1)注册
Autofac是一个IOC容器,既然是容器,就要往里面填东西。
//先定义这个容器
static IContainer Container { get; set; }
//然后通过注册往这个容器里填东西
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<ConsoleOutput>().As<IOutput>();
builder.RegisterType<TodayWrite>().As<IDateWriter>();
Container = builder.Build();
2)解析
using (var scope = Container.BeginLifetimeScope())
{
var writer = scope.Resolve<IDateWriter>();
//...
}
3) 使用
writer.WriteDate();
更多用法:
enum MyEnum
{
cn,
en
}
public interface ISpeak
{
void Say(string content);
}
public class ChineseSpeaker : ISpeak
{
string _param = "";
public ChineseSpeaker(string param)
{
this._param = param;
}
public void Say(string content)
{
Console.WriteLine(_param + "中国人说:" + content);
}
}
public class EnglishSpeaker : ISpeak
{
public void Say(string content)
{
Console.WriteLine("英国人说:" + content);
}
}
注册多个相同类型的对象,需要用命名区分:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<ChineseSpeaker>().Named<ISpeak>("cn");
builder.RegisterType<EnglishSpeaker>().Named<ISpeak>("en");
Container = builder.Build();
var sc = Container.ResolveNamed<ISpeak>("en");
sc.Say("Hello!");
注册多个相同类型并带有参数
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<ChineseSpeaker>().Keyed<ISpeak>(MyEnum.cn).SingleInstance();
builder.RegisterType<EnglishSpeaker>().Keyed<ISpeak>(MyEnum.en).InstancePerDependency();
Container = builder.Build();
var sc = Container.ResolveKeyed<ISpeak>(MyEnum.cn,new NamedParameter("param", "哈哈!"));
sc.Say("您好!");
Autofac在MVC中的使用(这里使用的是MVC5)
首先在全局类中注册:
public class MvcApplication : System.Web.HttpApplication
{
private void SetupResolveRules(ContainerBuilder builder)
{
builder.RegisterType<PersonRepository>().As<IPersonRepository>();
builder.RegisterType<CMS>().As<ICMS>();
}
protected void Application_Start()
{
var builder = new ContainerBuilder();
SetupResolveRules(builder);
builder.RegisterControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
}
1)在控制器(Controller)中使用
public class DefaultController : Controller
{
public IPersonRepository Repository { get; set; }
// GET: Default
public ActionResult Index()
{
var data = Repository.GetAll();
ViewBag.Users = data;
return View();
}
}
在视图中可以通过 foreach(var item in ViewBag.Users)进行遍历。
2)在视图(View)中使用
也可以直接在视图中使用:
@{
ViewBag.Title = "Default";
var users = DependencyResolver.Current.GetService<WebApp.Code.IPersonRepository>();
var cms = DependencyResolver.Current.GetService<WebApp.Code.ICMS>();
}
<h2>Default</h2>
@{
foreach (var item in users.GetAll())
{
<p>用户名: + @item.Name;</p>
}
}
<p>cms</p>
@(cms.WriteSomething())