使用Linq to Sql建立模型層
在這篇文章中我們建造一個電影資料庫管理程式,在這裡我們以一種極快極簡單的方式來建立電影資料庫管理程式,在控制層中直接對資料庫進行操作。
接著我們學習使用Reporsitory模式,使用Repository模式需要我們額外化些力氣,但它可以使我們的程式更容易測試,更好地應對變化。
一、什麼是模型類
MVC模型層包含除了檢視層和控制層之外的所有的邏輯,MVC模型層包含所有的商業邏輯和資料訪問邏輯
我們可以使用各種不同的技術來實現資料訪問邏輯。如:我們可以使用Microsoft Entity Framework,NHibernate,Subsonic或ADO.NET類來建造。
在這篇文章中,我們使用Linq to Sql來實現對資料庫的查詢和更新。Linq to Sql為我提供了操作Sql Server資料庫的非常簡單的介面,但不要以為ASP.NET MVC框架只能繫結Linq to Sql一起使用,ASP.NET MVC兼容於任何資料訪問技術。
二、建立電影資料資料庫
在這篇文章中,為了演示如何建立模型類,我們建立一個簡單的電影資料庫管理程式。
第一步建立一個新的資料庫。在解決方案資料夾中右擊App_Data資料夾選擇“Add”-“New Item”,在彈出的對話方塊中,選擇Sql Server Database,命名為MoviesDB.mdf。點選Add按鈕,如圖所示
《圖1》
當我們建立新資料庫後,我們在App_Data資料夾中雙擊MoviesDB.mdf檔案,開啟SqlServer Explorer如下圖所示
《圖2》
現在我們向資料庫中新增表以儲存電影資訊。在SqlServer Explorer中右擊Tables資料夾,選擇“Add”-“New Table”,開啟資料庫設計介面,如圖所示
《圖3》
我們需要對Id列做兩件事,首選需要把Id列標記為主鍵列,Linq to Sql在對資料進行insert和update操作時需要指定主鍵列資訊,其次需要把Id列標記為自增長列。
三、建立Linq to Sql類
我們本程式的的MVC模型層中包含對tblMovie資料庫的Linq to Sql類。建立Linq to Sql 類的簡單方法是:右擊Models資料夾,選擇“Add”-“New Item”,在彈出的對話方塊中選擇Linq to Sql Classes,並將其命名為Move.dbml,點選“Add”按鈕。
《圖4》
建立完Linq to Sql類後,會顯示Object Relational Designer。我們從Server Explorer視窗中拖動表到Object Relation Designer上,這時會在設計器上現示資料表。
《圖5》
預設情況下,當我們拖一個表到Object Relation Designer上的時候,Object Relation Designer會建立一個對應的類。如果我們不想讓我們的類名與表同名(tblMovie),在設計器中點選類名把它改成你想要的即可。
最後記得點選儲存按鈕,以生成並儲存我們的Linq to Sql類,否則Object Relation Designer不會生成Linq to Sql類。
四、在控制器動作中使用Linq to Sql
現在我們有了Linq to Sql類,我們可以使用這些類從資料庫檢索資料。在這一節中我們學習:如何在控器動作中使用Linq to Sql類直接訪問資料庫,並在檢視層顯示tblMovies表的資訊。
首先,我們修改HomeController類,這可以在應用程式Controllers資料夾中找到。把類的程式碼改成如下形式:
Listing 1 – Controllers/HomeController.cs
using System.Linq;
using System.Web.Mvc;
using MvcApplication1.Models;
namespace MvcApplication1.Controllers
{
[HandleError]
public class HomeController : Controller
{
public ActionResult Index()
{
var dataContext = new MovieDataContext();
var movies = from m in dataContext.Movies select m;
return View(movies);
}
}
}
在上面的程式碼的Index()動作中,我們使用Linq to Sql的DataContext(MovieDataContext類)操作MovieDB資料庫。MovieDataContext類是由Object Relation 設計器自動生成的類。
Linq查詢是使用DataContext檢索tblMovies表中所有資料。電影列表資料被賦給區域性變數movies,最後movies列表通過viewdata傳輸給檢視層。
要顯示所有的電影資訊,我們需要修改應用程式Views/Home/Index檢視。程式碼如下:
Listing 2 – Views/Home/Index.aspx
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="MvcApplication1.Views.Home.Index" %>
<%@ Import Namespace="MvcApplication1.Models" %>
<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
<ul>
<% foreach (Movie m in (IEnumerable)ViewData.Model)
{ %>
<li> <%= m.Title %> </li>
<% } %>
</ul>
</asp:Content>
上面的程式碼中包含<%@ Import Namespace="MvcApplication1.Models" %>,它匯入MvcApplication1.Models名稱空間,MvcApplication1.Models是我們模型類所在的名稱空間。
上面的程式碼有一個foreach迴圈迭代ViewData.Model中的每一項,每一部電影的標題就顯示出來了。
我們可以看到,在上面的程式碼中,我們把ViewData.Model強制轉換為 IEnumerable型別。只有這樣我們才能對ViewData.Model返回的資料進行迭代。
如果我們現在執行程式會發現是個空白頁面,因為我們還沒有向資料庫新增資料,在Server Explorer視窗中右擊tblMovies表,在彈出選單中選擇“Show Table Data”,然後輸入如圖的資料。
《圖6》
當我們向資料庫的表中新增完記錄後,再執行程式的時候,我們會發現所有的電影資料都顯示在列表中。如圖所示。
《圖7》
五、使用Repository模式
在前面部分中,我們在控制器動作中直接使用Linq to Sql類。在簡單的應用程式中我們這種做法並沒有什麼問題 ,但對於複雜的程式,這種直接在控制器中寫Linq to Sql操作資料庫的做法會給我們帶來不少的麻煩。
在控制器中直接編寫Linq to Sql的這種做法,在將來我們變更資料庫訪問技術的時候會變得很困難。比如說,我們將來可以會使用Microsoft Entity Framework來替換Microsoft Linq to Sql來訪問資料庫,一旦這樣做,那我們需要去修改每個控制器的程式碼。
另外在控制器中直接編寫Linq to Sql程式碼會使我們很難為應用程式編寫單元測試。一般地,當我們進行單元測試的時候,並不需要直接與資料庫互動,因為我們想要測試的是我們的程式邏輯,而不是測試資料庫伺服器。
為了使MVC應用程式更易維護,更易測試,我們應考慮使用Repository模式。
我們建立一個介面,該介面中定義了對資料庫操作所必需的方法宣告。然後我們編寫類,在類中實現介面中的方法,採取某種資料庫訪問技術操作資料庫。在控制器中,我們基於介面編寫程式碼,這樣我們就可以在將來變更資料庫訪問技術,而不需要修改控制器了。
這裡我們編寫了一個介面IMovieRepository,該介面中定義了一個方法ListAll();
Listing 3 – Models/IMovieRepository.cs
using System.Collections.Generic;
namespace MvcApplication1.Models
{
public interface IMovieRepository
{
IList<Movie> ListAll();
}
}
然後編寫一個類MovieRepository實現IMovieRepository介面,在該類中實現方法ListAll()。
Listing 4 – Models/MovieRepository.cs
using System.Collections.Generic;
using System.Linq;
namespace MvcApplication1.Models
{
public class MovieRepository : IMovieRepository
{
private MovieDataContext _dataContext;
public MovieRepository()
{
_dataContext = new MovieDataContext();
}
#region IMovieRepository Members
public IList<Movie> ListAll()
{
var movies = from m in _dataContext.Movies select m;
return movies.ToList();
}
#endregion
}
}
最後,我們在控制器MoviesController中使用Repository 模式,而不是直接使用Linq to Sql類操作資料庫。
Listing 5 – Controllers/MoviesController.cs
using System.Web.Mvc;
using MvcApplication1.Models;
namespace MvcApplication1.Controllers
{
public class MoviesController : Controller
{
private IMovieRepository _repository;
public MoviesController() : this(new MovieRepository())
{
}
public MoviesController(IMovieRepository repository)
{
_repository = repository;
}
public ActionResult Index()
{
return View(_repository.ListAll());
}
}
}
上面的程式碼中,MoviesController類有兩個建構函式,第一個建構函式是個無參建構函式。該建構函式被呼叫的時候,建立一個MovieRepository類的例項,並把這個例項傳遞給第二個建構函式。
第二個建構函式有一個IMovieRepository型的構造引數。這個建構函式就是把傳進來的引數賦給成員變數_repository。
MoviesController控制器的這種做法是為了實現依賴注入模式而設計的,即我們經常說的“構造依賴注入”。關於依賴注入的相關內空請參見Martin Fowler的文章
http://martinfowler.com/articles/injection.html
還需要引起我們注意的是,在 MoviesController類中,我們是與IMovieRepository介面進行互動,而不是直接與MovieRepository類進行互動。這就是我們常說的“要依賴於抽象,而不依賴於具體。”
當我們想修改資料庫訪問程式碼的時候,我們只需要編寫另一個類,該類也實現IMovieRepository介面。假設,我們建立了EntityFrameworkMovieRepository類或SubSonicMovieRepository類,因為我們控制器是基於介面編碼的,所以我們在構造控制器的時候傳入其中上面兩個類的一個例項,那控制器和相關的類就要以配合工作了。
如果我們想對MoviesController類進行測試的話,我們可以傳一個fake類到MoviesController控制器。這個fake類也是實現IMovieRepository介面,但並沒有真正訪問資料庫。這樣我們就可以只對MoviesController類進行測試,而沒必要使用真實的資料訪問邏輯。
總結
這篇文章演示瞭如何使用Linq to Sql建立MVC模型類。
首先,我們需要建立Linq to Sql類,然後直接在控制器動作中使用這些類。這種做法可以使我們快速簡單地在MVC應用程式中顯示資料庫中的資料。
然後,我們又把問題稍微複雜化了一些,但資料顯示更靈活,程式的彈性更大。這是因為我們使用了Repository模式。這種模式把所有的資料庫訪問邏輯全放在一個模型類中。在控制器中我們只對介面進行程式設計,這更能夠為我們將來更改資料訪問方式帶來很大的方便 ,並使程式更易測試。