モデル バインドと Web フォームを使用するプロジェクトへのビジネス ロジック レイヤーの追加

作成者: Tom FitzMacken

このチュートリアル シリーズでは、ASP.NET Web Forms プロジェクトでモデル バインドを使用する基本的な側面について説明します。 モデル バインドを使用すると、データ ソース オブジェクト (ObjectDataSource や SqlDataSource など) を扱うよりも、データの操作が簡単になります。 このシリーズは入門資料から始まり、後のチュートリアルでより高度な概念に進んでいきます。

このチュートリアルでは、ビジネス ロジック レイヤーでモデル バインドを使用する方法について説明します。 OnCallingDataMethods メンバーを設定して、現在のページ以外のオブジェクトを使用してデータ メソッドを呼び出すよう指定します。

このチュートリアルは、シリーズの前の部分で作成されたプロジェクトに基づいています。

C# または VB で完全なプロジェクトをダウンロードできます。 ダウンロード可能なコードは、Visual Studio 2012 または Visual Studio 2013 で動作します。 このチュートリアルで示す Visual Studio 2013 テンプレートとは若干異なる Visual Studio 2012 テンプレートを使用します。

作成する内容

モデル バインドを使用すると、Web ページの分離コード ファイルまたは別のビジネス ロジック クラスにデータ対話コードを配置できます。 前のチュートリアルでは、分離コード ファイルをデータ操作コードに使用する方法を示しました。 このアプローチは小規模なサイトに対して機能しますが、大規模なサイトをメインすると、コードの繰り返しや難易度が高くなる可能性があります。 抽象化レイヤーがないため、分離コード ファイルに存在するコードをプログラムでテストすることは非常に困難な場合もあります。

データ対話コードを一元化するために、データを操作するためのすべてのロジックを含むビジネス ロジック レイヤーを作成できます。 次に、Web ページからビジネス ロジック レイヤーを呼び出します。 このチュートリアルでは、前のチュートリアルで書いたすべてのコードをビジネス ロジック レイヤーに移動し、ページからそのコードを使用する方法について説明します。

このチュートリアルでは、次のことについて説明します。

  1. 分離コード ファイルからビジネス ロジック レイヤーにコードを移動する
  2. ビジネス ロジック レイヤー内のメソッドを呼び出すようにデータ バインド コントロールを変更する

ビジネス ロジック レイヤーを作成する

次に、Web ページから呼び出されるクラスを作成します。 このクラスのメソッドは、前のチュートリアルで使用したメソッドに似ていますが、値プロバイダー属性が含まれています。

まず、BLL という名前の新しいフォルダーを追加します。

add folder

BLL フォルダーに、SchoolBL.cs という名前の新しいクラスを作成します。 これには、分離コード ファイルにもともと存在していたすべてのデータ操作が含まれます。 メソッドは分離コード ファイル内のメソッドとほぼ同じですが、いくつかの変更が含まれます。

注意すべき最も重要な変更は、Page クラスのインスタンス内からコードを実行しなくなったということです。 Page クラスには、TryUpdateModel メソッドと ModelState プロパティが含まれています。 このコードをビジネス ロジック レイヤーに移動すると、これらのメンバーを呼び出す Page クラスのインスタンスはなくなります。 この問題を回避するには、TryUpdateModel または ModelState にアクセスするすべてのメソッドに ModelMethodContext パラメーターを追加する必要があります。 この ModelMethodContext パラメーターを使用して、TryUpdateModel を呼び出すか、ModelState を取得します。 この新しいパラメーターを考慮するために、Web ページ内の何も変更する必要はありません。

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();
}

アプリケーションを実行し、すべてのページが以前と同じように機能していることに注意してください。 検証ロジックも正しく機能します。

まとめ

このチュートリアルでは、データ アクセス層とビジネス ロジック層を使用するようにアプリケーションを再構成しました。 データ コントロールで、データ操作に現在のページではないオブジェクトを使用することを指定しました。