快速回发和模型绑定






4.86/5 (28投票s)
在回传时更快地保存数据,并简化 ASP.NET 控件的模型绑定
引言
当 ASP.NET MVC 问世时,许多开发人员之所以转向 MVC,是因为他们看到了 ASP.NET Web Form 架构中的两个主要缺陷。
- 可测试性
- 回传(Postback)模型
虽然像 ViewState
、静态控件 ID 等问题很容易克服,但回传模型和可测试性却很难替代。在本文中,我不想讨论关注点分离(separation of concerns)的问题。
本文为您提供了一个简单的变通方法,通过使用通用处理程序(Generic Handler)而不是将数据回传到同一页面,您可以省略所有不必要的生命周期事件。同时,在首次加载或需要时,您仍然能够保留页面生命周期的美妙和灵活性。在这种方法中,您无需放弃任何旨在为您提供巨大灵活性和快速应用开发(Rapid Application Development)的丰富的服务器端控件。
背景
本文假设您对 ASP.NET 页面生命周期和回传有基本的了解。您还应该对通用处理程序有一点了解。您可能希望通过以下文章来温习您的知识:
当前的方法
假设您正在开发一个表单,需要用户填写员工数据并提交。当表单提交时,默认情况下,它会提交到同一页面。这时,会发生以下事情:
- 所有控件被初始化,并将默认数据填充到每个控件中。
- 填充控件的数据,如下拉列表等。如果您不手动填充,这些数据可能会由
ViewState
维护。好吧,为了让本文专注于我的主题,我不会深入细节。 - 所有
ViewState
数据被应用到它们各自的控件上(加载ViewState
)。 - 控件的值被提交的表单数据覆盖(加载
PostbackData
)。 PageLoad
事件被触发。您可能通过将所有代码放在 if(!IsPostback
) 条件中来排除此事件中的代码执行。- Click 事件被触发。这是您将服务器控件的值获取到实体模型的地方。然后,您将数据保存到数据库。
- 重定向到某个其他页面。
Web Form 在保存数据时的问题:您可能会发现,您唯一的工作就是将表单数据保存到数据库,然后重定向到其他页面。因此,实例化控件、为控件填充值、加载视图状态、将数据从控件复制到您的模型,这些都是不必要的开销。如果您希望在保存数据后重定向到另一个页面,为什么还要处理控件、ViewState 和生命周期呢?
建议的方法
新方法提出的想法是,我们实际上不将数据回传到同一页面,而是回传到一个通用处理程序(.ashx)。这将包括以下步骤:
- 将您的数据提交到通用处理程序。将提交的表单数据绑定到实体模型。
- 将模型保存到数据库。
- 重定向到其他页面。
要执行第一步,您可以使用 ModelBinder
类的 Bind 方法将提交的表单数据绑定到模型。在这个方法中,我只是简单地遍历了模型实体的所有属性,并获取了相应的表单数据来填充它。
上图是演示应用程序的屏幕截图。
Using the Code
在本文中,我提供了一个示例,它使用快速回传方法对员工
执行基本的 CRUD 操作。该示例还为您提供了一种将表单数据与实体以及实体数据与控件进行绑定的简便方法。
使用这种方法涉及以下步骤:
- 创建一个新的通用处理程序,并调用方法来绑定数据。在这里的
ProcessRequest
方法中编写保存数据的代码。然后,重定向到其他页面。public void ProcessRequest(HttpContext context) { if (context.Request.HttpMethod == "POST") { Models.Employee emp = ModelBinder.Bind<models.employee>(); using (CompanyEntities db = new CompanyEntities()) { if (context.Request.QueryString["action"] == "create") db.Employees.Add(emp); else db.Entry(emp).State = EntityState.Modified; db.SaveChanges(); } context.Response.Redirect("~/Employee/List.aspx"); } else { context.Response.Write("Either you are trying to hack my system or you have visited this page accidentally."); } }
- 在您的
WebForm
页面中,将提交按钮的PostBackUrl
属性设置为新创建的处理程序。<div class="form-submit"> <asp:button id="btnSubmit" runat="server" text="Submit" postbackurl="~/Employee/Save.ashx?action=edit" /> </div>
在这里,我假设所有必需的身份验证和授权都已在 HttpModule
级别处理。否则,您可能希望在您的处理程序中考虑手动进行身份验证/授权。
上图是演示应用程序的屏幕截图。
模型绑定
提供的示例代码还提供了一种将模型与控件和表单数据绑定的基本方法。您可以扩展此代码以根据您的需求实现功能。如果您希望更详细地实现模型绑定,可以参考此链接。
关注点
- 提供的示例还使用了 ASP.NET 的 FriendlyUrls。因此,对于初学者来说,它可以作为一个示例,展示如何在 ASP.NET Web Form 中轻松生成 Restful URL。
- 提供的示例还使用了实体框架(Entity Framework)。因此,对于初学者来说,它可以作为一个非常轻量级的实体框架使用示例。
- 对于初学者而言,附件代码还可以作为一个示例,说明在只需要在服务器端处理一组代码的情况下,如何使用通用处理程序进行此类操作。
未来考量
嗯,这篇文章只是为您提供了想法和示例实现,我正计划创建一个小型库,用这种方法来处理更复杂的情况。其中一些包括:
- 将更复杂的控件与模型绑定
- 将表单数据绑定到
IEnumerables
- 当发生异常/错误时,将控制权交还给页面
我还在研究一些其他方法,以及本文讨论的方法。这些方法包括:
- 使用
PageMethods
保存数据而不是回传(有助于将相似的逻辑保留在同一页面中) - 使用 Web Service。创建一个不同的类来集中处理所有保存(创建/编辑)和删除操作。如果您想将 Web Service 暴露给第三方,这将很有帮助。
- 使用 Web API。是的,因为在 Visual Studio 2013 中,您可以在同一项目中并行创建 Web API,所以将所有创建/更新和删除逻辑移至 Web API,而仅将显示逻辑(表示层)保留在 WebForm 项目中是一个好主意。这样,您可以在快速应用开发项目中使用 Web Form 的灵活性和丰富的服务器控件,同时保持所有数据库操作更快。这种架构也支持在您希望的情况下将 Web API 暴露给第三方。
我也在研究以上三种方法,一旦我能用其中任何一种方法正确地构建我的解决方案,我将在同一篇或另一篇文章中发布它,具体取决于内容和架构。我很乐意听取专家们对这些方法的优缺点的意见,以便我们也能让漂亮的 Web Form 项目性能更快。
结论
无论何时您想提高 Web Form 应用程序的性能,您都可以轻松地为简单的 Web 表单切换到这种方法,同时在首次加载时利用页面生命周期和服务器控件的优势。此外,对于一些您可能不希望实现此方法的复杂页面,该方法可以与现有的回传方法并存。
我随时欢迎您的反馈/评论/建议,以进一步改进它。
历史
- 2013年8月14日
- 创建了第一个版本
- 2014年4月27日
- 文章更新,附带数据库
- 就另外三种方法展开了讨论