[译]使用LINQ to SQL创建Model Class

原文:Creating Model Classes with LINQ to SQL

这个教程的目标是讲解为ASP.NET MVC应用程序创建Model Class的一个方法。在这个教程中,我们学习如何利用Micorsoft LINQ to SQL的优势来构建Model Class和执行数据库访问。

在这个教程中,我们构建一个基本的电影数据库应用程序。首先,我们以最快最简单的方法创建这个电影数据库应用程序。我们直接在Controller中执行所有的数据访问。

接下来,我们学习如何使用Repository Pattern。使用Repository Pattern需要多做一些工作。但是采用Repository Pattern的优势足以令我们兴奋不已,它可以使我们的应用程序适应变化并易于测试。

什么是Model Class?

MVC Model包含所有应用程序逻辑,而不是包含在MVC View或者MVC Controller。特别是,应用程序的所有业务和数据访问逻辑都包含在MVC Model中。

你可以使用多种不同的技术来实现你的数据访问逻辑。比如,你可以使用Microsoft Entity Framework、NHibernater、Subsonic或者ADO.NET来构建你的数据访问类。

在这个教程中,我们使用LINQ to SQL来查询和更新数据库。LINQ to SQL提供了一个与Microsoft SQL Server数据库交互的非常简单的方法。LINQ to SQL被设计与ASP.NET MVC无关,ASP.NET MVC兼容任何数据访问技术。

创建一个电影数据库

在这个教程中(为了说明如何构建Model Class),我们构建了一个简单的电影数据库应用程序。第一步,创建一个新的数据库。在“解决方案资源管理”中,右键点击“App_Data”目录,选择“添加”->“新建项”。选择“SQL Server Database”模板,并命名为“MoviesDB.mdf”,然后点击“添加”按钮(参见Figure 1)。

mvc_tutorial10_cs_figure01

Figure 1: Adding a new SQL Server Database

新建数据库之后,我们可以在“App_Data”目录中双击“MoviesDB.mdf”文件打开数据库。双击“MoviesDB.mdf”文件打开“服务器资源管理器”(参见 Figure 2)。

mvc_tutorial10_cs_figure02

Figure 2: Using the Server Explorer window

我们需要在数据库中新建一个表,用于存储电影。在“服务器资源管理器”中,右键点击“表”,并选择“添加新表”。将打开表设计器(参见 Figure 3)。

mvc_tutorial10_cs_figure03

Figure 3: The Table Designer

我们在数据库表中加入如下列:

Column Name Data Type Allow Nulls
Id Int False
Title Nvarchar(200) False
Director Nvarchar(50) False

另外,我们对Id列还要做两种事。第一,标记Id列为主键列,在表设计器中选择Id列并点击“小钥匙”图标。当LINQ to SQL向数据库中插入或更新数据时,需要我们指定主键列。

第二,标记Id列为标识列(参见 Figure 3)。在向表加入新行时,标识列可以自动产生新的编号。

创建LINQ to SQL类

我们的MVC Model将包含一个用于表示tblMovie数据库表的LINQ to SQL类。创建LINQ to SQL类的最简单方法是,在Models目录上点击右键,选择“添加”->“新建项”。选择“LINQ to SQL类”模板,并命名为“Movie.dbml”,点击“添加”按钮(参考Figure 4)。

mvc_tutorial10_cs_figure04

Figure 4: Creating LINQ to SQL classes

创建电影表的LINQ to SQL类之后,“对象关系设计器”马上被显示出来。从“服务器资源管理器”中拖拽一个数据库表到“对象关系设计器”中,会生成一个表现数据库表的LINQ to SQL类。我们需要在“对象关系设计器”中加入tblMovie数据库表(参见 Figure 5)。

mvc_tutorial10_cs_figure05

Figure 5: Using the Object Relational Designer

默认情况下,“对象关系设计器”中生成的类名会与拖拽进来的数据库表名一模一样。然后,我们不想使用“tblMovie”来命名我们的类。因此,在“对象关系设计器”中的类名处单击左键,并重命名为“Movie”。

最后,记得点击“保存”按钮(工具栏上像软盘的那个按钮)保存LINQ to SQL类。否则我们设计的LINQ to SQL类会丢失。

在Controller Action中使用LINQ to SQL

现在我们已经有了LINQ to SQL类,我们可通过它从数据库获取数据。这个部分,你会学到如何在Controller Action中直接使用LINQ to SQL类。我们将从tblMovies数据库表中获取电影列表,并显示在MVC View中。

第一步,我们要修改HomeController类。这个类可以在应用程序的Controllers目录中找到。修改后的HomeController类参见Listing 1。

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

Listing 1 – Controllers\HomeController.cs

Listing 1中的Index() Action使用LINQ to SQL DataContext类(MovieDataContext)来代表MoviesDB数据库。MovieDataContext是Visual Studio“对象关系设计器”(Object Relational Designer)生成的。

一个LINQ查询,通过DataContext从数据库表tblMovies返回所有电影。电影列表被赋值给变量movies。最后这个列表通过ViewData传递给MVC View。

为了显示这些电影,我们接下来要修改Index View。你可以在\Views\Home目录找到Index View。按照Listing 2修改它。

<%@ 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>

Listing 2 – Views\Home\Index.aspx

注意,修改后的Index View顶部包含了一个<%@ Import Namespace %>指令。这个指令导入了MvcApplication1.Models命名空间。为了使Model中的类能够在View中正常工作,我们需要导入这个命名空间。

Listing 2中的View包含一个foreach,用于循环遍历ViewData.Model属性中的所有项。每个电影(movie)的标题(Title)属性被显示。

为了循环遍历ViewData.Model属性,ViewData.Model实现了接口IEnumerable。另一个选择是创建一个强类型的View。当你创建一个强类型的View,你需要在View的code-behind类为ViewData.Model灌入特定类型。

如果你在修改了HomeController类和Index View之后运行应用程序,你会看到一个空白页。那是因为数据库表tblMovies中还没有电影记录。

为了给tblMovies中加入新的记录,右键点击tblMovies数据库表,并选择“显示表数据”。你就可以通过显示出来的表格为tblMovies加入电影数据了(参见 Figure 6)。

mvc_tutorial10_cs_figure06s.png

Figure 6: Inserting movies

在给tblMovies表中加入一些记录之后,再运行应用程序,你可以看到Figure 7。所有电影记录被显示在列表中。

mvc_tutorial10_cs_figure07

Figure 7: Displaying movies with the Index view

使用Repository Pattern

在前面的部分,我们直接在Controller Action中使用LINQ to SQL类。我们直接在Index() Action中使用MovieDataContext类。在简单的应用程序中这么做没有什么不妥。然而,当你构建一个比较复杂的应用程序时,在Controller类中直接使用LINQ to SQL就是有问题的。

在Controller类中直接使用LINQ to SQL,会给将来切换其他数据访问技术带来困难。比如,你可能决定将数据访问技术从LINQ to SQL切换到Microsoft Entity Framework。那么,你需要重写应用程序中每一个Controller的数据库访问代码。

在Controller类中直接使用LINQ to SQL,还会使应用程序进行单元测试变得困难。一般而言,你一定不想在单元测试的时候涉及数据库。你希望单元测试可以测试应用程序的逻辑,而非数据库服务器。

为了让我们构建的MVC应用程序在未来能够适应变化,并且易于测试,我们需要考虑使用Repository Pattern。当我们使用Repository Pattern时,我们会创建一个独立的repository类,用于包含所有数据库访问逻辑。

当你创建Repository类时,首先要创建接口描述Repository类的所有方法。在Controller中,你所写的代码应该是针对接口而非Repository。这样你就可以在未来应用不同的数据访问技术来实现Repository。

在Listing 3中,一个名为IMovieRepository的接口,它声明了一个方法,名为ListAll()。

using System.Collections.Generic;
namespace MvcApplication1.Models
{
         public interface IMovieRepository
         {
              IList<Movie> ListAll();
         }
}

Listing 3 – Models\IMovieRepository.cs

在Listing 4中,Repository类(MovieRepository)实现了IMovieRepository接口,它包含了一个名为ListAll()的方法,对应IMovieRepository接口所声明的方法。

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
         }
}

Listing 4 – Models\MovieRepository.cs

最后,在Listin 5所列的MoviesController类中,使用了Repository Pattern,而没有直接使用LINQ to SQL类。

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

Listing 5 – Controllers\MoviesController.cs

请注意,在Listing 5中的MoviesController类有两个构造函数。第一个是无参数的构造函数,在应用程序启动时被调用。并实例化MovieRepository类,然传递第二个参数。

第二个构造函数具有一个IMovieRepository参数。并且只是简单将其赋值给名为_repository变量。

MoviesController类利用依赖注入模式优势。特别是,它是利用构造依赖注入。关于这个模式的更多信息,可以参考Marthin Fowler的文章:

http://martinfowler.com/articles/injection.html

这里请注意,MoviesController类的所有代码(除了第一个构造函数)都是与IMovieRepository接口交互,而非具体的MovieRepository类。与代码打交道的是一个抽象的接口,而不是一个具体的接口实现。

如果你想修改应用程序所使用的数据访问技术,你只需要简单使用不同的数据库访问技术实现IMovieRepository接口就可以了。比如,你可以创建一个EntityFrameworkMovieRepository类或者是一个SubSonicMovieRepository类。因为Controller类被编程为与接口对话,你可以通过给Controller类一个新的IMovieRepository接口实现而使其继续工作。

此外,如果你想测试MoviesController类,那么你可传递一个伪造的Moive Repository类给HomeController。你可以实现一个IMovieRepository类,不去访问实际的数据库,但包含所有IMovieRepository接口所需要的方法。这样,你就可以测试MoviesController类,但不访问实际的数据库。

摘要

这个教程的目标是讲解如果利用Microsoft LINQ to SQL的优势创建MVC Model类。我们研究了两种在ASP.NET MVC应用程序中显示数据库数据的策略。第一,我们创建了LINQ to SQL类,并直接在Controller Action中使用。在Controller中直接使用LINQ to SQL,使得在MVC应用程序中显示数据库数据非常的快捷和简单。

第二,我们探索了一个略微复杂,但更良性的显示数据库数据的途径。我们利用Repository Pattern,并将所有的数据库访问逻辑封装在一个独立的Repository类中。在我们的Controller类中,我们所写的所有代码都针对一个实际的类的接口。利用Repository Pattern可以允许我们在未来很容易的更换数据库访问技术,并且使我们的Controller类易于测试。

posted @ 2008-10-15 16:05  May 20th  阅读(982)  评论(0编辑  收藏  举报