一起谈.NET技术,【译】ASP.NET MVC并不仅仅只是Linq to SQL

  很多ASP.NET的教程中的示例代码使用的数据访问方法是Linq to Sql或是Entity Framework。我在www.asp.net的论坛上看到很多关于讨论是否有其他替代的数据库访问方式,回答是:当然有。这篇文章就讲述了使用Ado.Net作为数据访问层来实现一个典型的增删查改程序。

  由于是以练习作为目的,那我就不妨借用Spaanjaar’s 的N层构架文章(Building Layered Web Applications with Microsoft ASP.NET 2.0.)的构架方式。我强烈推荐你阅读他的系列文章,如果嫌太长起码也得看完前两部分,这样就能对N-Layer构架有个基本的认识。N-Layer构架的三个关键层分别为:业务对象层,业务逻辑层和数据访问层。而其数据访问层会几乎不加改变的包含在本文的MVC项目中,Spaanjaar的文件详细描述了各个层是如何组织的。这篇文章仅仅讲述各个层所扮演的角色,但是不会深入到代码的细节中。

  首先,我们来看Imar提供的程序,这是一个具有典型增删查改的程序,这个程序允许用户管理联系人,包括联系人的地址,电话,email。它能增,删,查,改任何实体。

  程序内包括的实体有:ContactPersons, PhoneNumbers, Addresses EmailAddresses.他们都隶属于程序的业务对象(BO)层。上述的每一个类都包含可以获取或者赋值的属性,但并不包含任何方法。而所有方法存放于业务逻辑层(BLL)中的对应类中。在业务对象层和业务逻辑层的实体和实体manger是一对一的关系,在业务逻辑层中类包含的方法都会返回业务对象层(BO)的实例,或是实例集合,或者保存实例(更新或是添加),或是删除实例。业务逻辑层(BLL)中也可以包含一些业务规则验证,安全性检查的代码。但在本篇文章为了简便起见,就不添加这些了。如果你对业务规则和安全性有兴趣的话,可以去看Imar文章的6 part series

  最后一层是数据访问层(DAL),同样,DAL层的类也和业务逻辑层(BLL)内的类有着一对一的关系,在BLL层的类中会调用相关DAL层中的方法。而在这些层中,只有DAL层需要知道利用什么技术(linq,entity framework..)保存业务实体。在本例中,使用Sql Server Express数据库和Ado.net。而这样分层的思想是如果你需要更换数据源(XML,oracle,更或者是Web Service甚至是Linq to Sql或者其他ORM框架),因为DAL层给BLL层暴漏的方法的签名是一致的,所以只需要更换DAL层即可。而为了保证所有DAL的实现有着同样的签名,则利用接口即可。但我想或许是未来帖子中讨论的话题了吧。

  MVC构架

  已经有很多优秀的文章中已经探讨了MVC程序的构架,所以本篇文章就不再累述相关细节了。如果想要了解更多,我推荐访问Asp.net MVC官方站点.简单二代说,M代表Model,也是包含BO,BLL,DAL的地方,V代表View,也是UI相关开发的部分,或者说是用户看到的部分,C是Controller的简写,也是控制用户请求与程序回复的部分。如果用户点击了一个指向特定地址的按钮,请求会和Controller的Action(类的方法)进行匹配,而Action负责处理请求,并返回响应。通常情况下是一个新的View,或者是更新现有的View。

  下面用Visual Studio创建一个MVC应用程序并删除默认的View和Controller,然后将Imar的程序中的Bo,Bll和DAL复制到这个MVC程序的Model内,我还复制了响应的数据库文件和Style.css。

  我还做了一些其他的修改,把数据库连接字符串添加到Web.Config中。除此之外,我还将复制过来的代码的命名空间做了响应的调整并把DAL层的代码升级到了3.0.虽然这并不是必须的。做完这些,我按Ctrl+Shift+F5来测试是否编译成功。------------------

  Controller

  我添加了4个Controller(Visual Studio附带的默认Controller已经被删除),和四个实体类相匹配。它们分别为:ContactController, PhoneController, AddressController and EmailController。

  每个Controller都含有四个Action:List, Add, Edit and Delete。首先需要在Global.exe中为这些Action注册这些路由。

public static void RegisterRoutes(RouteCollection routes)
{
  routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
  routes.MapRoute(
      "Default",
      "{controller}/{action}/{id}",
      new { controller = "Contact", action = "List", id = " " }
  );
}

  默认的View会显示所有联系人,BLL层中的ContactPersonManager类内的GetList()方法会获取所有联系人的数据,响应的List() Action代码如下:

public ActionResult List()
{
  var model = ContactPersonManager.GetList();
  return View(model);
}

  强类型View

  整个实例代码中,我都会使用强类型的View,因为这样不仅可以使用intellisense(智能提示)的好处,还不必依赖于使用String作为索引值的ViewData。为了让代码更加有序,我添加了一些命名空间到web.config中的节点,这些都是我用来替换Imar代码的方式:

namespace="System.Web.Mvc"/>
namespace="System.Web.Mvc.Ajax"/>
namespace="System.Web.Mvc.Html"/>
namespace="System.Web.Routing"/>
namespace="System.Linq"/>
namespace="System.Collections.Generic"/>
namespace="ContactManagerMVC.Views.ViewModels"/>
namespace="ContactManagerMVC.Models.BusinessObject"/>
namespace="ContactManagerMVC.Models.BusinessObject.Collections"/>
namespace="ContactManagerMVC.Models.BusinessLogic"/>
namespace="ContactManagerMVC.Models.DataAccess"/>
namespace="ContactManagerMVC.Models.Enums"/>

  上面代码意味着我可以在程序的任何地方访问上述命名空间。上面GetList()方法返回的类型是ContactPersonList,这个类型是ContactPerson对象的集合,在显示List的View中的Page声明如下:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
    Inherits="System.Web.Mvc.ViewPage<ContactPersonList>" %>

  我想你已经注意到了我使用了MasterPage,利用MasterPage,我借用了从Imar实例代码中的Css文件。用于显示ContactPerson对象的HTML如下:

<table class="table">
    <tr>
      <th scope="col">Idth>
      <th scope="col">Full Nameth>
      <th scope="col">Date of Birthth>
      <th scope="col">Typeth>
      <th scope="col"> th>
      <th scope="col"> th>
      <th scope="col"> th>
      <th scope="col"> th>
      <th scope="col"> th>
    tr>
    <%
      if (Model != null)
      {
        foreach (var person in Model)
        {%>
    <tr>
      <td><%= person.Id %>td>
      <td><%= person.FullName %>td>
      <td><%= person.DateOfBirth.ToString("d") %>td>
      <td><%= person.Type %>td>
      <td title="address/list" class="link">Addressestd>
      <td title="email/list" class="link">Emailtd>
      <td title="phone/list" class="link">Phone Numberstd>
      <td title="contact/edit" class="link">Edittd>
      <td title="contact/delete" class="link">Deletetd>
    tr>
    <%
        }
      }else{%>
    <tr>
      <td colspan="9">No Contacts Yettd>
    tr>
     <% }%>
  table>

   我想你已经能发现强类型View的好处了吧,Model的类型是ContactPersonList,所以你可以任意使用ContactPersonList的属性而不用强制转换,而强制转换错误只有在运行时才能被发现,所以这样省了不少事。

 在做Html时,我小小的作弊了一下,我本可以使用vs对list自动生成的view,但我没有。因为我需要和Imar的css文件相匹配的html.所以我运行了imar的程序,在浏览器中查看源代码,并把生成的html复制下来,imar使用GridView来生成列表,所以CSS会在运行时嵌入到html代码中。我将这些css代码移到一个外部的css文件中。并给

  

元素添加了一些额外样式,你可以在代码的下载链接中找到。

  我还未一些表格的单元格添加了title属性。这些包含了访问其他action的链接。我希望用户在浏览电话本或者编辑或者删除电话本时页面不会post back,换句话说,我希望我的网站是ajax的。而title属性就发挥作用了。而“。link”这个css选择符可以让普通的文本看起来像超链接,也就是有下划线和鼠标hover时出现小手。

  jQuery AJax

    在我们深入实现ajax功能的相关代码之前,下面3行代码是需要加入到html中:

button" id="addContact" name="addContact" value="Add Contact" />

details">

dialog" title="Confirmation Required">Are you sure about this?

  第一行代码的作用是一个允许用户添加新联系人的按钮,第二行代码是一个空div,方便后面显示信息,第三行代码是jQuery modal 提示验证对话框的一部分,用于提示用户是否删除一条记录。

     还需要在页面中添加3个js文件,第一个是主要的jQuery文件,其他两个分别是jQuery UI的核心js,以及date picker和modal dialog部分的js

  下面是我们生成后的列表显示视图以及完全的js代码:

  上面代码看起来让人望而却步,但实际上,使用jQuery,这非常简单。并且在上面代码中我多处加入了注释,代码一开始是用js代码是替换了数据源控件默认呈现出来的隔行变色的颜色。然后我加入了使点击行点击时可以变色的代码。然后下面的代码是防止IE对页面进行缓存。如果不禁止了IE缓存,你会为执行编辑或者删除以后,数据库改变了但页面却没有发生变化而撞墙的。

  接下来的代码更有趣了,依靠live()方法可以确$选择器中所有匹配的元素都被附加了相应的事件,无论元素当前是否存在于页面中。比如说,当你点击了Addresses链接,相应的结果就会在另一个表中出现:

  上图中可以看出表中包含编辑和删除链接,如果不使用live()方法,相应的链接就不会被附加事件。所有具有class为link的元素都会被委派上click事件。这个事件会首先获取到当前条目的id.以ContactPerson表为例,这个id就是ContactPersonId.在相应的子表中,则id会是电话号码或者是email地址。这些都是需要传递给controller action作为参数进行编辑,删除,或者显示的。

  你现在可以看出为什么我为每个单元格加上title属性了吧。title属性包含相关的route信息,而完全的url则把id附加到route作为url.然后,上面js会做一个检查,来看route信息内是否包含delete,如果包含delelte,则弹出一个对话框来提示用户是否要删除。看,是不是很简单?

  最后,代码会为添加联系人按钮添加click事件,在文章的下部分我们再进行讨论

  添加联系人以及自定义View Models

  使用Asp.net添加一条记录时,通常的做法是提供一个包含一系列输入框的form.对于ContactPerson类的大多属性来说,都是可以直接赋值的,比如:姓,名字,生日。但是其中有一个属性却不能直接赋值--Type属性,这个值是从PersonType.cs(朋友,同事等)中的枚举类型中选择出来的.这意味着用户只能从有限的几种选项中选择一种。可以用DropDwonList来实现,但是ContactPerson对象并不包含枚举类型的所有选项,所以我们只能提供一个自定义版本的ContactPerson类传递给View.这也是为什么要用到自定义View Model

我看过很多关于在程序的哪里放置View Models的讨论,有些人认为自定义View Models是Model的一部分,但是我认为它和View紧密相关,View Models并不是MVC程序必不可少的一部分,也不能服用,所以它不应该放到Model中区。我把所有的View Model放入一个创建的ViewModel文件夹下,ContactPersonViewModel.cs的源代码如下:

using System;
using System.Collections.Generic;
using System.Web.Mvc;
namespace ContactManagerMVC.Views.ViewModels
{
  public class ContactPersonViewModel
  {
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string MiddleName { get; set; }
    public string LastName { get; set; }
    public DateTime DateOfBirth { get; set; }
    public IEnumerable Type { get; set; }
  }
}

  看上面类的最后一个属性,我将这个type属性设置成IEnumerable类型,这样就可以将Type绑定到View中的Html.DropDownList了。

  对应的,也要在Controller中添加2个action,第一个action使用AcceptVerbs(HttpVerbs.Get)标签,第二个action使用AcceptVerbs(HttpVerbs.Post)标签,第一个方法用于从服务器向客户端传值,第一个方法处理从form提交的数据。

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Add()
{
  var personTypes = Enum.GetValues(typeof (PersonType))
    .Cast()
    .Select(p => new
                   {
                     ID = p, Name = p.ToString()
                   });
  var model = new ContactPersonViewModel
                {
                  Type = new SelectList(personTypes, "ID", "Name")
                };
  return PartialView(model);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Add(ContactPerson person)
{
  ContactPersonManager.Save(person);
  return RedirectToAction("List");
}

  上面第一个action的前几行代码将ContactType枚举类型转换成数组,数组中的每一个元素都是一个包含id和name属性的匿名对象,id是枚举值,Name是和对应枚举匹配的constant值,ContactPersonViewModel然后进行初始化并且Type属性被赋值相应的类型。我使用Partial View来添加联系人和使用ContactPersonViewModel类型的强类型,对应View部分的代码如下:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<script type="text/javascript">
  $(function() {
  $('#DateOfBirth').datepicker({ dateFormat: 'yy/mm/dd' });
  });
script>
<% using (Html.BeginForm("Add", "Contact", FormMethod.Post)) {%>
      <table>
        <tr>
          <td class="LabelCell">Nametd>
          <td><%= Html.TextBox("FirstName") %>td>
        tr>
        <tr>
          <td class="LabelCell">Middle Nametd>
          <td><%= Html.TextBox("MiddleName") %>td>
        tr>
        <tr>v
          <td class="LabelCell">Last Nametd>
          <td><%= Html.TextBox("LastName") %>td>
        tr>
        <tr>
          <td class="LabelCell">Date of Birthtd>
          <td><%= Html.TextBox("DateOfBirth", String.Empty)%>td>
        tr>
        <tr>
          <td class="LabelCell">Typetd>
          <td><%=Html.DropDownList("Type")%>
          td>
        tr>
        <tr>
          <td class="LabelCell">td>
          <td><input type="submit" name="submit" id="submit" value="Save" />td>
        tr>
      table>
<% } %>

  在最上面的代码我使用了jQuery UI的Date picker插件作为DateOfBirth输入框的选择方式,而DateOfBirth的第二个参数保证这个textbox在初始状态下为空。此外,所有的输入框对ContactPerson的对应属性名相同,这是为了确保默认的model binder不出差错,MVC还自动为ContactType枚举进行绑定。

  负责响应Post请求的方法可以自动将Request.Form的值和ContactPerson对象的对应属性进行匹配,然后调用BLL层的Save()方法,然后RedicrectToAction会让页面刷新最后调用List这个action.

  编辑一个联系人

  和前边一样,需要在Controller添加两个action来完成编辑,一个用于响应Get请求,另一个用于响应Post请求:

[AcceptVerbs(HttpVerbs.Get)]
    public ActionResult Edit(int id)
    {
      var personTypes = Enum.GetValues(typeof (PersonType))
        .Cast()
        .Select(p => new { ID = p, Name = p.ToString() });
      var contactPerson = ContactPersonManager.GetItem(id);
      var model = new ContactPersonViewModel
                    {
                      Id = id,
                      FirstName = contactPerson.FirstName,
                      MiddleName = contactPerson.MiddleName,
                      LastName = contactPerson.LastName,
                      DateOfBirth = contactPerson.DateOfBirth,
                      Type = new SelectList(personTypes, "ID", "Name", contactPerson.Type)
                    };
      return PartialView(model);
    }
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Edit(ContactPerson person)
    {
      ContactPersonManager.Save(person);
      return RedirectToAction("List");
    }

     

  我们在前面已经看到jQuery是如何调用Edit这个action并把被编辑人的id作为参数进行传递的了,然后id用于通过众所周知的bll调用DAL从数据库将联系人的详细信息取出来。在这个过程中ContactPersonViewModel被创建来返回从数据库取出的值,SelectList的通过三个参数构造,第一个参数是Person的类型,第三个参数是DropDownList当前选择的值

partial view的代码和Add View几乎一样:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<script type="text/javascript">
  $(function() {
    $('#DateOfBirth').datepicker({dateFormat: 'yy/mm/dd'});
  });
script>
<% using (Html.BeginForm("Edit", "Contact", FormMethod.Post)) {%>
     <table>
        <tr>
          <td class="LabelCell">Nametd>
          <td><%= Html.TextBox("FirstName") %>td>
        tr>
        <tr>
          <td class="LabelCell">Middle Nametd>
          <td><%= Html.TextBox("MiddleName") %>td>
        tr>
        <tr>
          <td class="LabelCell">Last Nametd>
          <td><%= Html.TextBox("LastName") %>td>
        tr>
        <tr>
          <td class="LabelCell">Date of Birthtd>
          <td><%= Html.TextBox("DateOfBirth", Model.DateOfBirth.ToString("yyyy/MM/dd")) %>td>
        tr>
        <tr>
          <td class="LabelCell">Typetd>
          <td><%= Html.DropDownList("Type")%>td>
        tr>
        <tr>
          <td class="LabelCell"><%= Html.Hidden("Id") %>td>
          <td><input type="submit" name="submit" id="submit" value="Save" />td>
        tr>
      table>
<% } %>

  关键不同是DateOfBirth包含一个将生日信息转换更容易识别的方式,还有在提交按钮附近添加了一个Html.Hidden(),用于保存被编辑用户的id。当然,form的action属性肯定是和Add的View不同。在Form中可以加一个参数用于告诉Action是Add View还是Edit View,这样就能减少代码的重复,但是在示例代码中我还是把他们分成了不同的action,来让职责划分的更清晰一些。

  删除一个联系人

  删除action就简单多了,并且不需要与之相关的View.仅仅是重新调用List这个action,被删除的数据就不复存在了。

public ActionResult Delete(int id)
{
  ContactPersonManager.Delete(id);
  return RedirectToAction("List");
}

  上面代码是我对BLL和DAL做出改变的地方,原来的ContactPersonManager.Delete()方法的参数是Person的实例,而在DAL中,只有id作为参数。我看不出传递整个对象给BLL,但BLL却只传递对象的唯一ID给DAL的意义所在,所以我将BLL的代码改成接受int类型的参数,这样写起来会更简单。

  当删除链接被点击时,调用jQuery的confirmation modal dialog:

  如果点击取消,则什么事也不发生,如果点击确认,则链接就会指向响应的delete action.

  管理集合

  所有的集合--PhoneNumberList,EmailAddressLIst,AddressList被管理的方式如出一辙。所以,我仅仅挑选EmailAddressLIst作为示例来说明方法,你可以下载示例代码来看其他部分。

  首先,我们来看显示被选择联系人的email地址,这包含了controller中List这个Action:

public ActionResult List(int id)
{
  var model = new EmailAddressListViewModel
                {
                  EmailAddresses = EmailAddressManager.GetList(id),
                  ContactPersonId = id
                };
  return PartialView(model);
}

  上面方法使用联系人的id作为联系人,并且返回一个自定义View Model—EmailAddressListViewModel.这也是将联系人的id传到View的方法:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<script type="text/javascript">
  $(function() {
    $('#add').click(function() {
      $.get('Email/Add/<%= Model.ContactPersonId %>', function(data) {
        $('#details').html(data);
      });
    });
  });
script>
<table class="table">
   <tr>
     <th scope="col">Contact Person Idth>
     <th scope="col">Emailth>
     <th scope="col">Typeth>
     <th scope="col"> th>
     <th scope="col"> th>
   tr>
   <%if(Model.EmailAddresses != null)
     {foreach (var email in Model.EmailAddresses) {%>
   <tr>
     <td><%= email.Id %>td>
     <td><%= email.Email %>td>
     <td><%= email.Type %>td>
     <td title="email/edit" class="link">Edittd>
     <td title="email/delete" class="link">Deletetd>
   tr>
        <%}
    }else
 {%>
   <tr>
     <td colspan="9">No email addresses for this contacttd>
   tr>
 <%}%>
table>
<input type="button" name="add" value="Add Email" id="add" />

  你可以看出添加方法需要ContactPersonID作为参数,我们需要确保可以添加新的联系人到响应的联系人列表。编辑和删除方法也是如此--id参数通过url传递到相关action.而所有的单元格都有title属性,这样就可以使用前面部署的live()方法:

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Add(int id)
{
  var contactTypes = Enum.GetValues(typeof(ContactType))
    .Cast()
    .Select(c => new
    {
      Id = c,
      Name = c.ToString()
    });
  var model = new EmailAddressViewModel
                {
                  ContactPersonId = id,
                  Type = new SelectList(contactTypes, "ID", "Name")
                };
  return PartialView("Add", model);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Add(EmailAddress emailAddress)
{
  emailAddress.Id = -1;
  EmailAddressManager.Save(emailAddress);
  return RedirectToAction("List", new {id = emailAddress.ContactPersonId});
}

  创建自定义View Model存在的目的是为了对现有的EmailAddress进行添加或编辑,这包括一些绑定IEnumerable集合到Type dropdown的属性,上面两个Add不同之处在于他们的返回不同,第一个返回partial view,第二个返回List这个action.

  集合中的每一个选在在一开始都会将id设为-1,这是为了保证符合”Upsert”存储过程的要求,因为Imar对于添加和删除使用的是同一个存储过程,如果不设置成-1,则当前新建的联系人会更新掉数据库中的联系人。如果你想了解更多,请看他的文章。下面是添加email address的partial view:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<script type="text/javascript">
  $(function() {
    $('#save').click(function() {
      $.ajax({
        type: "POST",
        url: $("#AddEmail").attr('action'),
        data: $("#AddEmail").serialize(),
        dataType: "text/plain",
        success: function(response) {
          $("#details").html(response);
        }
      });
    });
  });
script>
<% using(Html.BeginForm("Add", "Email", FormMethod.Post, new { id = "AddEmail" })) {%>
<table class="table">
<tr>
  <td>Email:td>
  <td><%= Html.TextBox("Email")%>td>
tr>
<tr>
  <td>Type:td>
  <td><%= Html.DropDownList("Type") %>td>
tr>
<tr>
  <td><%= Html.Hidden("ContactPersonId") %>td>
  <td><input type="button" name="save" id="save" value="Save" />td>
tr>
table>
<% } %>

  上面jQuery代码负责通过Ajax提交form.jQuery给html button(而不是input type=”submit”)附加事件,然后将页面中的内容序列化并通过post 请求发送到使用合适AcceptVerbs标签修饰的名为add()的action.

  编辑和删除EmailAddress对象

  编辑EmailAddress对象所涉及的action和前面提到的很相似,仍然是两个action,一个用于处理get请求,另一个用于处理post请求:

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Edit(int id)
{
  var emailAddress = EmailAddressManager.GetItem(id);
  var contactTypes = Enum.GetValues(typeof(ContactType))
    .Cast()
    .Select(c => new
    {
      Id = c,
      Name = c.ToString()
    });
  var model = new EmailAddressViewModel
  {
    Type = new SelectList(contactTypes, "ID", "Name", emailAddress.Type),
    Email = emailAddress.Email,
    ContactPersonId = emailAddress.ContactPersonId,
    Id = emailAddress.Id
  };
  return View(model);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(EmailAddress emailAddress)
{
  EmailAddressManager.Save(emailAddress);
  return RedirectToAction("List", "Email", new { id = emailAddress.ContactPersonId });
}

  下面的partial View代码现在应该很熟悉了吧:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<script type="text/javascript">
  $(function() {
    $('#save').click(function() {
      $.ajax({
        type: "POST",
        url: $("#EditEmail").attr('action'),
        data: $("#EditEmail").serialize(),
        dataType: "text/plain",
        success: function(response) {
          $("#details").html(response);
        }
      });
    });
  });
script>
<% using(Html.BeginForm("Edit", "Email", FormMethod.Post, new { id = "EditEmail" })) {%>
<table class="table">
<tr>
  <td>Email:td>
  <td><%= Html.TextBox("Email")%>td>
tr>
<tr>
  <td>Type:td>
  <td><%= Html.DropDownList("Type") %>td>
tr>
<tr>
  <td><%= Html.Hidden("ContactPersonId") %><%= Html.Hidden("Id") %>td>
  <td><input type="button" name="save" id="save" value="Save" />td>
tr>
table>

 

  上面代码仍然和Add View很像,除了包含EmailAddress.Id的html隐藏域,这是为了保证正确的email地址被更新,而Delete action就不用过多解释了吧。

public ActionResult Delete(int id)
{
  EmailAddressManager.Delete(id);
  return RedirectToAction("List", "Contact");
}

 总结

  这篇练习的目的是为了证明Asp.net MVC在没有Linq To Sql和Entity framework的情况下依然可以很完美的使用。文章使用了现有分层结构良好的Asp.net 2.0 form程序,还有可重用的,business Objects层,bussiness logic层以及Data Access 层。而DAL层使用Ado.net调用Sql Server的存储过程来实现。

  顺带的,我还展示了如何使用强类型的View Models和简洁的jQuery来让UI体验更上一层。这个程序并不是完美的,也不是用于真实世界。程序中View部分,以及混合编辑和删除的action都还有很大的空间可以重构提升,程序还缺少验证用户输入的手段,所有的删除操作都会返回页面本身。而更好的情况应该是显示删除后用一个子表来显示删除后的内容,这需要将ContactPersonId作为参数传递到相关action,这也是很好实现的。

---------------------------------------------------

原文链接:http://www.mikesdotnetting.com/Article/132/ASP.NET-MVC-is-not-all-about-Linq-to-SQL

translated by CareySon

时间: 2024-10-07 10:44:28

一起谈.NET技术,【译】ASP.NET MVC并不仅仅只是Linq to SQL的相关文章

【译】ASP.NET MVC并不仅仅只是Linq to SQL

很多ASP.NET的教程中的示例代码使用的数据访问方法是Linq to Sql或是Entity Framework.我在www.asp.net的论坛上看到很多关于讨论是否有其他替代的数据库访问方式,回答是:当然有.这篇文章就讲述了使用Ado.Net作为数据访问层来实现一个典型的增删查改程序. 由于是以练习作为目的,那我就不妨借用Spaanjaar's 的N层构架文章(Building Layered Web Applications with Microsoft ASP.NET 2.0.)的构架

Asp.net MVC并不仅仅只是Linq to SQL

很多Asp.net的教程中的示例代码使用的数据访问方法是Linq to Sql或是Entity Framework.我在 www.asp.net的论坛上看到很多关于讨论是否有其他替代的数据库访问方式,回答是:当然有.这篇文章 就讲述了使用Ado.Net作为数据访问层来实现一个典型的增删查改程序. 由于是以练习作为目的,那我就不妨借用Spaanjaar's 的N层构架文章(Building Layered Web Applications with Microsoft ASP.NET 2.0.)的

一起谈.NET技术,ASP.NET MVC 2示例Tailspin Travel UI层分析

Tailspin Travel 是一个旅游预订的应用程序示例,最新版本采用ASP.NET MVC 2技术构建,主要使用 DataAnnotations 验证, 客户端验证和ViewModels,还展示了许多Visual Studio 2010, .NET Framework 4, 和Windows Server AppFabric的技术,参看ASP.NET MVC 2示例Tailspin Travel. Tailspin Travel设计的技术比较多,今天我们来看看界面(UI)上的技术,在UI层

一起谈.NET技术,ASP.NET MVC 2中使用jQuery UI控件详解

问:我想给我的ASP.NET MVC输入表单添加一个日期选择控件,但模型-视图-控制器(MVC)并没有提供这样的辅助方法,我该如何添加控件? 答:和ASP.NET Web表单不一样,MVC架构没有提供可以在设计面板中拖放的有状态的服务端控件,相反,MVC鼓励使用简单的HTML布局元素和基于数据的标签作为页面布局的要素,功能和最终的布局用客户端JavaScript和CSS样式表控制. MVC提供了一套基于HtmlHelper的扩展方法渲染大部分HTML标签,对于更复杂的功能,你需要自己编写HTML

一起谈.NET技术,ASP.NET MVC 2生成动态表单的一种最简单的思路

在BPM.OA等系统中,都会存在一个表单设计器.有些是通过操作gridview来完成一个表单的设计:有些是通过类似VS拖拽的方法完成一个表单的设计.很明显后面一种优越于前面一种.无论是哪种,最后都会产生一些XML之类的表单结构的数据. 这篇文章将讲述,在表单设计器设计好表单之后,在ASP.NET MVC中如何将表单结构的xml转换成实际应用系统中的表单.看下面一个xml文件,我们假设它是由一个表单设计器设计出来的. <?xml version="1.0" encoding=&qu

一起谈.NET技术,ASP.NET MVC &amp;amp; EF 构建智能查询 二、模型的设计与ModelBinder

在第一篇中,我讲解了我们要做智能查询的原因,以及基本的解决方案设计.从这篇开始我们开始讲解它的实现过程. 其实在写这一系列文章之初,我其实是想由底至上去讲解,但是我又整理了一遍代码才发现,其实如果不了解最表面的东西,也是不太好深入的. 所以我们的第二篇文章就来讲一下我们这个智能查询框架中最浅,但也是使用最频繁的部分,也就是Model. 首先我们的Entity  或者说数据库的结构如下: 另外如下面代码,我们有一个用于传递name=value对,及查询谓词的model: public Action

一起谈.NET技术,ASP.NET MVC的全球化方案

由于项目需要最近在学习ASP.NET MVC.在实践中,网站要支持多语言,需要全球化.在MVC下我实现了一个全球化框架,在这里与各位分享一下,不足之处也请各位看官指教. 让URL支持全球化 经常上微软网站的朋友可能很熟悉类似包含..\zh-cn\.....\en-us\..的url形式,这就是本文要使用的全球化方案.当然还有使用QueryString传递参数的方案,基本思路我想是类似的. 由于MVC天生的URL路由原理,使得这个方案很容易被接受. 基本思路 这个方案的基本思路是: 1.当用户访问

一起谈.NET技术,ASP.NET MVC 3 概述

原文地址:http://www.asp.net/mvc/mvc3 导言 ASP.NET MVC3 在 ASP.NET MVC 1 和 2 的基础上,增加了大量的特性,使得代码更加简化,并且可以深度扩展.这篇文章提供包含在此次发布中的许多新特性的说明,分为以下部分: Razor 视图引擎 支持多视图引擎 Controller 改进 JavaScript 和 Ajax Model 验证的改进 依赖注入 Dependency Injection 的改进 其他新特性 Razor 视图引擎 ASP.NET

一起谈.NET技术,ASP.NET MVC 3 Beta初体验之实用的WebMail

Asp.net MVC 3 Beta中提供了非常实用发送邮件的组件:WebMail.我试用了一下,和System.Web.Mail类似.这篇文章将简单介绍一下这个组件的使用.通过分成不带附件的邮件发送和带附件的邮件发送两种情况进行讲解.用一个请求帮助的应用场景为例. 不带附件的邮件发送 首先定义Controller.EmailRequest用于请求一个发送邮件的页面,ProcessRequest用去处理发送邮件的请求,并在View中发送邮件. 代码 [HttpGet]public ActionR