Uso di Entity Framework 4.0 e del controllo ObjectDataSource, parte 1: Introduzione

di Tom Dykstra

Questa serie di esercitazioni si basa sull'applicazione Web Contoso University creata dalla Introduzione con la serie di esercitazioni di Entity Framework 4.0. Se non sono state completate le esercitazioni precedenti, come punto di partenza per questa esercitazione è possibile scaricare l'applicazione creata. È anche possibile scaricare l'applicazione creata dalla serie completa di esercitazioni.

L'applicazione Web di esempio Contoso University illustra come creare applicazioni Web Forms ASP.NET usando Entity Framework 4.0 e Visual Studio 2010. L'applicazione di esempio è un sito Web per un'università contoso fittizia. Include funzionalità, come ad esempio l'ammissione di studenti, la creazione di corsi e le assegnazioni di insegnati.

L'esercitazione mostra esempi in C#. L'esempio scaricabile contiene codice sia in C# che in Visual Basic.

Database First

Esistono tre modi per usare i dati in Entity Framework: Database First, Model First e Code First. Questa esercitazione è per Database First. Per informazioni sulle differenze tra questi flussi di lavoro e indicazioni su come scegliere la soluzione migliore per lo scenario, vedere Flussi di lavoro di sviluppo di Entity Framework.

Web Form

Come la serie di Introduzione, questa serie di esercitazioni usa il modello di Web Forms ASP.NET e presuppone che si sappia usare Web Forms ASP.NET in Visual Studio. In caso contrario, vedere Introduzione con ASP.NET 4,5 Web Forms. Se si preferisce usare il framework MVC ASP.NET, vedere Introduzione con Entity Framework usando ASP.NET MVC.

Versioni software

Illustrato nell'esercitazione Funziona anche con
Windows 7 Windows 8
Visual Studio 2010 Visual Studio 2010 Express for Web. L'esercitazione non è stata testata con versioni successive di Visual Studio. Esistono molte differenze tra selezioni di menu, finestre di dialogo e modelli.
.NET 4 .NET 4.5 è compatibile con le versioni precedenti con .NET 4, ma l'esercitazione non è stata testata con .NET 4.5.
Entity Framework 4 L'esercitazione non è stata testata con versioni successive di Entity Framework. A partire da Entity Framework 5, EF usa per impostazione predefinita l'introdotto DbContext API con EF 4.1. Il controllo EntityDataSource è stato progettato per usare l'API ObjectContext . Per informazioni su come usare il controllo EntityDataSource con l'API DbContext , vedere questo post di blog.

Domande

Se si hanno domande che non sono direttamente correlate all'esercitazione, è possibile pubblicarli nel forum ASP.NET Entity Framework, nel forum entity Framework e LINQ to Entities o StackOverflow.com.

Il EntityDataSource controllo consente di creare un'applicazione molto rapidamente, ma in genere è necessario mantenere una quantità significativa di logica di business e logica di accesso ai dati nelle pagine con estensione aspx . Se si prevede che l'applicazione cresci in complessità e richieda una manutenzione continua, è possibile investire più tempo di sviluppo in anticipo per creare una struttura di applicazioni a livello n o a livelli più gestibile. Per implementare questa architettura, è possibile separare il livello di presentazione dal livello di logica di business (BLL) e dal livello di accesso ai dati (DAL). Un modo per implementare questa struttura consiste nell'usare il ObjectDataSource controllo anziché il EntityDataSource controllo. Quando si usa il controllo, si implementa il ObjectDataSource codice di accesso ai dati e quindi lo si richiama nelle pagine aspx usando un controllo con molte delle stesse funzionalità di altri controlli dell'origine dati. Ciò consente di combinare i vantaggi di un approccio a livello n con i vantaggi dell'uso di un controllo Web Forms per l'accesso ai dati.

Il ObjectDataSource controllo offre maggiore flessibilità anche in altri modi. Poiché si scrive il proprio codice di accesso ai dati, è più semplice eseguire più di sola lettura, inserimento, aggiornamento o eliminazione di un tipo di entità specifico, ovvero le attività progettate per il EntityDataSource controllo. Ad esempio, è possibile eseguire la registrazione ogni volta che un'entità viene aggiornata, archiviare i dati ogni volta che un'entità viene eliminata o aggiornare automaticamente i dati correlati in base alle esigenze quando si inserisce una riga con un valore di chiave esterna.

Logica di business e classi di repository

Un ObjectDataSource controllo funziona richiamando una classe creata. La classe include metodi che recuperano e aggiornano i dati e forniscono i nomi di tali metodi al ObjectDataSource controllo nel markup. Durante l'elaborazione di rendering o postback, le ObjectDataSource chiamate ai metodi specificati.

Oltre alle operazioni CRUD di base, la classe creata da usare con il ObjectDataSource controllo potrebbe dover eseguire la logica di business quando legge o aggiorna i ObjectDataSource dati. Ad esempio, quando si aggiorna un reparto, potrebbe essere necessario verificare che nessun altro reparto abbia lo stesso amministratore perché una persona non può essere amministratore di più di un reparto.

In una ObjectDataSource documentazione, ad esempio la panoramica della classe ObjectDataSource, il controllo chiama una classe denominata oggetto business che include sia la logica di business che la logica di accesso ai dati. In questa esercitazione verranno create classi separate per la logica di business e per la logica di accesso ai dati. La classe che incapsula la logica di accesso ai dati è denominata repository. La classe di logica di business include metodi per la logica di business e metodi di accesso ai dati, ma i metodi di accesso ai dati chiamano il repository per eseguire attività di accesso ai dati.

Si creerà anche un livello di astrazione tra BLL e DAL che facilita l'unit test automatizzato del BLL. Questo livello di astrazione viene implementato creando un'interfaccia e usando l'interfaccia quando si crea un'istanza del repository nella classe business-logic. In questo modo è possibile fornire alla classe business-logic un riferimento a qualsiasi oggetto che implementa l'interfaccia del repository. Per un'operazione normale, viene fornito un oggetto repository che funziona con Entity Framework. Per il test, è possibile fornire un oggetto repository che funziona con i dati archiviati in modo che sia possibile modificare facilmente, ad esempio variabili di classe definite come raccolte.

La figura seguente illustra la differenza tra una classe business-logic che include la logica di accesso ai dati senza un repository e una che usa un repository.

Immagine05

Si inizierà creando pagine Web in cui il ObjectDataSource controllo è associato direttamente a un repository perché esegue solo attività di accesso ai dati di base. Nell'esercitazione successiva si creerà una classe di logica di business con la logica di convalida e si associa il ObjectDataSource controllo a tale classe anziché alla classe repository. Si creeranno anche unit test per la logica di convalida. Nella terza esercitazione di questa serie si aggiungeranno funzionalità di ordinamento e filtro all'applicazione.

Le pagine create in questa esercitazione funzionano con il Departments set di entità del modello di dati creato nella serie di esercitazioni di Introduzione.

Screenshot che mostra l'aspetto della pagina Reparti.

Immagine02

Aggiornamento del database e del modello di dati

Questa esercitazione verrà avviata eseguendo due modifiche al database, entrambe le quali richiedono modifiche corrispondenti al modello di dati creato nell'Introduzione con le esercitazioni di Entity Framework e Web Forms. In una di queste esercitazioni sono state apportate modifiche manualmente nella finestra di progettazione per sincronizzare il modello di dati con il database dopo una modifica del database. In questa esercitazione si userà lo strumento Update Model From Database della finestra di progettazione per aggiornare automaticamente il modello di dati.

Aggiunta di una relazione al database

In Visual Studio aprire l'applicazione Web Contoso University creata nell'Introduzione con la serie di esercitazioni Entity Framework e Web Forms e quindi aprire il diagramma del SchoolDiagram database.

Se si esamina la Department tabella nel diagramma del database, si noterà che ha una Administrator colonna. Questa colonna è una chiave esterna alla Person tabella, ma non viene definita alcuna relazione di chiave esterna nel database. È necessario creare la relazione e aggiornare il modello di dati in modo che Entity Framework possa gestire automaticamente questa relazione.

Nel diagramma di database fare clic con il pulsante destro del mouse sulla Department tabella e selezionare Relazioni.

Immagine80

Nella casella Relazioni chiave esterna fare clic su Aggiungi, quindi fare clic sui puntini di sospensione per tabelle e specifiche colonne.

Immagine81

Nella finestra di dialogo Tabelle e colonne impostare la tabella e il campo chiave primaria su Person e PersonIDe e impostare la tabella chiave esterna e il campo su Department e Administrator. Quando si esegue questa operazione, il nome della relazione verrà modificato da FK_Department_Department a FK_Department_Person.)

Immagine82

Fare clic su OK nella casella Tabelle e colonne , fare clic su Chiudi nella casella Relazioni chiave esterna e salvare le modifiche. Se viene chiesto se si desidera salvare le Person tabelle e Department , fare clic su .

Nota

Se sono state eliminate Person righe che corrispondono ai dati già presenti nella Administrator colonna, non sarà possibile salvare questa modifica. In tal caso, usare l'editor di tabelle in Esplora server per assicurarsi che il Administrator valore in ogni Department riga contenga l'ID di un record effettivamente presente nella Person tabella.

Dopo aver salvato la modifica, non sarà possibile eliminare una riga dalla Person tabella se tale persona è un amministratore del reparto. In un'applicazione di produzione viene fornito un messaggio di errore specifico quando un vincolo di database impedisce l'eliminazione o si specifica un'eliminazione a catena. Per un esempio di come specificare un'eliminazione a catena, vedere Entity Framework e ASP.NET – Introduzione Parte 2.

Aggiunta di una visualizzazione al database

Nella nuova pagina Department.aspx che si creerà, si vuole specificare un elenco a discesa degli insegnanti, con nomi in formato "ultimo, primo" in modo che gli utenti possano selezionare amministratori del reparto. Per semplificare questa operazione, si creerà una visualizzazione nel database. La visualizzazione sarà costituita solo dai dati necessari per l'elenco a discesa: il nome completo (formattato correttamente) e la chiave del record.

In Esplora server espandere School.mdf, fare clic con il pulsante destro del mouse sulla cartella Views e scegliere Aggiungi nuova visualizzazione.

Image06

Fare clic su Chiudi quando viene visualizzata la finestra di dialogo Aggiungi tabella e incollare l'istruzione SQL seguente nel riquadro SQL:

SELECT        LastName + ',' + FirstName AS FullName, PersonID
FROM          dbo.Person
WHERE        (HireDate IS NOT NULL)

Salvare la vista come vInstructorName.

Aggiornamento del modello di dati

Nella cartella DAL aprire il file SchoolModel.edmx , fare clic con il pulsante destro del mouse sull'area di progettazione e scegliere Aggiorna modello dal database.

Immagine07

Nella finestra di dialogo Scegli oggetti di database selezionare la scheda Aggiungi e selezionare la visualizzazione appena creata.

Immagine08

Fare clic su Fine.

Nella finestra di progettazione si noterà che lo strumento ha creato un'entità vInstructorName e una nuova associazione tra le Department entità e Person .

Immagine13

Nota

Nelle finestre Output ed Elenco errori potrebbe essere visualizzato un messaggio di avviso che informa che lo strumento ha creato automaticamente una chiave primaria per la nuova vInstructorName visualizzazione. Si tratta di un comportamento previsto.

Quando si fa riferimento alla nuova vInstructorName entità nel codice, non si vuole usare la convenzione di database per anteporre un prefisso "v" minuscolo. Di conseguenza, si rinominano l'entità e il set di entità nel modello.

Aprire il Visualizzatore modelli. Viene visualizzato vInstructorName come tipo di entità e una vista.

Immagine14

In SchoolModel (non SchoolModel.Store) fare clic con il pulsante destro del mouse su vInstructorName e scegliere Proprietà. Nella finestra Proprietà modificare la proprietà Name in "InstructorName" e modificare la proprietà Entity Set Name in "InstructorNames".

Immagine15

Salvare e chiudere il modello di dati e quindi ricompilare il progetto.

Uso di una classe repository e di un controllo ObjectDataSource

Creare un nuovo file di classe nella cartella DAL , denominarlo SchoolRepository.cs e sostituire il codice esistente con il codice seguente:

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

namespace ContosoUniversity.DAL
{
    public class SchoolRepository : IDisposable
    {
        private SchoolEntities context = new SchoolEntities();

        public IEnumerable<Department> GetDepartments()
        {
            return context.Departments.Include("Person").ToList();
        }

        private bool disposedValue = false;

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

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

    }
}

Questo codice fornisce un singolo GetDepartments metodo che restituisce tutte le entità nel Departments set di entità. Poiché si sa che si accederà alla Person proprietà di navigazione per ogni riga restituita, è necessario specificare il caricamento eager per tale proprietà usando il Include metodo . La classe implementa anche l'interfaccia IDisposable per assicurarsi che la connessione al database venga rilasciata quando l'oggetto viene eliminato.

Nota

Una procedura comune consiste nel creare una classe di repository per ogni tipo di entità. In questa esercitazione viene usata una classe di repository per più tipi di entità. Per altre informazioni sul modello di repository, vedere i post nel blog del team di Entity Framework e nel blog di Julie Lerman.

Il GetDepartments metodo restituisce un IEnumerable oggetto anziché un IQueryable oggetto per garantire che l'insieme restituito sia utilizzabile anche dopo l'eliminazione dell'oggetto repository stesso. Un IQueryable oggetto può causare l'accesso al database ogni volta che si accede, ma l'oggetto repository potrebbe essere eliminato dal momento in cui un controllo in ingresso tenta di eseguire il rendering dei dati. È possibile restituire un altro tipo di raccolta, ad esempio un IList oggetto anziché un IEnumerable oggetto . Tuttavia, la restituzione di un IEnumerable oggetto garantisce che sia possibile eseguire normali attività di elaborazione degli elenchi di sola lettura, ad foreach esempio cicli e query LINQ, ma non è possibile aggiungere o rimuovere elementi nella raccolta, il che potrebbe implicare che tali modifiche verrebbero rese persistenti nel database.

Creare una pagina Departments.aspx che usa la pagina master Site.Master e aggiungere il markup seguente nel Content controllo denominato Content2:

<h2>Departments</h2>
    <asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server" 
        TypeName="ContosoUniversity.DAL.SchoolRepository" 
        DataObjectTypeName="ContosoUniversity.DAL.Department" 
        SelectMethod="GetDepartments" >
    </asp:ObjectDataSource>
    <asp:GridView ID="DepartmentsGridView" runat="server" AutoGenerateColumns="False"
        DataSourceID="DepartmentsObjectDataSource"  >
        <Columns>
            <asp:CommandField ShowEditButton="True" ShowDeleteButton="True"
                ItemStyle-VerticalAlign="Top">
            </asp:CommandField>
            <asp:DynamicField DataField="Name" HeaderText="Name" SortExpression="Name" ItemStyle-VerticalAlign="Top" />
            <asp:DynamicField DataField="Budget" HeaderText="Budget" SortExpression="Budget" ItemStyle-VerticalAlign="Top" />
            <asp:DynamicField DataField="StartDate" HeaderText="Start Date" ItemStyle-VerticalAlign="Top" />
            <asp:TemplateField HeaderText="Administrator" SortExpression="Person.LastName" ItemStyle-VerticalAlign="Top" >
                <ItemTemplate>
                    <asp:Label ID="AdministratorLastNameLabel" runat="server" Text='<%# Eval("Person.LastName") %>'></asp:Label>,
                    <asp:Label ID="AdministratorFirstNameLabel" runat="server" Text='<%# Eval("Person.FirstMidName") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
        </Columns>
    </asp:GridView>

Questo markup crea un ObjectDataSource controllo che usa la classe del repository appena creata e un GridView controllo per visualizzare i dati. Il GridView controllo specifica i comandi Modifica ed Elimina , ma non è ancora stato aggiunto codice per supportarli.

Diverse colonne usano DynamicField controlli in modo che sia possibile sfruttare la funzionalità di formattazione e convalida automatica dei dati. Affinché funzionino, sarà necessario chiamare il EnableDynamicData metodo nel Page_Init gestore eventi. (DynamicControl i controlli non vengono usati nel Administrator campo perché non funzionano con le proprietà di navigazione.

Gli Vertical-Align="Top" attributi diventeranno importanti in un secondo momento quando si aggiunge una colonna con un controllo annidato GridView alla griglia.

Aprire il file Departments.aspx.cs e aggiungere l'istruzione seguente using :

using ContosoUniversity.DAL;

Aggiungere quindi il gestore seguente per l'evento della Init pagina:

protected void Page_Init(object sender, EventArgs e)
{
    DepartmentsGridView.EnableDynamicData(typeof(Department));
}

Nella cartella DAL creare un nuovo file di classe denominato Department.cs e sostituire il codice esistente con il codice seguente:

using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace ContosoUniversity.DAL
{
    [MetadataType(typeof(DepartmentMetaData))]
    public partial class Department
    {
    }

    public class DepartmentMetaData
    {
        [DataType(DataType.Currency)]
        [Range(0, 1000000, ErrorMessage = "Budget must be less than $1,000,000.00")]
        public Decimal Budget { get; set; }

        [DisplayFormat(DataFormatString="{0:d}",ApplyFormatInEditMode=true)]
        public DateTime StartDate { get; set; }

    }
}

Questo codice aggiunge metadati al modello di dati. Specifica che la proprietà dell'entità rappresenta effettivamente la BudgetDepartment valuta anche se il tipo di dati è Decimale specifica che il valore deve essere compreso tra 0 e $1.000.000.00. Specifica inoltre che la StartDate proprietà deve essere formattata come data nel formato mm/gg/a.

Eseguire la pagina Departments.aspx .

Screenshot che mostra la pagina Reparti quando è stata eseguita.

Si noti che, anche se non è stata specificata una stringa di formato nel markup di pagina Departments.aspx per le colonne Budget o Start Date , la formattazione predefinita valuta e data è stata applicata dai DynamicField controlli utilizzando i metadati forniti nel file Department.cs .

Aggiunta di funzionalità di inserimento ed eliminazione

Aprire SchoolRepository.cs, aggiungere il codice seguente per creare un Insert metodo e un Delete metodo. Il codice include anche un metodo denominato GenerateDepartmentID che calcola il valore successivo della chiave record disponibile da utilizzare dal Insert metodo . Questa operazione è necessaria perché il database non è configurato per calcolare automaticamente questa impostazione per la Department tabella.

public void InsertDepartment(Department department)
{
    try
    {
        department.DepartmentID = GenerateDepartmentID();
        context.Departments.AddObject(department);
        context.SaveChanges();
    }
    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
    {
        context.Departments.Attach(department);
        context.Departments.DeleteObject(department);
        context.SaveChanges();
    }
    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;
    }
}

private Int32 GenerateDepartmentID()
{
    Int32 maxDepartmentID = 0;
    var department = (from d in GetDepartments()
                      orderby d.DepartmentID descending
                      select d).FirstOrDefault();
    if (department != null)
    {
        maxDepartmentID = department.DepartmentID + 1;
    }
    return maxDepartmentID;
}

Metodo Attach

Il DeleteDepartment metodo chiama il Attach metodo per ristabilire il collegamento gestito nel gestore dello stato dell'oggetto del contesto dell'oggetto tra l'entità in memoria e la riga del database rappresentata. Questa operazione deve verificarsi prima che il metodo chiami il SaveChanges metodo .

Il termine contesto dell'oggetto fa riferimento alla classe Entity Framework che deriva dalla ObjectContext classe usata per accedere ai set di entità e alle entità. Nel codice per questo progetto la classe è denominata SchoolEntitiese un'istanza di è sempre denominata context. Il gestore dello stato dell'oggetto del contesto dell'oggetto è una classe che deriva dalla ObjectStateManager classe . Il contatto dell'oggetto utilizza il gestore dello stato dell'oggetto per archiviare gli oggetti entità e tenere traccia del fatto che ognuno sia sincronizzato con la riga o le righe della tabella corrispondenti nel database.

Quando si legge un'entità, il contesto dell'oggetto lo archivia nel gestore dello stato dell'oggetto e tiene traccia del fatto che tale rappresentazione dell'oggetto sia sincronizzata con il database. Ad esempio, se si modifica un valore di proprietà, viene impostato un flag per indicare che la proprietà modificata non è più sincronizzata con il database. Quindi, quando si chiama il SaveChanges metodo , il contesto dell'oggetto sa cosa fare nel database perché il gestore dello stato dell'oggetto sa esattamente cosa è diverso tra lo stato corrente dell'entità e lo stato del database.

Tuttavia, questo processo in genere non funziona in un'applicazione Web, perché l'istanza del contesto dell'oggetto che legge un'entità, insieme a tutto il relativo gestore dello stato degli oggetti, viene eliminata dopo il rendering di una pagina. L'istanza del contesto dell'oggetto che deve applicare le modifiche è una nuova istanza per l'elaborazione postback. Nel caso del DeleteDepartment metodo , il ObjectDataSource controllo ricrea la versione originale dell'entità da valori nello stato di visualizzazione, ma questa entità ricreata Department non esiste nel gestore dello stato dell'oggetto. Se è stato chiamato il DeleteObject metodo su questa entità ricreata, la chiamata avrà esito negativo perché il contesto dell'oggetto non sa se l'entità è sincronizzata con il database. Tuttavia, chiamando il Attach metodo viene ricreato lo stesso rilevamento tra l'entità ricreata e i valori nel database che sono stati originariamente eseguiti automaticamente quando l'entità è stata letta in un'istanza precedente del contesto dell'oggetto.

In alcuni casi il contesto dell'oggetto non deve tenere traccia delle entità nel gestore dello stato dell'oggetto ed è possibile impostare flag per impedirne l'esecuzione. Alcuni esempi sono illustrati nelle esercitazioni successive di questa serie.

Metodo SaveChanges

Questa semplice classe di repository illustra i principi di base di come eseguire operazioni CRUD. In questo esempio il SaveChanges metodo viene chiamato immediatamente dopo ogni aggiornamento. In un'applicazione di produzione si potrebbe voler chiamare il SaveChanges metodo da un metodo separato per ottenere un maggiore controllo su quando il database viene aggiornato. Alla fine dell'esercitazione successiva si troverà un collegamento a un white paper che illustra l'unità di lavoro che è un approccio al coordinamento degli aggiornamenti correlati. Si noti anche che nell'esempio il DeleteDepartment metodo non include codice per la gestione dei conflitti di concorrenza. Il codice a tale scopo verrà aggiunto in un'esercitazione successiva di questa serie.

Recupero dei nomi degli insegnanti da selezionare durante l'inserimento

Gli utenti devono essere in grado di selezionare un amministratore da un elenco a discesa in un elenco a discesa durante la creazione di nuovi reparti. Aggiungere quindi il codice seguente a SchoolRepository.cs per creare un metodo per recuperare l'elenco di insegnanti usando la vista creata in precedenza:

public IEnumerable<InstructorName> GetInstructorNames()
{
    return context.InstructorNames.OrderBy("it.FullName").ToList();
}

Creazione di una pagina per l'inserimento di reparti

Creare una pagina DepartmentsAdd.aspx che usa la pagina Site.Master e aggiungere il markup seguente nel Content controllo denominato Content2:

<h2>Departments</h2>
    <asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server" 
        TypeName="ContosoUniversity.DAL.SchoolRepository" DataObjectTypeName="ContosoUniversity.DAL.Department"
        InsertMethod="InsertDepartment" >
    </asp:ObjectDataSource>
    <asp:DetailsView ID="DepartmentsDetailsView" runat="server" 
        DataSourceID="DepartmentsObjectDataSource" AutoGenerateRows="False"
        DefaultMode="Insert" OnItemInserting="DepartmentsDetailsView_ItemInserting">
        <Fields>
            <asp:DynamicField DataField="Name" HeaderText="Name" />
            <asp:DynamicField DataField="Budget" HeaderText="Budget" />
            <asp:DynamicField DataField="StartDate" HeaderText="Start Date" />
            <asp:TemplateField HeaderText="Administrator">
                <InsertItemTemplate>
                    <asp:ObjectDataSource ID="InstructorsObjectDataSource" runat="server" 
                        TypeName="ContosoUniversity.DAL.SchoolRepository" 
                        DataObjectTypeName="ContosoUniversity.DAL.InstructorName"
                        SelectMethod="GetInstructorNames" >
                    </asp:ObjectDataSource>
                    <asp:DropDownList ID="InstructorsDropDownList" runat="server" 
                        DataSourceID="InstructorsObjectDataSource"
                        DataTextField="FullName" DataValueField="PersonID" OnInit="DepartmentsDropDownList_Init">
                    </asp:DropDownList>
                </InsertItemTemplate>
            </asp:TemplateField>
            <asp:CommandField ShowInsertButton="True" />
        </Fields>
    </asp:DetailsView>
   <asp:ValidationSummary ID="DepartmentsValidationSummary" runat="server" 
        ShowSummary="true" DisplayMode="BulletList"  />

Questo markup crea due ObjectDataSource controlli, uno per l'inserimento di nuove Department entità e uno per il recupero dei nomi degli insegnanti per il DropDownList controllo usato per la selezione degli amministratori del reparto. Il markup crea un DetailsView controllo per l'immissione di nuovi reparti e specifica un gestore per l'evento del ItemInserting controllo in modo da poter impostare il valore della Administrator chiave esterna. Alla fine è un ValidationSummary controllo per visualizzare i messaggi di errore.

Aprire DepartmentsAdd.aspx.cs e aggiungere l'istruzione seguente using :

using ContosoUniversity.DAL;

Aggiungere la variabile di classe e i metodi seguenti:

private DropDownList administratorsDropDownList;

protected void Page_Init(object sender, EventArgs e)
{
    DepartmentsDetailsView.EnableDynamicData(typeof(Department));
}

protected void DepartmentsDropDownList_Init(object sender, EventArgs e)
{
    administratorsDropDownList = sender as DropDownList;
}

protected void DepartmentsDetailsView_ItemInserting(object sender, DetailsViewInsertEventArgs e)
{
    e.Values["Administrator"] = administratorsDropDownList.SelectedValue;
}

Il Page_Init metodo abilita la funzionalità Dynamic Data. Il gestore dell'evento DropDownList del Init controllo salva un riferimento al controllo e il gestore per l'evento DetailsView del Inserting controllo usa tale riferimento per ottenere il PersonID valore dell'insegnante selezionato e aggiornare la Administrator proprietà di chiave esterna dell'entità Department .

Eseguire la pagina, aggiungere informazioni per un nuovo reparto e quindi fare clic sul collegamento Inserisci .

Immagine04

Immettere i valori per un altro nuovo reparto. Immettere un numero maggiore di 1.000.000.00 nel campo Budget e nella scheda al campo successivo. Nel campo viene visualizzato un asterisco e, se si tiene premuto il puntatore del mouse, è possibile visualizzare il messaggio di errore immesso nei metadati per tale campo.

Immagine03

Fare clic su Inserisci e viene visualizzato il messaggio di errore visualizzato dal ValidationSummary controllo nella parte inferiore della pagina.

Immagine12

Chiudere quindi il browser e aprire la pagina Departments.aspx . Aggiungere funzionalità di eliminazione alla pagina Departments.aspx aggiungendo un DeleteMethod attributo al ObjectDataSource controllo e un DataKeyNames attributo al GridView controllo. I tag di apertura per questi controlli saranno ora simili all'esempio seguente:

<asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server" 
        TypeName="ContosoUniversity.DAL.SchoolRepository" 
        DataObjectTypeName="ContosoUniversity.DAL.Department"
        SelectMethod="GetDepartments" 
        DeleteMethod="DeleteDepartment" >

    <asp:GridView ID="DepartmentsGridView" runat="server" AutoGenerateColumns="False"
        DataSourceID="DepartmentsObjectDataSource" DataKeyNames="DepartmentID" >

Eseguire la pagina.

Screenshot che mostra la pagina Reparti dopo l'esecuzione.

Eliminare il reparto aggiunto durante l'esecuzione della pagina DepartmentAdd.aspx .

Aggiunta di funzionalità di aggiornamento

Aprire SchoolRepository.cs e aggiungere il metodo seguente Update :

public void UpdateDepartment(Department department, Department origDepartment)
{
    try
    {
        context.Departments.Attach(origDepartment);
        context.ApplyCurrentValues("Departments", department);
        context.SaveChanges();
    }
    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;
    }
}

Quando si fa clic su Aggiorna nella pagina Departments.aspx , il ObjectDataSource controllo crea due Department entità da passare al UpdateDepartment metodo. Uno contiene i valori originali archiviati nello stato di visualizzazione e l'altro contiene i nuovi valori immessi nel GridView controllo. Il codice nel UpdateDepartment metodo passa l'entità Department con i valori originali al Attach metodo per stabilire il rilevamento tra l'entità e ciò che si trova nel database. Il codice passa quindi l'entità Department con i nuovi valori al ApplyCurrentValues metodo. Il contesto dell'oggetto confronta i valori precedenti e nuovi. Se un nuovo valore è diverso da un valore precedente, il contesto dell'oggetto modifica il valore della proprietà. Il SaveChanges metodo aggiorna quindi solo le colonne modificate nel database. Tuttavia, se la funzione di aggiornamento per questa entità è stata mappata a una stored procedure, l'intera riga verrà aggiornata indipendentemente dalle colonne modificate.

Aprire il file Departments.aspx e aggiungere gli attributi seguenti al DepartmentsObjectDataSource controllo:

  • UpdateMethod="UpdateDepartment"
  • ConflictDetection="CompareAllValues"
    In questo modo, i valori precedenti devono essere archiviati nello stato di visualizzazione in modo che possano essere confrontati con i nuovi valori nel Update metodo.
  • OldValuesParameterFormatString="orig{0}"
    In questo modo viene informato il controllo che il nome del parametro dei valori originali è origDepartment .

Il markup per il tag di apertura del ObjectDataSource controllo ora è simile all'esempio seguente:

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

Aggiungere un OnRowUpdating="DepartmentsGridView_RowUpdating" attributo al GridView controllo. Verrà usato per impostare il valore della Administrator proprietà in base alla riga selezionata dall'utente in un elenco a discesa. Il tag di apertura ora è simile all'esempio GridView seguente:

<asp:GridView ID="DepartmentsGridView" runat="server" AutoGenerateColumns="False"
        DataSourceID="DepartmentsObjectDataSource" DataKeyNames="DepartmentID"
        OnRowUpdating="DepartmentsGridView_RowUpdating">

Aggiungere un EditItemTemplate controllo per la Administrator colonna al GridView controllo, immediatamente dopo il ItemTemplate controllo per tale colonna:

<EditItemTemplate>
                    <asp:ObjectDataSource ID="InstructorsObjectDataSource" runat="server" DataObjectTypeName="ContosoUniversity.DAL.InstructorName"
                        SelectMethod="GetInstructorNames" TypeName="ContosoUniversity.DAL.SchoolRepository">
                    </asp:ObjectDataSource>
                    <asp:DropDownList ID="InstructorsDropDownList" runat="server" DataSourceID="InstructorsObjectDataSource"
                        SelectedValue='<%# Eval("Administrator")  %>'
                        DataTextField="FullName" DataValueField="PersonID" OnInit="DepartmentsDropDownList_Init" >
                    </asp:DropDownList>
                </EditItemTemplate>

Questo EditItemTemplate controllo è simile al InsertItemTemplate controllo nella pagina RepartiAggiungi.aspx . La differenza è che il valore iniziale del controllo viene impostato usando l'attributo SelectedValue .

Prima del GridView controllo, aggiungere un ValidationSummary controllo come si è fatto nella pagina RepartiAggiungi.aspx .

<asp:ValidationSummary ID="DepartmentsValidationSummary" runat="server" 
        ShowSummary="true" DisplayMode="BulletList"  />

Aprire Departments.aspx.cs e immediatamente dopo la dichiarazione di classe parziale, aggiungere il codice seguente per creare un campo privato per fare riferimento al DropDownList controllo:

private DropDownList administratorsDropDownList;

Aggiungere quindi gestori per l'evento DropDownList del controllo e l'evento GridView del InitRowUpdating controllo:

protected void DepartmentsDropDownList_Init(object sender, EventArgs e)
{
    administratorsDropDownList = sender as DropDownList;
}

protected void DepartmentsGridView_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
    e.NewValues["Administrator"] = administratorsDropDownList.SelectedValue;
}

Il gestore per l'evento InitDropDownList salva un riferimento al controllo nel campo della classe. Il gestore per l'evento RowUpdating usa il riferimento per ottenere il valore immesso dall'utente e applicarlo alla Administrator proprietà dell'entità Department .

Utilizzare la pagina DepartmentAdd.aspx per aggiungere un nuovo reparto, quindi eseguire la pagina Department.aspx e fare clic su Modifica nella riga aggiunta.

Nota

Non sarà possibile modificare le righe non aggiunte, ovvero già presenti nel database, a causa di dati non validi nel database; gli amministratori per le righe create con il database sono studenti. Se si tenta di modificarli, verrà visualizzata una pagina di errore che segnala un errore come 'InstructorsDropDownList' has a SelectedValue which is invalid because it does not exist in the list of items.

Immagine10

Se si immette un importo budget non valido e quindi si fa clic su Aggiorna, viene visualizzato lo stesso asterisco e messaggio di errore visualizzato nella pagina Reparti.aspx .

Modificare un valore di campo o selezionare un amministratore diverso e fare clic su Aggiorna. Viene visualizzata la modifica.

Screenshot che mostra la pagina Reparti.

Questa operazione completa l'introduzione all'uso del ObjectDataSource controllo per operazioni CRUD di base (creare, leggere, aggiornare, eliminare) con Entity Framework. È stata creata un'applicazione semplice a livello n, ma il livello di logica aziendale è ancora strettamente associato al livello di accesso ai dati, che complica gli unit test automatizzati. Nell'esercitazione seguente verrà illustrato come implementare il modello di repository per facilitare l'unit test.