Asp.mvc(三) ~ 使用 Autofac 实现依赖注入 #
在说 mvc 项目之前, 先看看下面一个依赖注入的例子, 新建一个名为 AutofacSample 的控制台程序,使用下面的指令获取 Atuofac :
Install-Package Autofac
创建一个 ILogger 接口, 其中定义了一个 void Log(string content)的方法:
namespace AutofacSample
{
public partial interface ILogger
{
void Log(string content);
}
}
定义 ConsoleLogger 与 TextLogger 均实现 ILogger 接口, 顾名思义: ConsoleLogger 表示使用控制台来写日志, TextLogger 表示使用文本的方式写日志:
ConsoleLogger:
using System;
namespace AutofacSample
{
public partial class ConsoleLogger : ILogger
{
public void Log(string content)
{
Console.WriteLine("时间: " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
Console.WriteLine("内容: " + content);
}
}
}
TextLogger:
using System;
using System.Configuration;
using System.IO;
using System.Text;
namespace AutofacSample
{
public partial class TextLogger : ILogger
{
private string _logPath = ConfigurationManager.AppSettings["logpath"];
public void Log(string content)
{
using (var writer = new StreamWriter(_logPath, true, Encoding.UTF8))
{
writer.WriteLine("-------------------------------------------------");
writer.WriteLine("时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
writer.WriteLine("内容:" + content);
writer.WriteLine("-------------------------------------------------");
}
}
}
}
下面再来创建一个 LogManager, 其作用就是控制 ILogger 来进入写日志:
namespace AutofacSample
{
public partial class LogManager
{
private ILogger _logger;
public LogManager(ILogger logger)
{
this._logger = logger;
}
public void Log(string content)
{
this._logger.Log(content);
}
}
}
在此声明了一个 ILogger 类型的 _logger 字段, 使用带参构造函数来初始化 _logger, 然后定义 Log 方法进行写日志 。
在 Program.cs 中编写如下代码:
using Autofac;
using Autofac.Configuration;
using System;
namespace AutofacSample
{
class Program
{
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterType(typeof(LogManager));
builder.RegisterType(typeof(ConsoleLogger)).As(typeof(ILogger));
//builder.RegisterType(typeof(TextLogger)).As(typeof(ILogger));
using (var container = builder.Build())
{
var logManager = container.Resolve<LogManager>();
logManager.Log("日志记录...");
}
Console.WriteLine("日志完毕");
Console.ReadKey();
}
}
}
定义容器构建者对象:
var builder = new ContainerBuilder();
注册 LogManager 类, 使构建者与容器知道这个类的存在:
builder.RegisterType(typeof(LogManager));
注册 ConsoleLogger类, 将其指定为 ILogger 实例:
builder.RegisterType(typeof(ConsoleLogger)).As(typeof(ILogger));
下面注释的一行意思与上面的一样, 只不过注册的是 TextLogger 类。
创建容器:
var container = builder.Build();
通过容器创建 LogManager 实例, 调用 Log 方法来写日志:
var logManager = container.Resolve<LogManager>();
logManager.Log("日志记录...");
运行之后, 我们会在CMD窗口中看到下面的结果:
下面我们把上面注册 Logger的代码改为以下(注意:由于在 TextLogger 日志文件路径是获取 app.config文件中的 appSetting节, 所以大家自行配置):
//builder.RegisterType(typeof(ConsoleLogger)).As(typeof(ILogger));
builder.RegisterType(typeof(TextLogger)).As(typeof(ILogger));
再来看看结果:
打开自己配置好的日志文件, 我的由于运行了多次, 每次运行都会增加一条记录:
上述方式是直接修改源码, 但是有时候, 我们并不像重新编译程序, 这时候, 我们可以通过配置文件来完成这个工作: 首先在配置文件(app.config)中新增一个 section 节:
<configSections>
<section name="autofac" type="Autofac.Configuration.SectionHandler, Autofac.Configuration" />
</configSections>
然后在下面在增加一些节点来注册我们的组件(Component),且制定我们的服务(Service) :
<autofac defaultAssembly="AutofacSample">
<components>
<component type="AutofacSample.TextLogger" service="AutofacSample.ILogger" />
</components>
</autofac>
需要注意的是: AutofacSample 是我们程序的命名空间, 无论是组件还是服务, 写的都是 FullName。 同样将其中的 type 改为 AutofacSample.ConsoleLogger 的话, 就表示指定控制台日志作为日志对象, 大家可以自行尝试。
好, 基本概念有了, 那下面就来说一说在 asp.net mvc 项目中如何使用 Autofac:
在 MonggodbSample 的 Services 层中(如果对此不清楚的请移步: http://blog.csdn.net/zhanxueguang/article/details/46994813)
private IRepository<M.User> _userRepository;
public UserService()
{
this._userRepository = new MongoRepository<M.User>();
}
在 UserService 中定义了
private IRepository<M.User> _userRepository
这个私有字段, 主要作用是为了获取操作数据库的相关对象, 在构造函数中, 将其实例成为
MongoRepository<M.User>
, 根据设计模式六大原则之一: 依赖倒置原则,高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。 我们直接将 _userRepository 字段实例成为
MongoRepository
, 自然是违反了这个原则, 所以我们需要这样修改:
private IRepository<M.User> _userRepository;
public UserService(IRepository<M.User> userRepository)
{
this._userRepository = userRepository;
}
将构造函数更改为接收一个
IRepository<M.User>
参数的构造函数, 下面再看 Web 层, 创建一个 UserController 控制器:
using System.Web.Mvc;
using Services.User;
using System.Threading.Tasks;
using M = Core.Domain;
using Web.Models.User;
using System.Collections.Generic;
namespace Web.Controllers
{
public class UserController : Controller
{
private IUserService _userService;
public UserController(IUserService userService)
{
_userService = userService;
}
}
}
同样,根据依赖倒置原则, 我们在 UserController 中定义了一个类型为 IUserService, 名为 _userService 的私有字段, 构造函数含义一个 IUserService userService 参数,用于初始化 _userService 字段, 但是我们仔细一想, 貌似 UserController 的父类 Controller 中并不包含带参的构造函数吧! 这个时候, Autofac 的强大之处就体现出来了, 我们首先通过 Nuget 包控制台键入以下命令获取 Autofac :
Install-Package Autofac.Mvc5
安装成功之后, 打开 Web 项目下的 Global.asax 应用程序文件:
using System.Web;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace Web
{
public class MvcApplication : HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
}
将 Application_Start 函数更改为:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
//获取容器建立者
var builder = new ContainerBuilder();
//注册泛型 Repository<>
builder.RegisterGeneric(typeof(MongoRepository<>)).As(typeof(IRepository<>))
.InstancePerLifetimeScope();
//注册 Service
builder.RegisterType(typeof(UserService)).As(typeof(IUserService));
//注册 Controller
builder.RegisterControllers(typeof(MvcApplication).Assembly);
//创建容器
var container = builder.Build();
//将上面的容器设置为一个新的依赖解析器
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
注释中对代码的作用解释的很清楚, 下面我们在 UserController 中添加一个名为 Index 的 Action :
public async Task<ActionResult> Index()
{
var result = await this._userService.FindManyAsync(x => true, 10, 0);
return View(result);
}
即调用 IUserService 中定义的 FindManyAsync 方法来获取相对应条件的数据, 我们再来创建一个以 Core.Domain.User为强类型的 Index 视图:
@model IEnumerable<Core.Domain.User>
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
<p>
@Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
@Html.DisplayNameFor(model => model.LoginName)
</th>
<th>
@Html.DisplayNameFor(model => model.LoginPwd)
</th>
<th>
@Html.DisplayNameFor(model => model.NickName)
</th>
<th>
@Html.DisplayNameFor(model => model.PhoneNo)
</th>
<th>
@Html.DisplayNameFor(model => model.Birthday)
</th>
<th>
@Html.DisplayNameFor(model => model.Email)
</th>
<th>
@Html.DisplayNameFor(model => model.Address)
</th>
<th>
@Html.DisplayNameFor(model => model.RegisterTime)
</th>
<th></th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.LoginName)
</td>
<td>
@Html.DisplayFor(modelItem => item.LoginPwd)
</td>
<td>
@Html.DisplayFor(modelItem => item.NickName)
</td>
<td>
@Html.DisplayFor(modelItem => item.PhoneNo)
</td>
<td>
@Html.DisplayFor(modelItem => item.Birthday)
</td>
<td>
@Html.DisplayFor(modelItem => item.Email)
</td>
<td>
@Html.DisplayFor(modelItem => item.Address)
</td>
<td>
@Html.DisplayFor(modelItem => item.RegisterTime)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
@Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
@Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
</td>
</tr>
}
</table>
界面效果:
上面所演示的仅仅只是 Autofac 的冰山一角, 仅仅针对 mvc, 就有注册 Controller , 设置依赖解析器, 注册模型绑定者, 注册 Web 抽象, 启用 View Pages 的属性注入, 启用动作过滤器的属性注入等等, 简直不要太多… 后续再专门抽出一个系列来说 IOC (Inversion of Conotrol) 控制反转。 好了好了,该睡觉了,时间有点晚了…