ASP.NET MVC并非关于LINQ到SQL

几乎每个示例应用程序,说明ASP.NET MVC使用LINQ到SQL或实体框架作为数据访问方法。一世'已经看到一些发布到论坛的问题 www.asp.net. 询问是否有任何替代方案,确实存在。本文将在数据访问层中使用纯ado.net来提供动态内容到典型的小型CRUD应用程序。

出于本练习的目的,我将从imar spaanjaar借用'S N层设计文章系列在此提供: 使用Microsoft ASP.NET 2.0构建分层Web应用程序。我强烈建议您阅读一系列文章,或者至少前两项熟悉N层方法的基础知识,以熟悉ASP.NET应用程序设计。从该应用程序中的3个关键图层:业务对象,业务逻辑和数据访问将包含在以下MVC应用程序中,只有很少或根本没有更改,文章系列提供了良好的细节,请如何构造图层。本文将查看他们扮演的角色,但不会以任何真实的详细信息进入他们的代码。

首先,我们看一下IMAR提出的应用程序。它's一个简单的,它是一个典型的crud操作。它允许用户与其地址,电话号码和电子邮件地址一起管理联系人。它具有创建,读取,更新和删除任何实体的功能。

与应用程序关联的实体是ContactPersonsonsonsonsonsonsonsonsonsonsonsonsonsonsonsonsonsons,phoneNumbers,地址和EmailAddresses。它们都生活在应用程序的业务对象(BO)层中。这些类中的每一个都包含原始示例中的Getter和Setter方法的公共属性。它们不具有任何行为,该行为在业务逻辑层(BLL)中 实体名称经理课程。  There's在实体及其关联的管理器类之间的一对一映射。每个Manager类都包含检索实体实例,实体集合的方法 保存 实体(更新或添加)并删除实体。该应用程序的此区域也可用于应用验证,安全性等,但不包括在样本中,以便它不是'太混乱了。如果您想在BLL中看到验证等,请看看imar's 6部分系列 该应用程序的功能更新,包括将其移动到ASP.NET框架的3.5版。

最终图层是数据访问层(DAL)。此图层还具有类的类,其中包含BLL中的管理器类与Manager类一起映射。 BLL中的方法实际上在DAL中呼叫相关方法。 DAL方法是应用程序中唯一知道用于持续(存储)实体的机制。在这种情况下,它's一个SQL Server Express数据库,因此这组类利用ADO.NET和SQLClient类。这种方法背后的想法是,如果您需要交换持久性机制(例如,使用XML或Oracle,或者调用Web服务,甚至将Linq或其他ORM调用,甚至是使用LINQ或其他ORM),则只需替换DAL即可。只要新的DAL公开了包含与来自BLL调用相同签名的方法,一切都应该继续工作,而无需更改应用程序的其他领域。通过使用界面可以实现任何新的DAL遵守现有方法签名,但这将是未来文章的主题......也许。

MVC架构

有很多好的文章讨论了MVC应用程序的体系结构,因此这个不会发生严肃的细节。为了更深的样子,我建议访问 了解微软的部分's官方ASP.NET MVC网站。简而言之,但是 M 是模特,这是博,BLL和DAL将居住的地方。这 V 是要查看的,这基本上是任何与UI相关的开发 - 或者用户看到的内容 C 是控制器。控制器协调应用程序响应用户所做的请求。如果用户单击指向特定URL的按钮,则该请求将映射到控制器操作(类方法),该控制器操作将负责处理服务请求所需的任何逻辑,并返回响应 - 通常是新视图,或者对现有视图的更新。

在Visual Studio中创建了一个新的MVC应用程序,并删除了默认视图和控制器,我做的第一件事 是从imar复制bo,bll和dal文件'■在新应用程序的模型区域的应用程序。我还将SQL Server数据库文件从原始站点中的App_data复制到MVC应用程序中的同一个位置,并与style.css文件相同,该文件被复制到MVC内容文件夹中。

我做了一些其他修改。需要将数据库连接字符串添加到MVC应用程序中's web.config文件。此外,我对复制的类文件中的命名空间进行了几个更改,并将一些DAL代码更新为C#3.0,尽管这些修正案都不是严格必需的。一旦我这样做了,我就会点击Ctrl + Shift + F5以确保项目编译。除了一些DAL方法及其关联的BLL方法之外,我不需要从现在开始重新审视这些文件,除了稍后将介绍。

控制器

我添加了四个控制器(删除了Visual Studio提供的默认值) - 每个实体提供一个。这些是ContactController,PhoneController,AddressController和EmailController。

每个控制器都将负责协调4操作 - 列表,添加,编辑和删除。所以我将要做的第一件事是在Global.asax中注册这些操作的路由:

public static void RegisterRoutes(RouteCollection routes)
{
  routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

  routes.MapRoute(
      "Default",                                              
      "{controller}/{action}/{id}",                           
      new { controller = "Contact", action = "列表", id = "" }  
  );
}

应用程序的默认视图将列出联系人。通过BLL中的ContactPersonManager类中的现有GetList()方法获取联系数据,因此列表()操作的代码如下:

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

强烈键入观点

I'M将在整个应用程序中使用强烈键入的视图,因为它们提供了视图中的IntelliSense和Don't依赖于字符串的ViewData索引,这容易出错。要在我继续时整理事情,我已经添加了一些名称空间(默认值下方的默认值)到Web.config<namespaces>部分。这些是我用于替换IMAR中现有名称空间的那些's code:

    

<名称空间>
<add namespace="System.Web.Mvc"/>
<add namespace="System.Web.Mvc.Ajax"/>
<add namespace="System.Web.Mvc.Html"/>
<add namespace="System.Web.Routing"/>
<add namespace="System.Linq"/>
<add namespace="System.Collections.Generic"/>
<add namespace="ContactManagerMVC.Views.ViewModels"/>
<add namespace="ContactManagerMVC.Models.BusinessObject"/>
<add namespace="ContactManagerMVC.Models.BusinessObject.Collections"/>
<add namespace="ContactManagerMVC.Models.BusinessLogic"/>
<add namespace="ContactManagerMVC.Models.DataAccess"/>
<add namespace="ContactManagerMVC.Models.Enums"/>
</名称空间>

这意味着它们可供所有应用程序使用,并且我不需要在视图中完全限定类型。 getList()方法返回的类型是ContactPersonlist,它在Bo图层中的集合文件夹中设置。它只是联系人对象的集合。列表视图顶部的页面声明如下:

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


您还会注意到我已包含一个母版页。在主页上,我引用了imar的css文件's示例代码。管理联系人对象显示的标记如下:

  

<table class="table">
    <tr>
      <th scope="col">Id</th>
      <th scope="col">Full Name</th>
      <th scope="col">Date of Birth</th>
      <th scope="col">Type</th>
      <th scope="col">&nbsp;</th>
      <th scope="col">&nbsp;</th>
      <th scope="col">&nbsp;</th>
      <th scope="col">&nbsp;</th>
      <th scope="col">&nbsp;</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="关联">Addresses</td>
      <td title="email/list" class="关联">Email</td>
      <td title="phone/list" class="关联">Phone Numbers</td>
      <td title="contact/edit" class="关联">Edit</td>
      <td title="contact/delete" class="关联">Delete</td>
    </tr>
    <%
        }
      }else{%>
    <tr>
      <td colspan="9">No Contacts Yet</td>
    </tr>  
     <% }%>
  </table>

您可以立即看到在视图中强有力打字的好处。该模型是ContactedPersonlist类型,因此每个项目是ContactPranson,它们的属性可提供,而无需将模型转换为ContactPersonlist。仅在运行时检测到不正确的铸件,这可能是不方便的。

我用HTML欺骗了一点。我本可以选择"List"从“视图内容”选项生成视图时,它将提供一些默认的模板HTML。我没有'T。我想要一些与imar合作的东西'S CSS更容易,所以我在我的机器上运行了他的应用程序,一旦它在浏览器中,就会查看源并从中复制呈现的HTML。 IMAR在他的Web表单应用程序中使用GridViews,因此在渲染时会自动嵌入到源中的一些CSS等。我清理了这个并将表格移动到名为CSS文件的CSS类。我还为此添加了一些额外的样式<th> and <td>元素。您可以在随附的下载中看到它们。

我还向某些表格中添加了一些标题属性。这些是包含原始应用程序内的其他操作的链接的链接。我决定在查看地址或电话号码时或者当用户想要编辑或删除现有记录时,我不希望整个页面发布。我希望该网站成为Ajax启用。这些标题属性在AJAX(jQuery提供)中播放关键角色,该功能在我的版本中的功能。这"link"CSS类使文本与超链接相同,因为它带下划线,光标转向指针(手)onMouseOver。

jquery ajax.

在查看管理Ajax功能的主要批量脚本之前,这里有三行被添加到列表视图底部的HTML行:

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

第一个是允许用户添加新联系人的按钮。第二个是一个空的div,它正在等待内容,第三个是jQuery模态确认框的一部分,提示用户确认他们想要删除记录。

母版页中的3个文件。一个是主jQuery文件,另外两个来自jQuery.ui库。这些都适用于模态对话框和日期选择器:

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

这里'呈现渲染视图的列表视图的完整jQuery,如下所示:

<script type="text/javascript">
  $(function() {
    // row colours
    $('tr:even').css('background-color', '#EFF3FB');
    $('tr:odd').css('background-color', '#FFFFFF');
    // selected row managment
    $('tr').click(function() {
      $('tr').each(function() {
        $(this).removeClass('SelectedRowStyle');
      });
      $(this).addClass('SelectedRowStyle');
    }); 
    // hide the dialog div
    $('#dialog').hide();
    // set up ajax to prevent caching of results in IE
    $.ajaxSetup({ cache: false });
    // add an onclick handler to items with the "关联" css class
    $('.link').live('click', function(event) {
      var id = $.trim($('td:first', $(this).parents('tr')).text());
      var loc = $(this).attr('title');
      // check to ensure the link is not a delete link
      if (loc.lastIndexOf('delete') == -1) {
        $.get(loc + '/' + id, function(data) {
          $('#details').html(data);
        });
      // if it is, show the modal dialog   
      } else {
        $('#dialog').dialog({
          buttons: {
            'Confirm': function() {
              window.location.href = loc + '/' + id;
            },
            'Cancel': function() {
              $(this).dialog('close');
            }
          }
        }); 
        $('#dialog').dialog('open');
        }
      }); 
      // add an onclick event handler to the add contact button
      $('#addContact').click(function() {
        $.get('Contact/Add', function(data) {
          $('#details').html(data);
        });
      }); 
    });
</script>

这可能看起来令人生畏,但与所有的东西都是jQuery,它很简单,我已经通过使用注释打破了各种部分,以便更容易遵循。脚本所做的第一件事是替换Web表单在服务器上提供给数据演示控件的替代数据管理。一旦渲染表,它将CSS样式运行到行。然后我添加了一些额外的代码来满足IMAR的事实'原始示例包括突出显示当前所选行的方式。然后我添加了一行来防止即缓存结果。如果你不'要这样做,你会划伤你的头想知道为什么编辑和删除在浏览器中没有出现在数据库中的记录中,而在数据库中已经清楚地更改。

下一点是有趣的。它利用了 。居住() 方法,可确保事件处理程序附加到所有匹配元素,无论它们是否存在。当用户单击地址链接时,结果是列出相关电话号码的另一个表:

您可以看到表包含编辑和删除链接。如果我没有't使用.live(),这些没有附加事件处理程序。事件处理程序附加到由此装饰的单元格的点击棋子"link"班级。它首先获得记录的ID的值。在联系人表的情况下,这将是ContactPersonid(它'■表行中的第一单元的内容)。在子表单中,它将是电话号码的ID或电子邮件地址。传递给负责编辑,删除或显示的控制器操作时需要这些。更多的是。

 您现在还应该了解为什么我已将标题属性添加到表格单元格。它们包含需要调用的路由,并且通过将ID附加到路由来构建完整的URL。接下来,进行检查,看看路线是否包含单词"delete"。如果不是,则解雇请求,结果显示在详细信息DIV中。如果是删除链接,则在删除记录之前调用模态确认框,使用户可以选择更改主意。看?我说这很简单!

最后,将一个事件处理程序连接到“添加联系人”按钮的Click事件中。我们将接下来看看这个部分。

添加联系人和自定义视图模型

在ASP.NET应用程序中添加记录时,惯常做法是向用户展示包含一系列输入的表单,它们可以通过它提供值。 ContactPerson对象的大多数输入都是简单的:名字,姓氏,出生日期。其中一个并不是那么简单的类型。必须从模型内枚举文件夹中的persontype.cs(朋友,同事等)中定义的枚举中绘制此值。这意味着我们必须呈现用户有限选择的有效值。下拉列表将完成这项工作。但是,一系列可选值表明了现有的ContactPerson对象的一部分,因此我们需要向要容纳此操作的ContactPerson提供定制版本。这是自定义视图模型进入的地方。

I'VE阅读了一些辩论,其中应该在应用程序内放置在视图模型的位置。有些人觉得它们是模型的一部分。我的看法是他们与意见有关。它们只是与MVC应用程序的相关性,并且不是真的可重复使用,因此他们不应该进入模型。因此,我选择创建ViewModels文件夹并在视图文件夹下添加。 为此,我添加了带有以下代码的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<SelectListItem..> Type { get; set; }
  }
}

查看最后一个属性,您可以看到我制作了类型的IEnumerable集合<SelectListItem>。这将绑定到视图中的HTML.DropdownList。

在控制器内添加两个动作。第一个是用接受者(httpverbs.get)属性装饰,而第二个是标有接受者(httpverbs.post)属性的。第一个负责将数据输入表单传递回复数据,而第二个处理表单提交时发布的值:

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Add()
{
  var personTypes = Enum.GetValues(typeof (PersonType))
    .Cast<PersonType>()
    .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("列表");
}

第一个操作中的前几行负责将ContactType枚举中的值传送到阵列,每个项目都被投射到具有ID属性和名称属性的匿名对象。 ID是枚举值,名称是与枚举一起使用的常量值。实例化了ContactPersonViewModel对象,类型属性与数据值值以及数据文本值传递给SelectList对象的IEnumous。我使用局部视图来添加联系人,再次选择强类型的选项并选择ToptedPersonViewModel进行类型。部分的代码如下:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ContactPersonViewModel>" %>

<script type="text/javascript">
  $(function() {
  $('#DateOfBirth').datepicker({ dateFormat: 'yy/mm/dd' });
  });
</script>

<% using (Html.BeginForm("Add", "Contact", FormMethod.Post)) {%>
      <table>
        <tr>
          <td class="LabelCell">Name</td>
          <td><%= Html.TextBox("FirstName") %></td>
        </tr>
        <tr>
          <td class="LabelCell">Middle Name</td>
          <td><%= Html.TextBox("MiddleName") %></td>
        </tr>
        <tr>v
          <td class="LabelCell">Last Name</td>
          <td><%= Html.TextBox("LastName") %></td>
        </tr>
        <tr>
          <td class="LabelCell">Date of Birth</td>
          <td><%= Html.TextBox("DateOfBirth", String.Empty)%></td>
        </tr>
        <tr>
          <td class="LabelCell">Type</td>
          <td><%=Html.DropDownList("Type")%>
          </td>
        </tr>
        <tr>
          <td class="LabelCell"></td>
          <td><input type="提交 " name="提交 " id="提交 " value="Save" /></td>
        </tr>
      </table>
<% } %>

顶部的jQuery将jQuery UI日期选择器(日历)附加到DateObitraTh TextBox。传递给DatyOfbirth Text Box的HTML帮助程序的第二个参数可确保默认情况下,该值为空。否则,所有输入都赋予与我们要填充的相应的ContactPerson属性相同的名称。这是为了确保默认模型粘合剂为我们工作,没有任何不必要的乱。 ContactType的枚举也由MVC自动绑定:

然后,响应POST请求的方法是能够将传入的Request.form值与ContactPerson对象上的属性匹配,然后调用具有最小代码的关联的BLL保存()方法。这是响应于对列表操作的redirectoation调用而刷新页面刷新。

编辑ContactPerson.

同样,控制器上有两个动作用于编辑:一个用于初始GET请求,另一个用于提交的另一个:

[AcceptVerbs(HttpVerbs.Get)]
    public ActionResult Edit(int id)
    {
      var personTypes = Enum.GetValues(typeof (PersonType))
        .Cast<PersonType>()
        .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("列表");
    }

We'已经看到jQuery如何用于调用通过要编辑的人的ID的编辑操作。该ID用于通过现在熟悉的BLL调用DAL方法从数据库中检索人员详细信息。 ContactPersonViewModel构造成返回的数据,并在添加方法中添加SelectList。但是,虽然,SelectList构造函数具有第三个参数,这是此人的实际类型。构造函数中的第三个参数是所选值。

部分视图代码再次与Add View相同:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ContactPersonViewModel>" %>

<script type="text/javascript">
  $(function() {
    $('#DateOfBirth').datepicker({dateFormat: 'yy/mm/dd'});
  });
</script>

<% using (Html.BeginForm("Edit", "Contact", FormMethod.Post)) {%> 
     <table>
        <tr>
          <td class="LabelCell">Name</td>
          <td><%= Html.TextBox("FirstName") %></td>
        </tr>
        <tr>
          <td class="LabelCell">Middle Name</td>
          <td><%= Html.TextBox("MiddleName") %></td>
        </tr>
        <tr>
          <td class="LabelCell">Last Name</td>
          <td><%= Html.TextBox("LastName") %></td>
        </tr>
        <tr>
          <td class="LabelCell">Date of Birth</td>
          <td><%= Html.TextBox("DateOfBirth", Model.DateOfBirth.ToString("yyyy/MM/dd")) %></td>
        </tr>
        <tr>
          <td class="LabelCell">Type</td>
          <td><%= Html.DropDownList("Type")%></td>
        </tr>
        <tr>
          <td class="LabelCell"><%= Html.Hidden("Id") %></td>
          <td><input type="提交 " name="提交 " id="提交 " value="Save" /></td>
        </tr>
      </table>
<% } %>

关键差异是DateOfbitration字段现在包含以友好方式显示日期的格式字符串。添加到该提交按钮附近的html.hiddon()辅助者,其具有编辑人员的值。哦,当然,表格是针对添加表格的不同行动。可能有一个有关组合添加和编辑表单和操作的论据,并使用令牌检测表单所在的模式(添加或编辑),以便在组合的局部视图中有条件地设置一些标记,并控制组合行动中的代码。这将减少相当数量的重复。为了清楚起见,我已经在这个样本中分开了它们。

删除联系人

删除动作很简单,并且没有任何视图相关联的WTIH。没有'需要。刚返回列表操作将刷新数据:

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

这是我选择改变原始BLL和DAL的地方。原始ContactPersonManager.delete()方法采用要删除的人的实例。在DAL DELETE()方法中,仅引用(或需要)的人的ID。当您通过其唯一ID引用它们时,我认为在整个对象中没有进入任何点,所以我修改了接受int的方法。它还使得代码更容易,因为我必须立即实例化ContactPerson对象以摆脱它。

jQuery在单击删除链接时调用“确认模态”对话框。

如果单击“取消”按钮,则不会发生任何内容(除了模态关闭)。如果单击了“确认”按钮,则会调用从jQuery构造的URL指向删除操作。

管理集合

所有集合 - Phonenumberlist,EmailAddressList和地址列表都以完全相同的方式管理。因此,我只能选择一个(EmailAddressList)来说明方法。您可以检查下载以确切地查看其他人的工作。

首先,我们将查看显示与所选联系人关联的电子邮件地址。这涉及控制器上的列表操作:

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

该方法采用触点的ID(来自行中的第一个单元格'如果您记得)并返回另一个自定义视图模型 - EmailAddressListViewModel。这是为了使联系人的ID可以传递到视图:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<EmailAddressListViewModel>" %>
<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 Id</th>
     <th scope="col">Email</th>
     <th scope="col">Type</th>
     <th scope="col">&nbsp;</th>
     <th scope="col">&nbsp;</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="关联">Edit</td>
     <td title="email/delete" class="关联">Delete</td>
   </tr>
        <%}
    }else
 {%>
   <tr>
     <td colspan="9">No email addresses for this contact</td>
   </tr>
 <%}%>
</table>
<input type="button" name="add" value="Add Email" id="add" />

您可以看到添加方法需要ContactPersonID。我们需要确保我们在正确的联系人添加新项目 'S馆藏。否则,编辑和删除方法在完全相同的情况下为ContactPerson本身工作 - 要修改或删除的项目的ID通过URL传递,并且表格单元格与标题属性设置,以便它们可以利用前面部署的.live()方法在列表视图中为联系人查看。

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Add(int id)
{
  var contactTypes = Enum.GetValues(typeof(ContactType))
    .Cast<ContactType>()
    .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("列表", new {id = emailAddress.ContactPersonId});
}

为此目的创建了一个自定义视图模型,以便显示用于编辑和添加的现有EmailAddress对象。这包括与绑定IEnumerable的相同类型的财产<SelectListItem>收集类型下拉列表。这些方法与他们的联系人同行不同的情况下,他们返回的内容。第一个将添加表单作为部分返回,而第二个重定向到同一控制器上的列表操作,这导致修改后的收集正在更新(也是No-Cache选项在原始AJAX中设置的原因) 。

在收集项的情况下,每个在保存之前的操作中都将其ID设置为-1。这是为了确保正确的代码流程控制"Upsert"存储过程。默认情况下,默认情况下,从Routedata ID参数(属于ContactPerson)拾取一个值,因此如果未设置为-1,则更新任何具有与当前联系人相同的ID的EaumbAddress对象。这是因为imar雇用了"Upsert"在一个过程中添加和编辑数据的方法。通过审查他的文章,可以在此处找到更多信息。与此同时,以下是添加电子邮件地址的局部视图:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<EmailAddressViewModel>" %>

<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="保存 " id="保存 " value="Save" /></td>
</tr>
</table>
<% } %>

此特定实例中的jQuery负责通过AJAX提交表单。它附加到HTML按钮(不是输入类型="submit", 你发现)。它序列化了表单字段的内容,并影响了由适当的Accessverbs属性装饰的Add()操作的POST请求。

编辑和删除EmailAddress对象

编辑EmainAddress obejcts涉及与之前已显示的操作和视图非常相似。控制器上还有两个动作 - 一个用于Get和Post一个:

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Edit(int id)
{
  var emailAddress = EmailAddressManager.GetItem(id);
  var contactTypes = Enum.GetValues(typeof(ContactType))
    .Cast<ContactType>()
    .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("列表", "Email", new { id = emailAddress.ContactPersonId });
}

编辑的局部视图也应该非常熟悉:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<EmailAddressViewModel>" %>

<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="保存 " id="保存 " value="Save" /></td>
</tr>
</table>
<% } %>

除了添加实际EmailAddress.id值的添加隐藏字段之外,这几乎与Add View相同,这确保了正确的电子邮件地址已更新。删除操作不需要真正的解释:

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

概括

本练习的对象是为了证明MVC应用程序在没有LINQ到SQL或实体框架的情况下完全可能。我采取了一个现有的ASP.NET 2.0 Web表单应用程序,这些应用程序很好地分层,并重新使用了业务对象,业务逻辑和数据访问层,几乎没有修改。 DAL仍然使用ADO.NET并调用对SQL Server数据库的存储过程。

一路上,我已经显示了如何使用强类型的视图模型和一点natty jquery来平滑用户的UI体验。它'没有任何方法的完美申请,绝对不是为现实世界做好准备。重构“添加和编辑”操作的视图和操作方面有很多改进的空间。该应用程序缺乏任何类型的验证。删除操作一切都会导致再次显示打开页面,而在删除电话中的项目,电子邮件或地址集合中将更好地更好地重新显示修改后的子表单。这需要在联系人传递到行动,并且应该相对容易实现。

下载代码