Autofact + Asp.net MVC + EF Code First(附原始碼)
本篇文章,講解如何使用Auotfac, Asp.net MVC和EF Code First,搭建一個鬆散的架構。 例子程式碼主要完成的功能是:
列出資料庫中Student表中的所有學生資訊。
閱讀目錄:
一、 使用Entity Framework Code First, 寫程式碼建立Student表
二、使用Migrations, 生成資料庫和初始化資料
三、建立Controller方法和View
四、正式專案開發中的困境
五、解耦合,脫離資料層
六、例項化,可惡的例項化
七、使用Autofac依賴注入
八、總結
一、使用Entity Framework Code First, 寫程式碼建立Student表
複製程式碼
public class SchoolContext : DbContext
{
public SchoolContext()
: base("DefaultConnection")
{
}
public DbSet<Student> Students { get; set; }
}
[Table("Student")]
public class Student
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
複製程式碼
二、使用Migrations, 生成資料庫和初始化資料
開啟”package manager console”
執行Migration, 生成資料庫更新程式碼
會在專案中生成Migrations資料夾,以及2個程式碼檔案。
修改程式碼, 在Seed方法中,新增程式的初始化資料, 新增3條記錄
複製程式碼
protected override void Seed (SchoolContext context)
{
// This method will be called after migrating to the latest version.
// You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data. E.g.
//
context.Students.AddOrUpdate(
s => s.Id,
new Student {Name = "Andrew Peters", Age = 18},
new Student {Name = "Brice Lambson", Age = 29},
new Student {Name = "Rowan Miller", Age = 56}
);
}
複製程式碼
執行”Update-Database”命令,生成和程式碼匹配的資料庫
下圖是生成的資料庫結果:
三、建立Controller方法和View
controller程式碼非常簡單,如下:
複製程式碼
public class HomeController : Controller
{
public ActionResult Index()
{
var studentRepository = new StudentRepository();
var students = studentRepository.GetStudents();//呼叫資料層方法,獲取資料
return View(students);
}
}
複製程式碼
最後,執行起來的效果:
四、正式專案開發中的困境
假設一個在一個真實的專案環境裡面,你和甲一起開發整個專案, 其中甲負責EF資料訪問部分,你負責MVC邏輯和顯示部分。
在真實專案中當然遠遠不止Student一個表,可能有上百個,還有很多的儲存過程。
你在開發的過程中,常常執行遇到資料層的bug,不斷丟擲異常,導致你的開發無法順利進行下去。你常常需要停下來,除錯到資料層,找到bug原因,然後告訴甲趕快改好,你還等著開發頁面上的一個ajax特效。
隨著不斷的出現的資料層bug, 眼看專案結束日期越來越近,你已經焦頭爛額,但是卻還有很多功能沒有完成,老闆也開始懷疑你的能力……….
五、解耦合,脫離資料層
你對甲已經忍無可忍了,你的命運為什麼要掌握在甲的手中,要想辦法擺脫甲。
好吧,我要依賴在抽象的介面上,而不是直接依賴甲開發的資料層。
首先我們可以建立一個介面:
public interface IStudentRepository
{
IEnumerable<Student> GetStudents();
}
然後, 建立一個整合這個介面的類,這個類並不訪問資料庫,但是提供我們開發頁面所需的資料。
複製程式碼
public class StubStudentRepository:IStudentRepository
{
public IEnumerable<Student> GetStudents()
{
return new[]
{
new Student {Id = 1, Name = "Sam", Age = 14}
};
}
}
複製程式碼
好了,一切都準備好了, 開始改造我們的Controller程式碼
複製程式碼
public class HomeController : Controller
{
public ActionResult Index()
{
IStudentRepository studentRepository = new StubStudentRepository();
//IStudentRepository studentRepository = new StudentRepository();//註釋掉訪問資料層的程式碼,用Stub類代替
var students = studentRepository.GetStudents();
return View(students);
}
}
複製程式碼
由於,我們寫的Stub類,不訪問資料庫,而且不需要有複雜的邏輯,只是提供我們Controller程式碼執行所需要的基本資料就可以了。這樣你的開發就依賴在你自己寫的更加可靠的Stub類上了。
最後,你叫來甲,對他說:哥們, 我為我們之間的依賴,建立好了介面,你以後的資料訪問程式碼,都從這個介面繼承吧。
從此,這個專案開發變成了另外一種樣子,你再也不抱怨甲總是寫不穩定的程式碼了(因為你不依賴他了),你總是能通過寫一些Stub類,返回不同的值,來測試你的介面程式碼。
六、例項化,可惡的例項化
在Controller的程式碼中,我們有下面2行程式碼,如果是釋出的情況下,我們使用下面一行,開發過程中,使用上面一行。
但是,這個專案程式碼太多了,難道到釋出的時候,要我一個個找出來,都換成真實的甲的資料庫訪問層的類的例項嗎?
IStudentRepository studentRepository = new StubStudentRepository();
//IStudentRepository studentRepository = new StudentRepository();//註釋掉訪問資料層的程式碼,用Stub類代替
七、使用Autofac依賴注入
這個時候,就是Autofac大顯身手的時候了,
首先,我們改造Controller程式碼:
public class HomeController : Controller
{
private readonly IStudentRepository _studentRepository;
//由建構函式來提供Controller的依賴IStudentRepository
public HomeController(IStudentRepository studentRepository)
{
_studentRepository = studentRepository;
}
public ActionResult Index()
{
var students = _studentRepository.GetStudents();
return View(students);
}
}
然後, 修改Global.asax,
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
//Autofac初始化過程
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly);//註冊所有的Controller
//開發環境下,使用Stub類
builder.RegisterAssemblyTypes(typeof (MvcApplication).Assembly).Where(
t => t.Name.EndsWith("Repository") && t.Name.StartsWith("Stub")).AsImplementedInterfaces();
//釋出環境下,使用真實的資料訪問層
//builder.RegisterAssemblyTypes(typeof(MvcApplication).Assembly).Where(
// t => t.Name.EndsWith("Repository")).AsImplementedInterfaces();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
//其它的初始化過程
........
}
}
複製程式碼
當我們使用下面這行程式碼的時候,所有的controller就都是使用Stub類的例項
//開發環境下,使用Stub類
builder.RegisterAssemblyTypes(typeof (MvcApplication).Assembly).Where(
t => t.Name.EndsWith("Repository") && t.Name.StartsWith("Stub")).AsImplementedInterfaces();
當我們使用下面程式碼的時候,所有的controller就都用的是實際的資料訪問類例項。
//釋出環境下,使用真實的資料訪問層
builder.RegisterAssemblyTypes(typeof(MvcApplication).Assembly).Where(t => t.Name.EndsWith("Repository")).AsImplementedInterfaces();