Aggiunta di un livello di logica di business a un progetto che usa l'associazione di modelli e i moduli Web

di Tom FitzMacken

Questa serie di esercitazioni illustra gli aspetti di base dell'uso dell'associazione di modelli con un progetto di Web Forms ASP.NET. L'associazione di modelli rende più semplice l'interazione dei dati rispetto alla gestione di oggetti origine dati, ad esempio ObjectDataSource o SqlDataSource. Questa serie inizia con materiale introduttivo e passa a concetti più avanzati nelle esercitazioni successive.

Questa esercitazione illustra come usare l'associazione di modelli con un livello di logica di business. Verrà impostato il membro OnCallingDataMethods per specificare che un oggetto diverso dalla pagina corrente viene usato per chiamare i metodi di dati.

Questa esercitazione si basa sul progetto creato nelle parti precedenti della serie.

È possibile scaricare il progetto completo in C# o VB. Il codice scaricabile funziona con Visual Studio 2012 o Visual Studio 2013. Usa il modello di Visual Studio 2012, leggermente diverso dal modello di Visual Studio 2013 illustrato in questa esercitazione.

Obiettivo

L'associazione di modelli consente di inserire il codice di interazione dei dati nel file code-behind per una pagina Web o in una classe di logica di business separata. Le esercitazioni precedenti hanno illustrato come usare i file code-behind per il codice di interazione dei dati. Questo approccio funziona per i siti di piccole dimensioni, ma può causare ripetizioni del codice e maggiore difficoltà quando si gestisce un sito di grandi dimensioni. Può anche essere molto difficile testare il codice a livello di codice che risiede nei file code behind perché non esiste alcun livello di astrazione.

Per centralizzare il codice di interazione dei dati, è possibile creare un livello di logica di business contenente tutta la logica per interagire con i dati. Si chiama quindi il livello della logica di business dalle pagine Web. Questa esercitazione illustra come spostare tutto il codice scritto nelle esercitazioni precedenti in un livello di logica di business e quindi usare tale codice dalle pagine.

In questa esercitazione si apprenderà come:

  1. Spostare il codice dai file code-behind a un livello di logica di business
  2. Modificare i controlli associati ai dati per chiamare i metodi nel livello della logica di business

Creare un livello di logica di business

Verrà ora creata la classe chiamata dalle pagine Web. I metodi in questa classe sono simili ai metodi usati nelle esercitazioni precedenti e includono gli attributi del provider di valori.

Aggiungere prima di tutto una nuova cartella denominata BLL.

aggiungere una cartella

Nella cartella BLL creare una nuova classe denominata SchoolBL.cs. Conterrà tutte le operazioni di dati originariamente presenti nei file code-behind. I metodi sono quasi uguali ai metodi nel file code-behind, ma includono alcune modifiche.

La modifica più importante da notare è che non si esegue più il codice dall'interno di un'istanza della classe Page . La classe Page contiene il metodo TryUpdateModel e la proprietà ModelState . Quando questo codice viene spostato in un livello di logica di business, non è più disponibile un'istanza della classe Page per chiamare questi membri. Per risolvere questo problema, è necessario aggiungere un parametro ModelMethodContext a qualsiasi metodo che accede a TryUpdateModel o ModelState. Si usa questo parametro ModelMethodContext per chiamare TryUpdateModel o recuperare ModelState. Non è necessario modificare alcun elemento nella pagina Web per account di questo nuovo parametro.

Sostituire il codice in SchoolBL.cs con il codice seguente.

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

Rivedere le pagine esistenti per recuperare i dati dal livello della logica di business

Infine, si converteranno le pagine Students.aspx, AddStudent.aspx e Courses.aspx dall'uso delle query nel file code-behind all'uso del livello di logica di business.

Nei file code-behind per Studenti, AddStudent e Corsi eliminare o commentare i metodi di query seguenti:

  • studentsGrid_GetData
  • studentsGrid_UpdateItem
  • studentsGrid_DeleteItem
  • addStudentForm_InsertItem
  • coursesGrid_GetData

È ora necessario non avere codice nel file code-behind che riguarda le operazioni sui dati.

Il gestore eventi OnCallingDataMethods consente di specificare un oggetto da usare per i metodi di dati. In Students.aspx aggiungere un valore per tale gestore eventi e modificare i nomi dei metodi di dati ai nomi dei metodi della logica di business.

<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">

Nel file code-behind per Students.aspx definire il gestore eventi per l'evento CallingDataMethods. In questo gestore eventi specificare la classe di logica di business per le operazioni sui dati.

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

In AddStudent.aspx apportare modifiche simili.

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

In Courses.aspx apportare modifiche simili.

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

Eseguire l'applicazione e notare che tutte le pagine funzionano in precedenza. La logica di convalida funziona correttamente.

Conclusione

In questa esercitazione viene ristrutturata l'applicazione per usare un livello di accesso ai dati e un livello di logica di business. È stato specificato che i controlli dati usano un oggetto che non è la pagina corrente per le operazioni sui dati.