Добавление уровня бизнес-логики в проект, использующий привязку модели и веб-формы

; автор — Том ФитцМакен (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 с использования запросов в файле кода программной части на уровень бизнес-логики.

В файлах кода программной части для учащихся, 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();
}

Запустите приложение и обратите внимание, что все страницы работают так же, как и ранее. Логика проверки также работает правильно.

Заключение

В этом руководстве вы перестроили приложение для использования уровня доступа к данным и уровня бизнес-логики. Вы указали, что элементы управления данными используют объект, который не является текущей страницей для операций с данными.