Freigeben über


Verwenden von Entity Framework 4.0 und dem ObjectDataSource-Steuerelement, Teil 2: Hinzufügen einer Geschäftslogikebene und Komponententests

von Tom Dykstra

Diese Tutorialreihe baut auf der Webanwendung der Contoso University auf, die vom Erste Schritte mit der Tutorialreihe Entity Framework 4.0 erstellt wird. Wenn Sie die vorherigen Tutorials nicht abgeschlossen haben, können Sie als Ausgangspunkt für dieses Tutorial die Anwendung herunterladen, die Sie erstellt hätten. Sie können auch die Anwendung herunterladen, die von der vollständigen Tutorialreihe erstellt wird. Wenn Sie Fragen zu den Tutorials haben, können Sie diese im ASP.NET Entity Framework-Forum veröffentlichen.

Im vorherigen Tutorial haben Sie mithilfe von Entity Framework und dem ObjectDataSource -Steuerelement eine n-Ebenen-Webanwendung erstellt. In diesem Tutorial wird gezeigt, wie Sie Geschäftslogik hinzufügen und gleichzeitig die Geschäftslogikebene (Business Logic Layer, BLL) und die Datenzugriffsebene (DATA Access Layer, DAL) getrennt halten. Außerdem wird gezeigt, wie Automatisierte Komponententests für die BLL erstellt werden.

In diesem Tutorial führen Sie die folgenden Aufgaben aus:

  • Erstellen Sie eine Repositoryschnittstelle, die die benötigten Datenzugriffsmethoden deklariert.
  • Implementieren Sie die Repositoryschnittstelle in der Repositoryklasse.
  • Erstellen Sie eine Geschäftslogikklasse, die die Repositoryklasse aufruft, um Datenzugriffsfunktionen auszuführen.
  • Verbinden Sie das ObjectDataSource Steuerelement mit der Geschäftslogikklasse und nicht mit der Repositoryklasse.
  • Erstellen Sie ein Komponententestprojekt und eine Repositoryklasse, die In-Memory-Sammlungen für den Datenspeicher verwendet.
  • Erstellen Sie einen Komponententest für Geschäftslogik, den Sie der Geschäftslogikklasse hinzufügen möchten, und führen Sie dann den Test aus, und sehen Sie, wie er fehlschlägt.
  • Implementieren Sie die Geschäftslogik in der Business Logic-Klasse, führen Sie dann den Komponententest erneut aus, und sehen Sie, ob er erfolgreich ist.

Sie arbeiten mit den Seiten Departments.aspx und DepartmentsAdd.aspx , die Sie im vorherigen Tutorial erstellt haben.

Erstellen einer Repositoryschnittstelle

Beginnen Sie mit dem Erstellen der Repositoryschnittstelle.

Image08

Erstellen Sie im Ordner DAL eine neue Klassendatei, nennen Sie sie ISchoolRepository.cs, und ersetzen Sie den vorhandenen Code durch den folgenden Code:

using System;
using System.Collections.Generic;

namespace ContosoUniversity.DAL
{
    public interface ISchoolRepository : IDisposable
    {
        IEnumerable<Department> GetDepartments();
        void InsertDepartment(Department department);
        void DeleteDepartment(Department department);
        void UpdateDepartment(Department department, Department origDepartment);
        IEnumerable<InstructorName> GetInstructorNames();
    }
}

Die Schnittstelle definiert eine Methode für jede CRUD-Methode (erstellen, lesen, aktualisieren, löschen), die Sie in der Repositoryklasse erstellt haben.

Geben Sie in der SchoolRepository Klasse in SchoolRepository.cs an, dass diese Klasse die ISchoolRepository Schnittstelle implementiert:

public class SchoolRepository : IDisposable, ISchoolRepository

Erstellen einer Business-Logic-Klasse

Als Nächstes erstellen Sie die Business Logic-Klasse. Sie tun dies, damit Sie Geschäftslogik hinzufügen können, die ObjectDataSource vom Steuerelement ausgeführt wird, obwohl Sie dies noch nicht tun werden. Derzeit führt die neue Geschäftslogikklasse nur die gleichen CRUD-Vorgänge wie das Repository aus.

Image09

Erstellen Sie einen neuen Ordner, und nennen Sie ihn BLL. (In einer realen Anwendung wird die Geschäftslogikebene in der Regel als Klassenbibliothek – ein separates Projekt – implementiert, aber um dieses Tutorial einfach zu halten, werden BLL-Klassen in einem Projektordner aufbewahrt.)

Erstellen Sie im Ordner BLL eine neue Klassendatei, nennen Sie sie SchoolBL.cs, und ersetzen Sie den vorhandenen Code durch den folgenden Code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using ContosoUniversity.DAL;

namespace ContosoUniversity.BLL
{
    public class SchoolBL : IDisposable
    {
        private ISchoolRepository schoolRepository;

        public SchoolBL()
        {
            this.schoolRepository = new SchoolRepository();
        }

        public SchoolBL(ISchoolRepository schoolRepository)
        {
            this.schoolRepository = schoolRepository;
        }

        public IEnumerable<Department> GetDepartments()
        {
            return schoolRepository.GetDepartments();
        }

        public void InsertDepartment(Department department)
        {
            try
            {
                schoolRepository.InsertDepartment(department);
            }
            catch (Exception ex)
            {
                //Include catch blocks for specific exceptions first,
                //and handle or log the error as appropriate in each.
                //Include a generic catch block like this one last.
                throw ex;
            }
        }

        public void DeleteDepartment(Department department)
        {
            try
            {
                schoolRepository.DeleteDepartment(department);
            }
            catch (Exception ex)
            {
                //Include catch blocks for specific exceptions first,
                //and handle or log the error as appropriate in each.
                //Include a generic catch block like this one last.
                throw ex;
            }
        }

        public void UpdateDepartment(Department department, Department origDepartment)
        {
            try
            {
                schoolRepository.UpdateDepartment(department, origDepartment);
            }
            catch (Exception ex)
            {
                //Include catch blocks for specific exceptions first,
                //and handle or log the error as appropriate in each.
                //Include a generic catch block like this one last.
                throw ex;
            }

        }

        public IEnumerable<InstructorName> GetInstructorNames()
        {
            return schoolRepository.GetInstructorNames();
        }

        private bool disposedValue = false;

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

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

    }
}

Dieser Code erstellt dieselben CRUD-Methoden, die Sie zuvor in der Repositoryklasse gesehen haben, aber anstatt direkt auf die Entity Framework-Methoden zuzugreifen, ruft er die Repositoryklassenmethoden auf.

Die Klassenvariable, die einen Verweis auf die Repositoryklasse enthält, ist als Schnittstellentyp definiert, und der Code, der die Repositoryklasse instanziiert, ist in zwei Konstruktoren enthalten. Der parameterlose Konstruktor wird vom ObjectDataSource Steuerelement verwendet. Es wird eine instance der SchoolRepository Zuvor erstellten Klasse erstellt. Mit dem anderen Konstruktor kann der Code, der die Geschäftslogikklasse instanziiert, in jedem Objekt übergeben werden, das die Repositoryschnittstelle implementiert.

Die CRUD-Methoden, die die Repositoryklasse aufrufen, und die beiden Konstruktoren ermöglichen es, die Geschäftslogikklasse mit dem von Ihnen ausgewählten Back-End-Datenspeicher zu verwenden. Die Geschäftslogikklasse muss nicht wissen, wie die klasse, die sie aufruft, die Daten speichert. (Dies wird häufig als Persistenz-Ignoranz bezeichnet.) Dies erleichtert Komponententests, da Sie die Geschäftslogikklasse mit einer Repositoryimplementierung verbinden können, die so einfach wie In-Memory-Sammlungen List zum Speichern von Daten verwendet.

Hinweis

Technisch gesehen sind die Entitätsobjekte immer noch nicht persistenz-unwissend, da sie von Klassen instanziiert werden, die von der Entity Framework-Klasse erben EntityObject . Bei vollständiger Persistenz können Sie einfache alte CLR-Objekte oder POCOs anstelle von Objekten verwenden, die von der EntityObject -Klasse erben. Die Verwendung von POCOs sprengt den Rahmen dieses Tutorials. Weitere Informationen finden Sie unter Testierbarkeit und Entity Framework 4.0 auf der MSDN-Website.)

Jetzt können Sie die ObjectDataSource Steuerelemente mit der Geschäftslogikklasse anstatt mit dem Repository verbinden und überprüfen, ob alles wie zuvor funktioniert.

Ändern Sie in Departments.aspx und DepartmentsAdd.aspx jedes Vorkommen von TypeName="ContosoUniversity.DAL.SchoolRepository" in TypeName="ContosoUniversity.BLL.SchoolBL". (Es gibt vier Instanzen in allen.)

Führen Sie die Seiten Departments.aspx und DepartmentsAdd.aspx aus, um sicherzustellen, dass sie weiterhin wie zuvor funktionieren.

Image01

Image02

Erstellen einer Unit-Test Projekt- und Repositoryimplementierung

Fügen Sie der Projektmappe mithilfe der Vorlage Testprojekt ein neues Projekt hinzu, und nennen Sie es ContosoUniversity.Tests.

Fügen Sie im Testprojekt einen Verweis auf das System.Data.Entity Projekt hinzu, und fügen Sie dem Projekt einen Projektverweis ContosoUniversity hinzu.

Sie können jetzt die Repositoryklasse erstellen, die Sie mit Komponententests verwenden. Der Datenspeicher für dieses Repository befindet sich innerhalb der -Klasse.

Bild12

Erstellen Sie im Testprojekt eine neue Klassendatei, nennen Sie sie MockSchoolRepository.cs, und ersetzen Sie den vorhandenen Code durch den folgenden Code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ContosoUniversity.DAL;
using ContosoUniversity.BLL;

namespace ContosoUniversity.Tests
{
    class MockSchoolRepository : ISchoolRepository, IDisposable
    {
        List<Department> departments = new List<Department>();
        List<InstructorName> instructors = new List<InstructorName>();

        public IEnumerable<Department> GetDepartments()
        {
            return departments;
        }

        public void InsertDepartment(Department department)
        {
            departments.Add(department);
        }

        public void DeleteDepartment(Department department)
        {
            departments.Remove(department);
        }

        public void UpdateDepartment(Department department, Department origDepartment)
        {
            departments.Remove(origDepartment);
            departments.Add(department);
        }

        public IEnumerable<InstructorName> GetInstructorNames()
        {
            return instructors;
        }

        public void Dispose()
        {
            
        }
    }
}

Diese Repositoryklasse verfügt über dieselben CRUD-Methoden wie die, die direkt auf das Entity Framework zugreift, aber sie arbeiten mit List Sammlungen im Arbeitsspeicher statt mit einer Datenbank. Dies erleichtert es einer Testklasse, Komponententests für die Geschäftslogikklasse einzurichten und zu überprüfen.

Erstellen von Komponententests

Die Projektvorlage Test hat eine Stubkomponententestklasse für Sie erstellt, und Ihre nächste Aufgabe besteht darin, diese Klasse zu ändern, indem Sie ihr Komponententestmethoden für die Geschäftslogik hinzufügen, die Sie der Geschäftslogikklasse hinzufügen möchten.

Bild13

An der Contoso University kann jeder einzelne Dozent nur Administrator einer einzelnen Abteilung sein, und Sie müssen Geschäftslogik hinzufügen, um diese Regel zu erzwingen. Zunächst fügen Sie Tests hinzu und führen die Tests aus, um zu sehen, dass sie fehlschlagen. Anschließend fügen Sie den Code hinzu und führen die Tests erneut aus, und erwarten, dass sie erfolgreich sind.

Öffnen Sie die Datei UnitTest1.cs , und fügen Sie Anweisungen für die Geschäftslogik- und Datenzugriffsebenen using hinzu, die Sie im Projekt ContosoUniversity erstellt haben:

using ContosoUniversity.BLL;
using ContosoUniversity.DAL;

Ersetzen Sie die TestMethod1 -Methode durch die folgenden Methoden:

private SchoolBL CreateSchoolBL()
{
    var schoolRepository = new MockSchoolRepository();
    var schoolBL = new SchoolBL(schoolRepository);
    schoolBL.InsertDepartment(new Department() { Name = "First Department", DepartmentID = 0, Administrator = 1, Person = new Instructor () { FirstMidName = "Admin", LastName = "One" } });
    schoolBL.InsertDepartment(new Department() { Name = "Second Department", DepartmentID = 1, Administrator = 2, Person = new Instructor() { FirstMidName = "Admin", LastName = "Two" } });
    schoolBL.InsertDepartment(new Department() { Name = "Third Department", DepartmentID = 2, Administrator = 3, Person = new Instructor() { FirstMidName = "Admin", LastName = "Three" } });
    return schoolBL;
}

[TestMethod]
[ExpectedException(typeof(DuplicateAdministratorException))]
public void AdministratorAssignmentRestrictionOnInsert()
{
    var schoolBL = CreateSchoolBL();
    schoolBL.InsertDepartment(new Department() { Name = "Fourth Department", DepartmentID = 3, Administrator = 2, Person = new Instructor() { FirstMidName = "Admin", LastName = "Two" } });
}

[TestMethod]
[ExpectedException(typeof(DuplicateAdministratorException))]
public void AdministratorAssignmentRestrictionOnUpdate()
{
    var schoolBL = CreateSchoolBL();
    var origDepartment = (from d in schoolBL.GetDepartments()
                          where d.Name == "Second Department"
                          select d).First();
    var department = (from d in schoolBL.GetDepartments()
                          where d.Name == "Second Department"
                          select d).First();
    department.Administrator = 1;
    schoolBL.UpdateDepartment(department, origDepartment);
}

Die CreateSchoolBL -Methode erstellt eine instance der Repositoryklasse, die Sie für das Komponententestprojekt erstellt haben, und übergibt sie dann an einen neuen instance der Geschäftslogikklasse. Die Methode verwendet dann die Geschäftslogikklasse, um drei Abteilungen einzufügen, die Sie in Testmethoden verwenden können.

Die Testmethoden überprüfen, ob die Business Logic-Klasse eine Ausnahme auslöst, wenn jemand versucht, eine neue Abteilung mit demselben Administrator wie eine vorhandene Abteilung einzufügen, oder wenn jemand versucht, den Administrator einer Abteilung zu aktualisieren, indem er sie auf die ID einer Person festlegt, die bereits Administrator einer anderen Abteilung ist.

Sie haben die Ausnahmeklasse noch nicht erstellt, sodass dieser Code nicht kompiliert wird. Klicken Sie zum Kompilieren mit der rechten Maustaste, DuplicateAdministratorException und wählen Sie Generieren und dann Klasse aus.

Screenshot: Auswahl

Dadurch wird eine Klasse im Testprojekt erstellt, die Sie löschen können, nachdem Sie die Ausnahmeklasse im Standard-Projekt erstellt haben. und implementierte die Geschäftslogik.

Führen Sie das Testprojekt aus. Wie erwartet schlagen die Tests fehl.

Image03

Hinzufügen von Geschäftslogik zum Durchführen eines Testdurchlaufs

Als Nächstes implementieren Sie die Geschäftslogik, die es unmöglich macht, als Administrator einer Abteilung jemanden festzulegen, der bereits Administrator einer anderen Abteilung ist. Sie lösen eine Ausnahme aus der Geschäftslogikebene aus und erfassen sie dann in der Präsentationsebene, wenn ein Benutzer eine Abteilung bearbeitet und auf Aktualisieren klickt, nachdem er jemanden ausgewählt hat, der bereits Administrator ist. (Sie können auch Kursleiter aus der Dropdownliste entfernen, die bereits Administratoren sind, bevor Sie die Seite rendern, aber der Zweck besteht darin, mit der Geschäftslogikebene zu arbeiten.)

Erstellen Sie zunächst die Ausnahmeklasse, die Sie auslösen, wenn ein Benutzer versucht, einen Kursleiter zum Administrator mehrerer Abteilungen zu machen. Erstellen Sie im Standard-Projekt eine neue Klassendatei im Ordner BLL, nennen Sie sie DuplicateAdministratorException.cs, und ersetzen Sie den vorhandenen Code durch den folgenden Code:

using System;

namespace ContosoUniversity.BLL
{
    public class DuplicateAdministratorException : Exception
    {
        public DuplicateAdministratorException(string message)
            : base(message)
        {
        }
    }
}

Löschen Sie nun die temporäre Datei DuplicateAdministratorException.cs , die Sie zuvor im Testprojekt erstellt haben, um kompilieren zu können.

Öffnen Sie im projekt Standard die Datei SchoolBL.cs, und fügen Sie die folgende Methode hinzu, die die Validierungslogik enthält. (Der Code verweist auf eine Methode, die Sie später erstellen werden.)

private void ValidateOneAdministratorAssignmentPerInstructor(Department department)
{
    if (department.Administrator != null)
    {
        var duplicateDepartment = schoolRepository.GetDepartmentsByAdministrator(department.Administrator.GetValueOrDefault()).FirstOrDefault();
        if (duplicateDepartment != null && duplicateDepartment.DepartmentID != department.DepartmentID)
        {
            throw new DuplicateAdministratorException(String.Format(
                "Instructor {0} {1} is already administrator of the {2} department.", 
                duplicateDepartment.Person.FirstMidName, 
                duplicateDepartment.Person.LastName, 
                duplicateDepartment.Name));
        }
    }
}

Sie rufen diese Methode auf, wenn Sie Entitäten einfügen oder aktualisieren Department , um zu überprüfen, ob eine andere Abteilung bereits über denselben Administrator verfügt.

Der Code ruft eine Methode auf, um die Datenbank nach einer Department Entität zu durchsuchen, die über den gleichen Administrator Eigenschaftswert wie die Entität verfügt, die eingefügt oder aktualisiert wird. Wenn eine gefunden wird, löst der Code eine Ausnahme aus. Es ist keine Überprüfung erforderlich, wenn die Entität, die eingefügt oder aktualisiert wird, keinen Administrator Wert aufweist, und keine Ausnahme ausgelöst wird, wenn die Methode während eines Updates aufgerufen wird und die Department gefundene Entität mit der entität übereinstimmt, die Department aktualisiert wird.

Rufen Sie die neue Methode über die Insert Methoden und Update auf:

public void InsertDepartment(Department department)
{
    ValidateOneAdministratorAssignmentPerInstructor(department);
    try
    ...

public void UpdateDepartment(Department department, Department origDepartment)
{
    ValidateOneAdministratorAssignmentPerInstructor(department);
    try
    ...

Fügen Sie in ISchoolRepository.cs die folgende Deklaration für die neue Datenzugriffsmethode hinzu:

IEnumerable<Department> GetDepartmentsByAdministrator(Int32 administrator);

Fügen Sie in SchoolRepository.cs die folgende using Anweisung hinzu:

using System.Data.Objects;

Fügen Sie in SchoolRepository.cs die folgende neue Datenzugriffsmethode hinzu:

public IEnumerable<Department> GetDepartmentsByAdministrator(Int32 administrator)
{
    return new ObjectQuery<Department>("SELECT VALUE d FROM Departments as d", context, MergeOption.NoTracking).Include("Person").Where(d => d.Administrator == administrator).ToList();
}

Dieser Code ruft Entitäten Department ab, die über einen angegebenen Administrator verfügen. Es sollte nur eine Abteilung gefunden werden (falls vorhanden). Da jedoch keine Einschränkung in die Datenbank integriert ist, ist der Rückgabetyp eine Sammlung, falls mehrere Abteilungen gefunden werden.

Wenn der Objektkontext Entitäten aus der Datenbank abruft, verfolgt er diese standardmäßig im Objektstatus-Manager. Der MergeOption.NoTracking Parameter gibt an, dass diese Nachverfolgung für diese Abfrage nicht durchgeführt wird. Dies ist erforderlich, da die Abfrage möglicherweise genau die Entität zurückgibt, die Sie aktualisieren möchten, und sie dann nicht mehr in der Lage wären, diese Entität anzufügen. Wenn Sie beispielsweise die Verlaufsabteilung auf der Seite Departments.aspx bearbeiten und den Administrator unverändert lassen, gibt diese Abfrage die Verlaufsabteilung zurück. Wenn NoTracking nicht festgelegt ist, enthält der Objektkontext bereits die Abteilungsentität History in seinem Objektstatus-Manager. Wenn Sie dann die Entität der Verlaufsabteilung anfügen, die aus dem Ansichtszustand neu erstellt wurde, löst der Objektkontext eine Ausnahme aus, die besagt "An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key".

(Alternativ zur Angabe MergeOption.NoTrackingkönnen Sie einen neuen Objektkontext nur für diese Abfrage erstellen. Da der neue Objektkontext über einen eigenen Objektstatus-Manager verfügen würde, tritt beim Aufrufen der Methode kein Konflikt auf Attach . Der neue Objektkontext würde Metadaten und Datenbankverbindungen mit dem ursprünglichen Objektkontext gemeinsam nutzen, sodass die Leistungseinbuße bei diesem alternativen Ansatz minimal wäre. Der hier gezeigte Ansatz führt jedoch die NoTracking Option ein, die In anderen Kontexten nützlich ist. Die NoTracking Option wird in einem späteren Tutorial in dieser Reihe weiter erläutert.)

Fügen Sie im Testprojekt die neue Datenzugriffsmethode zu MockSchoolRepository.cs hinzu:

public IEnumerable<Department> GetDepartmentsByAdministrator(Int32 administrator)
{
    return (from d in departments
            where d.Administrator == administrator
            select d);
}

Dieser Code verwendet LINQ, um dieselbe Datenauswahl durchzuführen, für die das ContosoUniversity Projektrepository LINQ to Entities verwendet.

Führen Sie das Testprojekt erneut aus. Dieses Mal werden die Tests bestanden.

Image04

Behandeln von ObjectDataSource-Ausnahmen

Führen Sie im Projekt die ContosoUniversity Seite Departments.aspx aus, und versuchen Sie, den Administrator für eine Abteilung in jemanden zu ändern, der bereits Administrator für eine andere Abteilung ist. (Denken Sie daran, dass Sie nur Abteilungen bearbeiten können, die Sie während dieses Tutorials hinzugefügt haben, da die Datenbank mit ungültigen Daten vorinstalliert ist.) Sie erhalten die folgende Serverfehlerseite:

Image05

Sie möchten nicht, dass Benutzern diese Art von Fehlerseite angezeigt wird, daher müssen Sie Code zur Fehlerbehandlung hinzufügen. Öffnen Sie Departments.aspx , und geben Sie einen Handler für das OnUpdated Ereignis von an DepartmentsObjectDataSource. Das ObjectDataSource öffnende Tag ähnelt nun dem folgenden Beispiel.

<asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server" 
        TypeName="ContosoUniversity.BLL.SchoolBL"
        DataObjectTypeName="ContosoUniversity.DAL.Department" 
        SelectMethod="GetDepartments" 
        DeleteMethod="DeleteDepartment" 
        UpdateMethod="UpdateDepartment"
        ConflictDetection="CompareAllValues"
        OldValuesParameterFormatString="orig{0}" 
        OnUpdated="DepartmentsObjectDataSource_Updated" >

Fügen Sie in Departments.aspx.cs die folgende using Anweisung hinzu:

using ContosoUniversity.BLL;

Fügen Sie den folgenden Handler für das Updated Ereignis hinzu:

protected void DepartmentsObjectDataSource_Updated(object sender, ObjectDataSourceStatusEventArgs e)
{
    if (e.Exception != null)
    {
        if (e.Exception.InnerException is DuplicateAdministratorException)
        {
            var duplicateAdministratorValidator = new CustomValidator();
            duplicateAdministratorValidator.IsValid = false;
            duplicateAdministratorValidator.ErrorMessage = "Update failed: " + e.Exception.InnerException.Message;
            Page.Validators.Add(duplicateAdministratorValidator);
            e.ExceptionHandled = true;
        }
    }
}

Wenn das ObjectDataSource Steuerelement eine Ausnahme abfängt, wenn es versucht, das Update auszuführen, übergibt es die Ausnahme im Ereignisargument (e) an diesen Handler. Der Code im Handler überprüft, ob es sich bei der Ausnahme um die Ausnahme des doppelten Administrators handelt. Wenn dies der Grund ist, erstellt der Code ein Validierungssteuerelement, das eine Fehlermeldung für das anzuzeigende ValidationSummary Steuerelement enthält.

Führen Sie die Seite aus, und versuchen Sie erneut, jemanden zum Administrator von zwei Abteilungen zu machen. Dieses Mal zeigt das ValidationSummary Steuerelement eine Fehlermeldung an.

Abbildung06

Nehmen Sie ähnliche Änderungen an der Seite DepartmentsAdd.aspx vor. Geben Sie in DepartmentsAdd.aspx einen Handler für das OnInserted Ereignis von an DepartmentsObjectDataSource. Das resultierende Markup ähnelt dem folgenden Beispiel.

<asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server" 
        TypeName="ContosoUniversity.BLL.SchoolBL" DataObjectTypeName="ContosoUniversity.DAL.Department" 
        InsertMethod="InsertDepartment"  
        OnInserted="DepartmentsObjectDataSource_Inserted">

Fügen Sie in DepartmentsAdd.aspx.cs die gleiche using Anweisung hinzu:

using ContosoUniversity.BLL;

Fügen Sie den folgenden Ereignishandler hinzu:

protected void DepartmentsObjectDataSource_Inserted(object sender, ObjectDataSourceStatusEventArgs e)
{
    if (e.Exception != null)
    {
        if (e.Exception.InnerException is DuplicateAdministratorException)
        {
            var duplicateAdministratorValidator = new CustomValidator();
            duplicateAdministratorValidator.IsValid = false;
            duplicateAdministratorValidator.ErrorMessage = "Insert failed: " + e.Exception.InnerException.Message;
            Page.Validators.Add(duplicateAdministratorValidator);
            e.ExceptionHandled = true;
        }
    }
}

Sie können jetzt die Seite "DepartmentsAdd.aspx.cs " testen, um sicherzustellen, dass sie auch Versuche ordnungsgemäß verarbeitet, um eine Person zum Administrator von mehr als einer Abteilung zu machen.

Damit wird die Einführung in die Implementierung des Repositorymusters für die Verwendung des ObjectDataSource Steuerelements mit Entity Framework abgeschlossen. Weitere Informationen zum Repositorymuster und zur Testbarkeit finden Sie im MSDN-Whitepaper Testability and Entity Framework 4.0.

Im folgenden Tutorial erfahren Sie, wie Sie der Anwendung Sortier- und Filterfunktionen hinzufügen.