1. 程式人生 > >Autofact + Asp.net MVC + EF Code First(附原始碼)

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