将业务逻辑层添加到使用模型绑定和 Web 窗体的项目

作者 Tom FitzMacken

本教程系列演示了将模型绑定与 ASP.NET Web Forms项目配合使用的基本方面。 模型绑定使数据交互比处理数据源对象 ((如 ObjectDataSource 或 SqlDataSource) )更直接。 本系列从介绍性材料开始,在后面的教程中将介绍更高级的概念。

本教程演示如何对业务逻辑层使用模型绑定。 您将设置 OnCallingDataMethods 成员以指定使用当前页以外的对象来调用数据方法。

本教程基于在系列 前面部分创建 的项目。

可以使用 C# 或 VB 下载 完整的项目。 可下载的代码适用于 Visual Studio 2012 或 Visual Studio 2013。 它使用 Visual Studio 2012 模板,该模板与本教程中显示的 Visual Studio 2013 模板略有不同。

生成目标

模型绑定使你能够将数据交互代码放在网页的代码隐藏文件中或单独的业务逻辑类中。 前面的教程演示了如何将代码隐藏文件用于数据交互代码。 此方法适用于小型网站,但在维护大型网站时可能会导致代码重复和更大的难度。 由于没有抽象层,因此以编程方式测试驻留在代码隐藏文件中的代码也非常困难。

若要集中数据交互代码,可以创建一个业务逻辑层,其中包含用于与数据交互的所有逻辑。 然后,从网页调用业务逻辑层。 本教程介绍如何将前面教程中编写的所有代码移动到业务逻辑层,然后从页面使用该代码。

在本教程中,你将:

  1. 将代码从代码隐藏文件移动到业务逻辑层
  2. 更改数据绑定控件以调用业务逻辑层中的方法

创建业务逻辑层

现在,您将创建从网页调用的 类。 此类中的方法类似于在前面的教程中使用的方法,并包含值提供程序属性。

首先,添加名为 BLL 的新文件夹。

添加文件夹

在 BLL 文件夹中,创建名为 SchoolBL.cs 的新类。 它将包含最初驻留在代码隐藏文件中的所有数据操作。 方法几乎与代码隐藏文件中的方法相同,但会包含一些更改。

要注意的最重要更改是,不再从 Page 类的实例中执行代码。 Page 类包含 TryUpdateModel 方法和 ModelState 属性。 当此代码移动到业务逻辑层时,你不再具有用于调用这些成员的 Page 类的实例。 若要解决此问题,必须将 ModelMethodContext 参数添加到访问 TryUpdateModel 或 ModelState 的任何方法。 使用此 ModelMethodContext 参数调用 TryUpdateModel 或检索 ModelState。 无需更改网页中的任何内容即可考虑此新参数。

将 SchoolBL.cs 中的代码替换为以下代码。

using System;
using System.Linq;
using ContosoUniversityModelBinding.Models;
using System.Web.ModelBinding;
using System.Web.UI.WebControls;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;

namespace ContosoUniversityModelBinding.BLL
{
    public class SchoolBL : IDisposable
    {
        SchoolContext db = new SchoolContext();

        public IQueryable<Student> GetStudents([Control] AcademicYear? displayYear)
        {
            var query = db.Students.Include(s => s.Enrollments.Select(e => e.Course));

            if (displayYear != null)
            {
                query = query.Where(s => s.Year == displayYear);
            }

            return query;
        }

        public void InsertStudent(ModelMethodContext context)
        {
            var item = new Student();

            context.TryUpdateModel(item);
            if (context.ModelState.IsValid)
            {
                db.Students.Add(item);
                db.SaveChanges();
            }
        }

        public void DeleteStudent(int studentID, ModelMethodContext context)
        {
            var item = new Student { StudentID = studentID };
            db.Entry(item).State = EntityState.Deleted;
            try
            {
                db.SaveChanges();
            }
            catch (DbUpdateConcurrencyException)
            {
                context.ModelState.AddModelError("",
                    String.Format("Item with id {0} no longer exists in the database.", studentID));
            }
        }

        public void UpdateStudent(int studentID, ModelMethodContext context)
        {
            Student item = null;
            item = db.Students.Find(studentID);
            if (item == null)
            {
                context.ModelState.AddModelError("", String.Format("Item with id {0} was not found", studentID));
                return;
            }

            context.TryUpdateModel(item);
            if (context.ModelState.IsValid)
            {
                db.SaveChanges();
            }
        }

        public IQueryable<Enrollment> GetCourses([QueryString] int? studentID)
        {
            var query = db.Enrollments.Include(e => e.Course)
                .Where(e => e.StudentID == studentID);
            return query;
        }

        private bool disposedValue = false;

        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposedValue)
            {
                if (disposing)
                {
                    db.Dispose();
                }
            }
            this.disposedValue = true;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }
}

修改现有页面以从业务逻辑层检索数据

最后,你将将页面 Students.aspx、AddStudent.aspx 和 Courses.aspx 从使用代码隐藏文件中的查询转换为使用业务逻辑层。

在 Students、AddStudent 和 Courses 的代码隐藏文件中,删除或注释掉以下查询方法:

  • studentsGrid_GetData
  • studentsGrid_UpdateItem
  • studentsGrid_DeleteItem
  • addStudentForm_InsertItem
  • coursesGrid_GetData

现在,代码隐藏文件中应该没有与数据操作相关的代码。

使用 OnCallingDataMethods 事件处理程序可以指定要用于数据方法的对象。 在 Students.aspx 中,为该事件处理程序添加值,并将数据方法的名称更改为业务逻辑类中方法的名称。

<asp:GridView runat="server" ID="studentsGrid"
    ItemType="ContosoUniversityModelBinding.Models.Student" DataKeyNames="StudentID"
    SelectMethod="GetStudents"
    UpdateMethod="UpdateStudent" DeleteMethod="DeleteStudent"
    AllowSorting="true" AllowPaging="true" PageSize="4"
    AutoGenerateEditButton="true" AutoGenerateDeleteButton="true"
    AutoGenerateColumns="false" 
    OnCallingDataMethods="studentsGrid_CallingDataMethods">

在 Students.aspx 的代码隐藏文件中,定义 CallingDataMethods 事件的事件处理程序。 在此事件处理程序中,为数据操作指定业务逻辑类。

protected void studentsGrid_CallingDataMethods(object sender, CallingDataMethodsEventArgs e)
{
    e.DataMethodsObject = new ContosoUniversityModelBinding.BLL.SchoolBL();
}

在 AddStudent.aspx 中,进行类似的更改。

<asp:FormView runat="server" ID="addStudentForm"
    ItemType="ContosoUniversityModelBinding.Models.Student"
    InsertMethod="InsertStudent" DefaultMode="Insert"
    OnCallingDataMethods="addStudentForm_CallingDataMethods"
    RenderOuterTable="false" OnItemInserted="addStudentForm_ItemInserted">
protected void addStudentForm_CallingDataMethods(object sender, CallingDataMethodsEventArgs e)
{
    e.DataMethodsObject = new ContosoUniversityModelBinding.BLL.SchoolBL();
}

在 Courses.aspx 中,进行类似的更改。

<asp:GridView runat="server" ID="coursesGrid"
    ItemType="ContosoUniversityModelBinding.Models.Enrollment"
    SelectMethod="GetCourses" AutoGenerateColumns="false"
    OnCallingDataMethods="coursesGrid_CallingDataMethods">
protected void coursesGrid_CallingDataMethods(object sender, CallingDataMethodsEventArgs e)
{
    e.DataMethodsObject = new ContosoUniversityModelBinding.BLL.SchoolBL();
}

运行应用程序,并注意所有页面都像以前一样工作。 验证逻辑也正常工作。

结论

在本教程中,你将重新构造应用程序,以使用数据访问层和业务逻辑层。 您指定数据控件使用对象,该对象不是当前页的数据操作。