ASP.NET MVC并不仅仅只是LINQ To SQL

开发 后端
很多ASP.NET的教程中的示例代码使用的数据访问方法是Linq to SQL或是Entity Framework,本文就讲述了使用ADO.NET作为数据访问层来实现一个典型的增删查改程序,这里只是讲述各个层所扮演的角色,但是不会深入到代码的细节中。

ASP.NET MVC是微软官方提供的MVC模式编写ASP.NET Web应用程序的一个框架,不久前微软发布了ASP.NET MVC 2 RC 2新版本增强了对HTML的控制,同时对Area特性也有一定的增强,而在不久之后也将会支持Visual Studio 2010

这里我们只是讲述各个层所扮演的角色,但是不会深入到代码的细节中。N-Layer构架的三个关键层分别为:业务对象层,业务逻辑层和数据访问层。而其数据访问层会几乎不加改变的包含在本文的MVC项目中,Spaanjaar的文件详细描述了各个层是如何组织的。

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

Imar提供的程序

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

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

MVC构架

简单的说,M代表Model,也是包含BO,BLL,DAL的地方,V代表View,也是UI相关开发的部分,或者说是用户看到的部分,C是Controller的简写,也是控制用户请求与程序回复的部分。如果用户点击了一个指向特定地址的按钮,请求会和Controller的Action(类的方法)进行匹配,而Action负责处理请求,并返回响应。通常情况下是一个新的View,或者是更新现有的View。

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

创建一个MVC应用程序

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

Controller

添加4个Controller(VisualStudio附带的默认Controller已经被删除),和四个实体类相匹配。它们分别为:ContactController,PhoneController,AddressControllerandEmailController。

四个实体类

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

  1. public static void RegisterRoutes(RouteCollection routes)  
  2. {  
  3.   routes.IgnoreRoute("{resource}.axd/{*pathInfo}");  
  4.  
  5.   routes.MapRoute(  
  6.       "Default",                                                
  7.       "{controller}/{action}/{id}",                             
  8.       new { controller = "Contact"action = "List"id = "" }    
  9.   );  

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

  1. public ActionResult List()  
  2. {  
  3.   var model = ContactPersonManager.GetList();  
  4.   return View(model);  

#p#

强类型View

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

  1. <namespaces> 
  2. <add namespace="System.Web.Mvc"/> 
  3. <add namespace="System.Web.Mvc.Ajax"/> 
  4. <add namespace="System.Web.Mvc.Html"/> 
  5. <add namespace="System.Web.Routing"/> 
  6. <add namespace="System.Linq"/> 
  7. <add namespace="System.Collections.Generic"/> 
  8. <add namespace="ContactManagerMVC.Views.ViewModels"/> 
  9. <add namespace="ContactManagerMVC.Models.BusinessObject"/> 
  10. <add namespace="ContactManagerMVC.Models.BusinessObject.Collections"/> 
  11. <add namespace="ContactManagerMVC.Models.BusinessLogic"/> 
  12. <add namespace="ContactManagerMVC.Models.DataAccess"/> 
  13. <add namespace="ContactManagerMVC.Models.Enums"/> 
  14. </namespaces> 

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

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

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

  1. <table class="table"> 
  2.     <tr> 
  3.       <th scope="col">Id</th> 
  4.       <th scope="col">Full Name</th> 
  5.       <th scope="col">Date of Birth</th> 
  6.       <th scope="col">Type</th> 
  7.       <th scope="col">&nbsp;</th> 
  8.       <th scope="col">&nbsp;</th> 
  9.       <th scope="col">&nbsp;</th> 
  10.       <th scope="col">&nbsp;</th> 
  11.       <th scope="col">&nbsp;</th>    
  12.     </tr> 
  13.     <%  
  14.       if (Model != null)  
  15.       {  
  16.         foreach (var person in Model)  
  17.         {%> 
  18.     <tr> 
  19.       <td><%= person.Id %></td> 
  20.       <td><%= person.FullName %></td> 
  21.       <td><%= person.DateOfBirth.ToString("d") %></td> 
  22.       <td><%= person.Type %></td> 
  23.       <td title="address/list" class="link">Addresses</td> 
  24.       <td title="email/list" class="link">Email</td> 
  25.       <td title="phone/list" class="link">Phone Numbers</td> 
  26.       <td title="contact/edit" class="link">Edit</td> 
  27.       <td title="contact/delete" class="link">Delete</td> 
  28.     </tr> 
  29.     <%  
  30.         }  
  31.       }else{%> 
  32.     <tr> 
  33.       <td colspan="9">No Contacts Yet</td> 
  34.     </tr>    
  35.      <% }%> 
  36.   </table> 

 

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

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

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

JQuery Ajax

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

  1. <input type="button" id="addContact" name="addContact" value="Add Contact" /> 
  2. <div id="details"></div> 
  3. <div id="dialog" title="Confirmation Required">Are you sure about this?</div> 

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

还需要在页面中添加3个js文件,第一个是主要的jQuery文件,其他两个分别是jQueryUI的核心js,以及datepicker和modaldialog部分的JS:

  1. <script src="../../Scripts/jquery-1.3.2.min.js" type="text/javascript"></script> 
  2. <script src="../../Scripts/ui.core.min.js" type="text/javascript"></script> 
  3. <script src="../../Scripts/jquery-ui.min.js" type="text/javascript"></script> 

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

列表显示视图以及完全的JS代码

 

  1. <script type="text/javascript"> 
  2.   $(function() {  
  3.     // row colours  
  4.     $('tr:even').css('background-color', '#EFF3FB');  
  5.     $('tr:odd').css('background-color', '#FFFFFF');  
  6.     // selected row managment  
  7.     $('tr').click(function() {  
  8.       $('tr').each(function() {  
  9.         $(this).removeClass('SelectedRowStyle');  
  10.       });  
  11.       $(this).addClass('SelectedRowStyle');  
  12.     });   
  13.     // hide the dialog div  
  14.     $('#dialog').hide();  
  15.     // set up ajax to prevent caching of results in IE  
  16.     $.ajaxSetup({ cache: false });  
  17.     // add an onclick handler to items with the "link" css class  
  18.     $('.link').live('click', function(event) {  
  19.       var id = $.trim($('td:first', $(this).parents('tr')).text());  
  20.       var loc = $(this).attr('title');  
  21.       // check to ensure the link is not a delete link  
  22.       if (loc.lastIndexOf('delete') == -1) {  
  23.         $.get(loc + '/' + id, function(data) {  
  24.           $('#details').html(data);  
  25.         });  
  26.       // if it is, show the modal dialog     
  27.       } else {  
  28.         $('#dialog').dialog({  
  29.           buttons: {  
  30.             'Confirm': function() {  
  31.               window.location.href = loc + '/' + id;  
  32.             },  
  33.             'Cancel': function() {  
  34.               $(this).dialog('close');  
  35.             }  
  36.           }  
  37.         });   
  38.         $('#dialog').dialog('open');  
  39.         }  
  40.       });   
  41.       // add an onclick event handler to the add contact button  
  42.       $('#addContact').click(function() {  
  43.         $.get('Contact/Add', function(data) {  
  44.           $('#details').html(data);  
  45.         });  
  46.       });   
  47.     });  
  48. </script> 

 

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

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

点击Addresses链接的结果

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

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

添加联系人以及自定义ViewModels

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

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

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Web.Mvc;  
  4.  
  5. namespace ContactManagerMVC.Views.ViewModels  
  6. {  
  7.   public class ContactPersonViewModel  
  8.   {  
  9.     public int Id { get; set; }  
  10.     public string FirstName { get; set; }  
  11.     public string MiddleName { get; set; }  
  12.     public string LastName { get; set; }  
  13.     public DateTime DateOfBirth { get; set; }  
  14.     public IEnumerable<SelectListItem> Type { get; set; }  
  15.   }  

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

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

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

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

自动为ContactType枚举进行绑定

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

编辑一个联系人

我们在前面已经看到jQuery是如何调用Edit这个action并把被编辑人的id作为参数进行传递的了,然后id用于通过众所周知的bll调用DAL从数据库将联系人的详细信息取出来。

  1. <script type="text/javascript"> 
  2.   $(function() {  
  3.     $('#DateOfBirth').datepicker({dateFormat: 'yy/mm/dd'});  
  4.   });  
  5. </script> 
  6.  
  7. <% using (Html.BeginForm("Edit", "Contact", FormMethod.Post)) {%>   
  8.      <table> 
  9.         <tr> 
  10.           <td class="LabelCell">Name</td> 
  11.           <td><%= Html.TextBox("FirstName") %></td> 
  12.         </tr> 
  13.         <tr> 
  14.           <td class="LabelCell">Middle Name</td> 
  15.           <td><%= Html.TextBox("MiddleName") %></td> 
  16.         </tr> 
  17.         <tr> 
  18.           <td class="LabelCell">Last Name</td> 
  19.           <td><%= Html.TextBox("LastName") %></td> 
  20.         </tr> 
  21.         <tr> 
  22.           <td class="LabelCell">Date of Birth</td> 
  23.           <td><%= Html.TextBox("DateOfBirth", Model.DateOfBirth.ToString("yyyy/MM/dd")) %></td> 
  24.         </tr> 
  25.         <tr> 
  26.           <td class="LabelCell">Type</td> 
  27.           <td><%= Html.DropDownList("Type")%></td> 
  28.         </tr> 
  29.         <tr> 
  30.           <td class="LabelCell"><%= Html.Hidden("Id") %></td> 
  31.           <td><input type="submit" name="submit" id="submit" value="Save" /></td> 
  32.         </tr> 
  33.       </table> 
  34. <% } %> 

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

删除一个联系人

删除action就简单多了,并且不需要与之相关的View.仅仅是重新调用List这个action,被删除的数据就不复存在了。上面代码是我对BLL和DAL做出改变的地方,原来的ContactPersonManager.Delete()方法的参数是Person的实例,而在DAL中,只有id作为参数。我看不出传递整个对象给BLL,但BLL却只传递对象的唯一ID给DAL的意义所在,所以我将BLL的代码改成接受int类型的参数,这样写起来会更简单。当删除链接被点击时,调用jQuery的confirmationmodaldialog:

调用jQuery的confirmationmodaldialog

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

管理集合

所有的集合--PhoneNumberList,EmailAddressLIst,AddressList被管理的方式如出一辙。所以,我仅仅挑选EmailAddressLIst作为示例来说明方法,你可以下载示例代码来看其他部分。上面方法使用联系人的id作为联系人,并且返回一个自定义ViewModel—EmailAddressListViewModel.这也是将联系人的id传到View的方法:

  1. <script type="text/javascript"> 
  2.   $(function() {  
  3.     $('#add').click(function() {  
  4.       $.get('Email/Add/<%= Model.ContactPersonId %>', function(data) {  
  5.         $('#details').html(data);  
  6.       });  
  7.     });  
  8.   });  
  9. </script> 
  10. <table class="table"> 
  11.    <tr> 
  12.      <th scope="col">Contact Person Id</th> 
  13.      <th scope="col">Email</th> 
  14.      <th scope="col">Type</th> 
  15.      <th scope="col">&nbsp;</th> 
  16.      <th scope="col">&nbsp;</th> 
  17.    </tr> 
  18.    <%if(Model.EmailAddresses != null)  
  19.      {foreach (var email in Model.EmailAddresses) {%> 
  20.    <tr> 
  21.      <td><%= email.Id %></td> 
  22.      <td><%= email.Email %></td> 
  23.      <td><%= email.Type %></td> 
  24.      <td title="email/edit" class="link">Edit</td> 
  25.      <td title="email/delete" class="link">Delete</td> 
  26.    </tr> 
  27.         <%}  
  28.     }else  
  29.  {%> 
  30.    <tr> 
  31.      <td colspan="9">No email addresses for this contact</td> 
  32.    </tr> 
  33.  <%}%> 
  34. </table> 
  35. <input type="button" name="add" value="Add Email" id="add" /> 

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

  1. [AcceptVerbs(HttpVerbs.Get)]  
  2. public ActionResult Add(int id)  
  3. {  
  4.   var contactTypes = Enum.GetValues(typeof(ContactType))  
  5.     .Cast<ContactType>()  
  6.     .Select(c => new  
  7.     {  
  8.       Id = c,  
  9.       Name = c.ToString()  
  10.     });  
  11.   var model = new EmailAddressViewModel  
  12.                 {  
  13.                   ContactPersonId = id,  
  14.                   Type = new SelectList(contactTypes, "ID", "Name")  
  15.                 };  
  16.   return PartialView("Add", model);  
  17. }  
  18.  
  19.  
  20. [AcceptVerbs(HttpVerbs.Post)]  
  21. public ActionResult Add(EmailAddress emailAddress)  
  22. {  
  23.   emailAddress.Id = -1;  
  24.   EmailAddressManager.Save(emailAddress);  
  25.   return RedirectToAction("List", new {id = emailAddress.ContactPersonId});  

创建自定义ViewModel存在的目的是为了对现有的EmailAddress进行添加或编辑,这包括一些绑定IEnumerable<SelectListItem>集合到Typedropdown的属性,上面两个Add不同之处在于他们的返回不同,第一个返回partialview,第二个返回List这个action。

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

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

编辑和删除EmailAddress对象

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

  1. AcceptVerbs(HttpVerbs.Get)]  
  2. public ActionResult Edit(int id)  
  3. {  
  4.   var emailAddress = EmailAddressManager.GetItem(id);  
  5.   var contactTypes = Enum.GetValues(typeof(ContactType))  
  6.     .Cast<ContactType>()  
  7.     .Select(c => new  
  8.     {  
  9.       Id = c,  
  10.       Name = c.ToString()  
  11.     });  
  12.   var model = new EmailAddressViewModel  
  13.   {  
  14.     Type = new SelectList(contactTypes, "ID", "Name", emailAddress.Type),  
  15.     Email = emailAddress.Email,  
  16.     ContactPersonId = emailAddress.ContactPersonId,  
  17.     Id = emailAddress.Id  
  18.   };  
  19.   return View(model);  
  20. }  
  21.  
  22. [AcceptVerbs(HttpVerbs.Post)]  
  23. public ActionResult Edit(EmailAddress emailAddress)  
  24. {  
  25.   EmailAddressManager.Save(emailAddress);  
  26.   return RedirectToAction("List", "Email", new { id = emailAddress.ContactPersonId });  

 

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

  1. <script type="text/javascript"> 
  2.   $(function() {  
  3.     $('#save').click(function() {  
  4.       $.ajax({  
  5.         type: "POST",  
  6.         url: $("#EditEmail").attr('action'),  
  7.         data: $("#EditEmail").serialize(),  
  8.         dataType: "text/plain",  
  9.         success: function(response) {  
  10.           $("#details").html(response);  
  11.         }  
  12.       });  
  13.     });  
  14.   });  
  15. </script> 
  16.  
  17. <% using(Html.BeginForm("Edit", "Email", FormMethod.Post, new { id = "EditEmail" })) {%> 
  18. <table class="table"> 
  19. <tr> 
  20.   <td>Email:</td> 
  21.   <td><%= Html.TextBox("Email")%></td> 
  22. </tr> 
  23. <tr> 
  24.   <td>Type:</td> 
  25.   <td><%= Html.DropDownList("Type") %></td> 
  26. </tr> 
  27. <tr> 
  28.   <td><%= Html.Hidden("ContactPersonId") %><%= Html.Hidden("Id") %></td> 
  29.   <td><input type="button" name="save" id="save" value="Save" /></td> 
  30. </tr> 
  31. </table> 

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

  1. public ActionResult Delete(int id)  
  2. {  
  3.   EmailAddressManager.Delete(id);  
  4.   return RedirectToAction("List", "Contact");  

总结

这篇练习的目的是为了证明ASP.NET MVC在没有Linq To SQL和Entity framework的情况下依然可以很完美的使用。文章使用了现有分层结构良好的ASP.NET 2.0 form程序,还有可重用的businessObjects层,bussinesslogic层以及DataAccess层。而DAL层使用ADO.NET调用SQL Server的存储过程来实现。

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

文章转自CareySon的博客,

原文地址:http://www.cnblogs.com/CareySon/archive/2010/03/03/1676891.html
 
 【编辑推荐】

  1. 浅谈ASP.NET MVC中TempData的实现机制
  2. 详解ASP.NET MVC 2中的Area特性
  3. 详解ASP.NET MVC 2中强类型HTML辅助方法
  4. 浅析ASP.NET MVC中Controller与View数据传递
  5. 图解ASP.NET MVC与WebForm的区别
责任编辑:王晓东 来源: 博客园
相关推荐

2019-03-01 10:42:10

折叠手机iPhone智能手机

2023-06-06 07:42:10

参数类型函数

2017-09-28 17:20:17

荣之联DataZoohadoop

2016-05-13 19:05:24

2013-08-21 10:04:32

大数据Gartner

2009-11-28 20:08:20

谷歌ChromeWeb应用

2018-07-06 15:07:17

2011-04-13 13:49:50

ASP.NET网站优化

2011-02-13 09:17:02

ASP.NET

2009-09-10 14:02:08

LINQ ASP.NE

2009-07-31 12:43:59

ASP.NET MVC

2009-07-24 13:20:44

MVC框架ASP.NET

2016-05-10 10:15:54

2020-01-15 06:00:52

物联网IOT大数据

2013-07-04 15:22:46

华为WLAN接入

2009-07-23 14:31:20

ASP.NET MVC

2009-07-20 10:53:59

ASP.NET MVC

2009-07-23 15:44:39

ASP.NET MVC

2009-07-22 10:09:59

ASP.NET MVC

2009-07-22 13:24:24

ASP.NET MVC
点赞
收藏

51CTO技术栈公众号