Freigeben über


Aktualisieren in Batches (C#)

von Scott Mitchell

PDF herunterladen

Erfahren Sie, wie Sie mehrere Datenbankdatensätze in einem einzelnen Vorgang aktualisieren. In der Benutzeroberflächenebene erstellen wir ein GridView-Objekt, in dem jede Zeile bearbeitet werden kann. Auf der Datenzugriffsebene werden die verschiedenen Updatevorgänge innerhalb einer Transaktion umgebrochen, um sicherzustellen, dass alle Updates erfolgreich sind oder dass ein Rollback für alle Updates ausgeführt wird.

Einführung

Im vorherigen Tutorial haben wir erfahren, wie Sie die Datenzugriffsebene erweitern, um Unterstützung für Datenbanktransaktionen hinzuzufügen. Datenbanktransaktionen garantieren, dass eine Reihe von Datenänderungsanweisungen als ein atomischer Vorgang behandelt wird, wodurch sichergestellt wird, dass alle Änderungen fehlschlagen oder alle erfolgreich sind. Mit dieser low-level DAL-Funktionalität sind wir bereit, unsere Aufmerksamkeit auf die Erstellung von Batchdatenänderungsschnittstellen zu richten.

In diesem Tutorial erstellen wir ein GridView-Objekt, in dem jede Zeile bearbeitet werden kann (siehe Abbildung 1). Da jede Zeile in ihrer Bearbeitungsoberfläche gerendert wird, ist keine Spalte der Schaltflächen Bearbeiten, Aktualisieren und Abbrechen erforderlich. Stattdessen gibt es zwei Schaltflächen Produkte aktualisieren auf der Seite, die bei Klick die GridView-Zeilen aufzählen und die Datenbank aktualisieren.

Jede Zeile in GridView kann bearbeitet werden.

Abbildung 1: Jede Zeile in GridView ist bearbeitbar (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Lassen Sie uns loslegen!

Hinweis

Im Tutorial Ausführen von Batch Updates haben wir eine Batchbearbeitungsschnittstelle mithilfe des DataList-Steuerelements erstellt. Dieses Tutorial unterscheidet sich vom vorherigen Tutorial darin, dass eine GridView verwendet wird und die Batchaktualisierung innerhalb des Bereichs einer Transaktion ausgeführt wird. Nach Abschluss dieses Tutorials ermuntern Sie, zum vorherigen Tutorial zurückzukehren und es so zu aktualisieren, dass die datenbanktransaktionsbezogene Funktionalität verwendet wird, die im vorherigen Tutorial hinzugefügt wurde.

Untersuchen der Schritte zum Bearbeiten aller GridView-Zeilen

Wie im Tutorial Eine Übersicht über das Einfügen, Aktualisieren und Löschen von Daten erläutert, bietet GridView integrierte Unterstützung für die Zeilenbearbeitung der zugrunde liegenden Daten. Intern notiert gridView, welche Zeile über die EditIndex -Eigenschaft bearbeitet werden kann. Da gridView an seine Datenquelle gebunden wird, überprüft es jede Zeile, um festzustellen, ob der Index der Zeile dem Wert von EditIndexentspricht. Wenn ja, werden diese Zeilenfelder mithilfe ihrer Bearbeitungsschnittstellen gerendert. Für BoundFields ist die Bearbeitungsschnittstelle ein TextBox-Objekt, dessen Text Eigenschaft dem Wert des Datenfelds zugewiesen wird, das von der BoundField s-Eigenschaft DataField angegeben wird. Für TemplateFields wird anstelle EditItemTemplate von ItemTemplateverwendet.

Denken Sie daran, dass der Bearbeitungsworkflow gestartet wird, wenn ein Benutzer auf die Schaltfläche Bearbeiten einer Zeile klickt. Dies führt zu einem Postback, legt die GridView-Eigenschaft auf EditIndex den Index des geklickten Zeilens fest und binden die Daten erneut an das Raster. Wenn auf die Schaltfläche Abbrechen einer Zeile geklickt wird, wird beim Postback auf EditIndex den Wert von -1 festgelegt, bevor die Daten erneut an das Raster binden. Da die Zeilen von GridView die Indizierung bei Null beginnen, hat die Einstellung EditIndex auf -1 die Auswirkung, dass gridView im schreibgeschützten Modus angezeigt wird.

Die EditIndex -Eigenschaft eignet sich gut für die Zeilenbearbeitung, ist aber nicht für die Batchbearbeitung konzipiert. Damit das gesamte GridView bearbeitbar ist, muss jede Zeile mithilfe der Bearbeitungsoberfläche gerendert werden. Die einfachste Möglichkeit, dies zu erreichen, besteht darin, zu erstellen, wo jedes bearbeitbare Feld als TemplateField implementiert wird, dessen Bearbeitungsschnittstelle im ItemTemplatedefiniert ist.

In den nächsten Schritten erstellen wir eine vollständig bearbeitbare GridView. In Schritt 1 beginnen wir mit dem Erstellen von GridView und seiner ObjectDataSource und konvertieren die zugehörigen BoundFields und CheckBoxField in TemplateFields. In Den Schritten 2 und 3 verschieben wir die Bearbeitungsschnittstellen von den TemplateFields EditItemTemplate in die ItemTemplate entsprechenden.

Schritt 1: Anzeigen von Produktinformationen

Bevor wir uns gedanken über das Erstellen einer GridView machen, bei der Zeilen bearbeitbar sind, beginnen wir mit der Anzeige der Produktinformationen. Öffnen Sie die BatchUpdate.aspx Seite im BatchData Ordner, und ziehen Sie ein GridView-Objekt aus der Toolbox auf die Designer. Legen Sie die GridViews ID auf ProductsGrid fest, und wählen Sie aus ihrem Smarttag aus, um sie an eine neue ObjectDataSource namens ProductsDataSourcezu binden. Konfigurieren Sie die ObjectDataSource, um ihre Daten aus der s-Methode GetProducts der ProductsBLL Klasse abzurufen.

Konfigurieren der ObjectDataSource für die Verwendung der ProductsBLL-Klasse

Abbildung 2: Konfigurieren der ObjectDataSource für die Verwendung der ProductsBLL -Klasse (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Abrufen der Produktdaten mithilfe der GetProducts-Methode

Abbildung 3: Abrufen der Produktdaten mithilfe der GetProducts -Methode (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Wie gridView sind die Änderungsfeatures von ObjectDataSource so konzipiert, dass sie zeilenweise funktionieren. Um eine Reihe von Datensätzen zu aktualisieren, müssen wir ein wenig Code in die CodeBehind-Klasse der ASP.NET Seite schreiben, die die Daten in Batches übergibt und sie an die BLL übergibt. Legen Sie daher die Dropdownlisten in den Registerkarten UPDATE, INSERT und DELETE von ObjectDataSource auf (Keine) fest. Klicken Sie auf Fertig stellen, um den Assistenten abzuschließen.

Legen Sie die Drop-Down Listen in den Registerkarten UPDATE, INSERT und DELETE auf (Keine) fest.

Abbildung 4: Festlegen der Drop-Down Listen in den Registerkarten UPDATE, INSERT und DELETE auf (Keine) (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Nach Abschluss des Assistenten zum Konfigurieren von Datenquellen sollte das deklarative Markup von ObjectDataSource wie folgt aussehen:

<asp:ObjectDataSource ID="ProductsDataSource" runat="server" 
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetProducts" TypeName="ProductsBLL">
</asp:ObjectDataSource>

Wenn Sie den Assistenten zum Konfigurieren von Datenquellen ausführen, erstellt Visual Studio auch BoundFields und ein CheckBoxField für die Produktdatenfelder in GridView. In diesem Tutorial können Benutzer nur den Namen, die Kategorie, den Preis und die eingestellten status anzeigen und bearbeiten. Entfernen Sie alle Felder außer ProductName, CategoryName, UnitPriceund Discontinued , und benennen Sie die HeaderText Eigenschaften der ersten drei Felder in Product, Category und Price um. Aktivieren Sie schließlich die Kontrollkästchen Paging aktivieren und Sortierung aktivieren im Smarttag von GridView.

An diesem Punkt verfügt gridView über drei BoundFields (ProductName, CategoryName, und UnitPrice) und ein CheckBoxField (Discontinued). Wir müssen diese vier Felder in TemplateFields konvertieren und dann die Bearbeitungsoberfläche von den TemplateFields EditItemTemplate in die zugehörige ItemTemplateverschieben.

Hinweis

Das Erstellen und Anpassen von TemplateFields wurde im Tutorial Anpassen der Datenänderungsschnittstelle untersucht. Wir werden die Schritte zum Konvertieren von BoundFields und CheckBoxField in TemplateFields und zum Definieren der Bearbeitungsschnittstellen in ihren ItemTemplate s durchlaufen. Wenn Sie jedoch hängen bleiben oder eine Auffrischung benötigen, zögern Sie nicht, auf dieses frühere Tutorial zurück zu verweisen.

Klicken Sie im Smarttag von GridView auf den Link Spalten bearbeiten, um das Dialogfeld Felder zu öffnen. Wählen Sie als Nächstes jedes Feld aus, und klicken Sie auf den Link Dieses Feld in ein TemplateField konvertieren.

Konvertieren der vorhandenen BoundFields und CheckBoxField in TemplateField

Abbildung 5: Konvertieren der vorhandenen BoundFields und CheckBoxField in TemplateField

Nachdem jedes Feld ein TemplateField ist, können wir die Bearbeitungsoberfläche nun von den EditItemTemplate s in die ItemTemplate s verschieben.

Schritt 2: Erstellen derProductNameUnitPrice Schnittstellen, undDiscontinuedBearbeitungsschnittstellen

Das Erstellen der ProductNameSchnittstellen , UnitPrice, und Discontinued ist das Thema dieses Schritts und ziemlich einfach, da jede Schnittstelle bereits in TemplateField definiert EditItemTemplateist. Das Erstellen der CategoryName Bearbeitungsoberfläche ist etwas komplizierter, da wir eine DropDownList der entsprechenden Kategorien erstellen müssen. Diese CategoryName Bearbeitungsschnittstelle wird in Schritt 3 behandelt.

Beginnen wir mit dem ProductName TemplateField. Klicken Sie im Smarttag von GridView auf den Link Vorlagen bearbeiten, und führen Sie einen Drilldown zu TemplateField ProductName s aus EditItemTemplate. Wählen Sie das Textfeld aus, kopieren Sie es in die Zwischenablage, und fügen Sie es dann in templateField ProductName ein ItemTemplate. Ändern Sie die TextBox-Eigenschaft in IDProductName.

Fügen Sie als Nächstes einen RequiredFieldValidator zu hinzu ItemTemplate , um sicherzustellen, dass der Benutzer einen Wert für jeden Produktnamen bereitstellt. Legen Sie die ControlToValidate -Eigenschaft auf ProductName und die ErrorMessage -Eigenschaft auf Sie müssen den Namen des Produkts angeben fest. und die Text -Eigenschaft auf *. Nachdem Sie diese Ergänzungen am ItemTemplategemacht haben, sollte Ihr Bildschirm ähnlich wie Abbildung 6 aussehen.

Das ProductName TemplateField enthält jetzt ein TextBox und ein RequiredFieldValidator.

Abbildung 6: Das ProductName TemplateField enthält jetzt ein TextBox und ein RequiredFieldValidator (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Beginnen Sie für die UnitPrice Bearbeitungsoberfläche, indem Sie das TextBox-Element von in EditItemTemplate kopieren ItemTemplate. Platzieren Sie als Nächstes ein $ vor dem TextBox-Element, und legen Sie seine ID Eigenschaft auf UnitPrice und seine Columns Eigenschaft auf 8 fest.

Fügen Sie außerdem einen CompareValidator zu den UnitPrice s ItemTemplate hinzu, um sicherzustellen, dass der vom Benutzer eingegebene Wert ein gültiger Währungswert größer oder gleich $0,00 ist. Legen Sie die Validierungseigenschaft auf ControlToValidate UnitPrice und ihre ErrorMessage Eigenschaft auf Sie müssen einen gültigen Währungswert eingeben fest. Lassen Sie alle Währungssymbole aus., die Text -Eigenschaft auf *, die Type -Eigenschaft auf Currency, die Operator -Eigenschaft auf GreaterThanEqual, und die ValueToCompare -Eigenschaft auf 0 .

Fügen Sie einen CompareValidator hinzu, um sicherzustellen, dass der eingegebene Preis ein nicht negativer Währungswert ist.

Abbildung 7: Hinzufügen eines CompareValidator-Werts, um sicherzustellen, dass der eingegebene Preis ein nicht negativer Währungswert ist (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Für das Discontinued TemplateField können Sie das bereits im definierten ItemTemplateCheckBox verwenden. Legen Sie einfach auf ID Discontinued und die Enabled -Eigenschaft auf fest true.

Schritt 3: Erstellen der BearbeitungsschnittstelleCategoryName

Die Bearbeitungsoberfläche in den CategoryName TemplateFields EditItemTemplate enthält ein TextBox-Objekt, das den Wert des CategoryName Datenfelds anzeigt. Wir müssen dies durch eine DropDownList ersetzen, die die möglichen Kategorien auflistet.

Hinweis

Das Tutorial Anpassen der Datenänderungsschnittstelle enthält eine ausführlichere und umfassendere Erläuterung zum Anpassen einer Vorlage, um eine DropDownList anstelle eines TextBox-Elements einzuschließen. Während die hier beschriebenen Schritte abgeschlossen sind, werden sie nur sehr kurz dargestellt. Einen ausführlicheren Einblick in das Erstellen und Konfigurieren der Kategorien DropDownList finden Sie im Tutorial Anpassen der Datenänderungsschnittstelle .

Ziehen Sie eine DropDownList aus der Toolbox auf das CategoryName TemplateField-Element, ItemTemplateund legen Sie dessen ID auf fest Categories. An diesem Punkt definieren wir in der Regel die DropDownLists-Datenquelle über das zugehörige Smarttag, wodurch eine neue ObjectDataSource erstellt wird. Dadurch wird jedoch die ObjectDataSource innerhalb von ItemTemplatehinzugefügt, was zu einer ObjectDataSource-instance führt, die für jede GridView-Zeile erstellt wird. Stattdessen erstellen wir die ObjectDataSource außerhalb von GridView s TemplateFields. Beenden Sie die Vorlagenbearbeitung, und ziehen Sie eine ObjectDataSource aus der Toolbox auf die Designer unter objectDataSourceProductsDataSource. Benennen Sie die neue ObjectDataSourceCategoriesDataSource, und konfigurieren Sie sie für die Verwendung der s-Methode GetCategories der CategoriesBLL Klasse.

Konfigurieren der ObjectDataSource für die Verwendung der CategoriesBLL-Klasse

Abbildung 8: Konfigurieren der ObjectDataSource für die Verwendung der CategoriesBLL -Klasse (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Abrufen der Kategoriedaten mithilfe der GetCategories-Methode

Abbildung 9: Abrufen der Kategoriedaten mithilfe der GetCategories -Methode (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Da diese ObjectDataSource nur zum Abrufen von Daten verwendet wird, legen Sie die Dropdownlisten in den Registerkarten UPDATE und DELETE auf (Keine) fest. Klicken Sie auf Fertig stellen, um den Assistenten abzuschließen.

Legen Sie die Drop-Down Listen auf den Registerkarten UPDATE und DELETE auf (Keine) fest.

Abbildung 10: Festlegen der Drop-Down Listen in den Registerkarten UPDATE und DELETE auf (Keine) (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Nach Abschluss des Assistenten sollte das CategoriesDataSource deklarative Markup von s wie folgt aussehen:

<asp:ObjectDataSource ID="CategoriesDataSource" runat="server" 
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>

Kehren Sie nach dem CategoriesDataSource Erstellten und Konfigurierten zu den CategoryName TemplateFields ItemTemplate zurück, und klicken Sie im Smarttag DropDownList auf den Link Datenquelle auswählen. Wählen Sie im Datenquellenkonfigurations-Assistenten die CategoriesDataSource Option aus der ersten Dropdownliste aus, und wählen Sie CategoryName für die Anzeige und CategoryID als Wert aus.

Binden der DropDownList an die CategoriesDataSource

Abbildung 11: Binden der DropDownList an die CategoriesDataSource (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

An diesem Punkt listet dropDownList Categories alle Kategorien auf, wählt aber noch nicht automatisch die entsprechende Kategorie für das Produkt aus, das an die GridView-Zeile gebunden ist. Um dies zu erreichen, müssen wir die Categories DropDownList s SelectedValue auf den Wert des CategoryID Produkts festlegen. Klicken Sie im Smarttag DropDownList auf den Link DataBindings bearbeiten, und ordnen Sie die SelectedValue Eigenschaft dem CategoryID Datenfeld zu, wie in Abbildung 12 dargestellt.

Binden des CategoryID-Werts des Produkts an die DropDownList s SelectedValue-Eigenschaft

Abbildung 12: Binden des Product s-Werts CategoryID an die DropDownList-Eigenschaft SelectedValue

Ein letztes Problem bleibt bestehen: Wenn für das Produkt kein CategoryID Wert angegeben ist, führt die Datenbindungsanweisung zu SelectedValue einer Ausnahme. Dies liegt daran, dass DropDownList nur Elemente für die Kategorien enthält und keine Option für die Produkte bietet, die über einen NULL Datenbankwert für CategoryIDverfügen. Um dies zu beheben, legen Sie die DropDownList-Eigenschaft AppendDataBoundItems auf fest true , und fügen Sie der DropDownList ein neues Element hinzu, wobei die Value Eigenschaft aus der deklarativen Syntax weggelassen wird. Stellen Sie also sicher, dass die deklarative Syntax von Categories DropDownList wie folgt aussieht:

<asp:DropDownList ID="Categories" runat="server" AppendDataBoundItems="True" 
    DataSourceID="CategoriesDataSource" DataTextField="CategoryName" 
    DataValueField="CategoryID" SelectedValue='<%# Bind("CategoryID") %>'>
    <asp:ListItem Value=">-- Select One --</asp:ListItem>
</asp:DropDownList>

Beachten Sie, dass das <asp:ListItem Value=""> -- Select One-Attribut explizit auf eine leere Zeichenfolge festgelegt ist Value . Im Tutorial Anpassen der Datenänderungsschnittstelle finden Sie eine ausführlichere Erläuterung, warum dieses zusätzliche DropDownList-Element erforderlich ist, um den NULL Fall zu behandeln, und warum die Zuweisung der Value Eigenschaft zu einer leeren Zeichenfolge unerlässlich ist.

Hinweis

Es gibt hier ein potenzielles Leistungs- und Skalierbarkeitsproblem, das erwähnenswert ist. Da jede Zeile über eine DropDownList verfügt, die den CategoriesDataSource als Datenquelle verwendet, wird die Methode der CategoriesBLL Klasse s GetCategoriesn mal pro Seitenbesuch aufgerufen, wobei n die Anzahl der Zeilen in GridView ist. Diese n-Aufrufe führen zu GetCategoriesn-Abfragen an die Datenbank. Diese Auswirkungen auf die Datenbank können verringert werden, indem die zurückgegebenen Kategorien entweder in einem Anforderungscache oder über die Cacheebene mithilfe einer SQL-Zwischenspeicherungsabhängigkeit oder eines sehr kurzen zeitbasierten Ablaufs zwischengespeichert werden.

Schritt 4: Abschließen der Bearbeitungsschnittstelle

Wir haben eine Reihe von Änderungen an den GridView-Vorlagen vorgenommen, ohne anzuhalten, um unseren Fortschritt anzuzeigen. Nehmen Sie sich einen Moment Zeit, um unseren Fortschritt über einen Browser anzuzeigen. Wie in Abbildung 13 dargestellt, wird jede Zeile mithilfe von ItemTemplategerendert, die die Bearbeitungsschnittstelle der Zelle enthält.

Jede GridView-Zeile kann bearbeitet werden.

Abbildung 13: Jede GridView-Zeile ist bearbeitbar (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Es gibt einige kleinere Formatierungsprobleme, die wir an dieser Stelle berücksichtigen sollten. Beachten Sie zunächst, dass der UnitPrice Wert vier Dezimalstellen enthält. Um dies zu beheben, kehren Sie zu TemplateField UnitPrice s ItemTemplate zurück, und klicken Sie im Smarttag TextBox auf den Link DataBindings bearbeiten. Geben Sie als Nächstes an, dass die Text Eigenschaft als Zahl formatiert werden soll.

Formatieren der Text-Eigenschaft als Zahl

Abbildung 14: Formatieren der Text Eigenschaft als Zahl

Zweitens, lassen Sie das Kontrollkästchen in der Discontinued Spalte zentrieren (anstatt es links ausgerichtet zu haben). Klicken Sie im Smarttag von GridView auf Spalten bearbeiten, und wählen Sie in der Discontinued Liste der Felder in der unteren linken Ecke das TemplateField aus. Führen Sie einen Drilldown durch ItemStyle , und legen Sie die HorizontalAlign Eigenschaft wie in Abbildung 15 dargestellt auf Zentriert fest.

Zentrzentrale des nicht mehr eingestellten CheckBox

Abbildung 15: Zentrierendes Discontinued CheckBox

Fügen Sie als Nächstes der Seite ein ValidationSummary-Steuerelement hinzu, und legen Sie die ShowMessageBox -Eigenschaft auf true und die ShowSummary -Eigenschaft auf fest false. Fügen Sie auch die Schaltflächenwebsteuerelemente hinzu, die beim Klicken die Änderungen des Benutzers aktualisieren. Fügen Sie insbesondere zwei Button-Websteuerelemente hinzu, eines über der GridView und eines darunter, und legen Sie beide Steuerelementeigenschaften Text auf Produkte aktualisieren fest.

Da die Bearbeitungsschnittstelle von GridView in ihren TemplateFields ItemTemplate definiert ist, sind die EditItemTemplate s überflüssig und können gelöscht werden.

Nachdem Sie die oben genannten Formatierungsänderungen vorgenommen, die Schaltflächen-Steuerelemente hinzugefügt und die unnötigen EditItemTemplate s entfernt haben, sollte die deklarative Syntax Ihrer Seite wie folgt aussehen:

<p>
    <asp:Button ID="UpdateAllProducts1" runat="server" Text="Update Products" />
</p>
<p>
    <asp:GridView ID="ProductsGrid" runat="server" AutoGenerateColumns="False" 
        DataKeyNames="ProductID" DataSourceID="ProductsDataSource" 
        AllowPaging="True" AllowSorting="True">
        <Columns>
            <asp:TemplateField HeaderText="Product" SortExpression="ProductName">
                <ItemTemplate>
                    <asp:TextBox ID="ProductName" runat="server" 
                        Text='<%# Bind("ProductName") %>'></asp:TextBox>
                    <asp:RequiredFieldValidator ID="RequiredFieldValidator1" 
                        ControlToValidate="ProductName"
                        ErrorMessage="You must provide the product's name." 
                        runat="server">*</asp:RequiredFieldValidator>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Category" 
                SortExpression="CategoryName">
                <ItemTemplate>
                    <asp:DropDownList ID="Categories" runat="server" 
                        AppendDataBoundItems="True" 
                        DataSourceID="CategoriesDataSource"
                        DataTextField="CategoryName" 
                        DataValueField="CategoryID" 
                        SelectedValue='<%# Bind("CategoryID") %>'>
                        <asp:ListItem>-- Select One --</asp:ListItem>
                    </asp:DropDownList>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Price" 
                SortExpression="UnitPrice">
                <ItemTemplate>
                    $<asp:TextBox ID="UnitPrice" runat="server" Columns="8" 
                        Text='<%# Bind("UnitPrice", "{0:N}") %>'></asp:TextBox>
                    <asp:CompareValidator ID="CompareValidator1" runat="server" 
                        ControlToValidate="UnitPrice"
                        ErrorMessage="You must enter a valid currency value. 
                                      Please omit any currency symbols."
                        Operator="GreaterThanEqual" Type="Currency" 
                        ValueToCompare="0">*</asp:CompareValidator>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Discontinued" SortExpression="Discontinued">
                <ItemTemplate>
                    <asp:CheckBox ID="Discontinued" runat="server" 
                        Checked='<%# Bind("Discontinued") %>' />
                </ItemTemplate>
                <ItemStyle HorizontalAlign="Center" />
            </asp:TemplateField>
        </Columns>
    </asp:GridView>
</p>
<p>
    <asp:Button ID="UpdateAllProducts2" runat="server" Text="Update Products" />
    <asp:ObjectDataSource ID="ProductsDataSource" runat="server" 
        OldValuesParameterFormatString="original_{0}"
        SelectMethod="GetProducts" TypeName="ProductsBLL">
    </asp:ObjectDataSource>
    <asp:ObjectDataSource ID="CategoriesDataSource" runat="server" 
        OldValuesParameterFormatString="original_{0}"
        SelectMethod="GetCategories" TypeName="CategoriesBLL">
    </asp:ObjectDataSource>
    <asp:ValidationSummary ID="ValidationSummary1" runat="server" 
        ShowMessageBox="True" ShowSummary="False" />
</p>

Abbildung 16 zeigt diese Seite, wenn sie über einen Browser angezeigt wird, nachdem die Schaltflächen-Websteuerelemente hinzugefügt und die Formatierungsänderungen vorgenommen wurden.

Die Seite enthält jetzt zwei Schaltflächen zum Aktualisieren von Produkten

Abbildung 16: Die Seite enthält jetzt zwei Schaltflächen "Produkte aktualisieren" (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Schritt 5: Aktualisieren der Produkte

Wenn ein Benutzer diese Seite besucht, nimmt er seine Änderungen vor und klickt dann auf eine der beiden Schaltflächen Produkte aktualisieren. An diesem Punkt müssen wir die vom Benutzer eingegebenen Werte für jede Zeile irgendwie in einem ProductsDataTable instance speichern und diese dann an eine BLL-Methode übergeben, die dann diese ProductsDataTable instance an die DAL s-Methode UpdateWithTransaction übergibt. Die UpdateWithTransaction -Methode, die wir im vorherigen Tutorial erstellt haben, stellt sicher, dass der Änderungsbatch als atomischer Vorgang aktualisiert wird.

Erstellen Sie eine Methode namens BatchUpdate in BatchUpdate.aspx.cs , und fügen Sie den folgenden Code hinzu:

private void BatchUpdate()
{
    // Enumerate the GridView's Rows collection and create a ProductRow
    ProductsBLL productsAPI = new ProductsBLL();
    Northwind.ProductsDataTable products = productsAPI.GetProducts();
    foreach (GridViewRow gvRow in ProductsGrid.Rows)
    {
        // Find the ProductsRow instance in products that maps to gvRow
        int productID = Convert.ToInt32(ProductsGrid.DataKeys[gvRow.RowIndex].Value);
        Northwind.ProductsRow product = products.FindByProductID(productID);
        if (product != null)
        {
            // Programmatically access the form field elements in the 
            // current GridViewRow
            TextBox productName = (TextBox)gvRow.FindControl("ProductName");
            DropDownList categories = 
                (DropDownList)gvRow.FindControl("Categories");
            TextBox unitPrice = (TextBox)gvRow.FindControl("UnitPrice");
            CheckBox discontinued = 
                (CheckBox)gvRow.FindControl("Discontinued");
            // Assign the user-entered values to the current ProductRow
            product.ProductName = productName.Text.Trim();
            if (categories.SelectedIndex == 0) 
                product.SetCategoryIDNull(); 
            else 
                product.CategoryID = Convert.ToInt32(categories.SelectedValue);
            if (unitPrice.Text.Trim().Length == 0) 
                product.SetUnitPriceNull(); 
            else 
                product.UnitPrice = Convert.ToDecimal(unitPrice.Text);
            product.Discontinued = discontinued.Checked;
        }
    }
    // Now have the BLL update the products data using a transaction
    productsAPI.UpdateWithTransaction(products);
}

Diese Methode beginnt mit dem Abrufen aller Produkte in einem ProductsDataTable über einen Aufruf der BLL s-Methode GetProducts . Anschließend wird die ProductGrid GridView-Auflistung Rowsaufgelistet. Die Rows Auflistung enthält eine GridViewRow instance für jede Zeile, die in GridView angezeigt wird. Da maximal zehn Zeilen pro Seite angezeigt werden, enthält die GridView-Auflistung Rows nicht mehr als zehn Elemente.

Für jede Zeile wird aus ProductID der DataKeys Auflistung gegriffen, und die entsprechende ProductsRow wird aus dem ProductsDataTableausgewählt. Auf die vier TemplateField-Eingabesteuerelemente wird programmgesteuert verwiesen, und ihre Werte werden den Eigenschaften des ProductsRow instance zugewiesen. Nachdem jeder GridView-Zeilenwert verwendet wurde, um den ProductsDataTablezu aktualisieren, wird er an die BLL-Methode UpdateWithTransaction übergeben, die, wie im vorherigen Tutorial gezeigt, einfach in die DAL s-Methode UpdateWithTransaction herunterruft.

Der für dieses Tutorial verwendete Batchupdatealgorithmus aktualisiert jede Zeile in der , die ProductsDataTable einer Zeile in GridView entspricht, unabhängig davon, ob die Produktinformationen geändert wurden. Obwohl solche blinden Updates in der Regel kein Leistungsproblem sind, können sie zu überflüssigen Datensätzen führen, wenn Sie Änderungen an der Datenbanktabelle überwachen. Zurück im Tutorial Ausführen von Batch Updates haben wir eine Batchaktualisierungsschnittstelle mit dataList untersucht und Code hinzugefügt, der nur die Datensätze aktualisiert, die tatsächlich vom Benutzer geändert wurden. Sie können die Techniken von Performing Batch Updates verwenden, um den Code in diesem Tutorial bei Bedarf zu aktualisieren.

Hinweis

Wenn die Datenquelle über das Smarttag an gridView gebunden wird, weist Visual Studio automatisch die Primärschlüsselwerte der Datenquelle der GridView-Eigenschaft DataKeyNames zu. Wenn Sie die ObjectDataSource nicht wie in Schritt 1 beschrieben über das Smarttag von GridView an gridView gebunden haben, müssen Sie die GridView-Eigenschaft DataKeyNames manuell auf ProductID festlegen, um über die DataKeys Auflistung auf den ProductID Wert für jede Zeile zuzugreifen.

Der in BatchUpdate verwendete Code ähnelt dem in den BLL-MethodenUpdateProduct. Der Standard Unterschied besteht darin, dass in den UpdateProduct Methoden nur ein einzelner ProductRow instance aus der Architektur abgerufen wird. Der Code, der die Eigenschaften von ProductRow zuweist, ist zwischen den UpdateProducts Methoden und dem Code innerhalb der foreach Schleife in BatchUpdateidentisch, ebenso wie das Gesamtmuster.

Um dieses Tutorial abzuschließen, muss die BatchUpdate -Methode aufgerufen werden, wenn auf eine der Schaltflächen Produkte aktualisieren geklickt wird. Erstellen Sie Ereignishandler für die Click Ereignisse dieser beiden Button-Steuerelemente, und fügen Sie den Ereignishandlern den folgenden Code hinzu:

BatchUpdate();
ClientScript.RegisterStartupScript(this.GetType(), "message", 
    "alert('The products have been updated.');", true);

Zuerst wird ein Aufruf von BatchUpdateausgeführt. Als Nächstes wird verwendet, um JavaScript einzugeben, in dem ein Meldungsfeld mit dem ClientScript property Text Die Produkte wurden aktualisiert angezeigt wird.

Nehmen Sie sich eine Minute Zeit, um diesen Code zu testen. Rufen Sie BatchUpdate.aspx einen Browser auf, bearbeiten Sie eine Reihe von Zeilen, und klicken Sie auf eine der Schaltflächen Produkte aktualisieren. Wenn keine Eingabevalidierungsfehler vorliegen, sollte ein Meldungsfeld mit dem Text Die Produkte wurden aktualisiert angezeigt. Um die Atomarität des Updates zu überprüfen, sollten Sie eine zufällige CHECK Einschränkung hinzufügen, z. B. eine Einschränkung, die werte von 1234,56 nicht zuzulassen UnitPrice . Bearbeiten Sie dann aus BatchUpdate.aspxeine Reihe von Datensätzen, und stellen Sie sicher, dass Sie einen wert des Produkts UnitPrice auf den unzulässigen Wert ( 1234,56 ) festlegen. Dies sollte zu einem Fehler führen, wenn beim Klicken auf Produkte aktualisieren mit den anderen Änderungen während dieses Batchvorgangs ein Rollback auf die ursprünglichen Werte erfolgt ist.

Eine alternativeBatchUpdateMethode

Die BatchUpdate soeben untersuchte Methode ruft alle Produkte aus der BLL-Methode ab GetProducts und aktualisiert dann nur die Datensätze, die in GridView angezeigt werden. Dieser Ansatz ist ideal, wenn gridView kein Paging verwendet, aber wenn dies der Fall ist, kann es Hunderte, Tausende oder Zehntausende von Produkten geben, aber nur zehn Zeilen in GridView. In einem solchen Fall ist es weniger als ideal, alle Produkte aus der Datenbank zu erhalten, um 10 davon zu ändern.

Für diese Arten von Situationen sollten Sie stattdessen die folgende BatchUpdateAlternate Methode verwenden:

private void BatchUpdateAlternate()
{
    // Enumerate the GridView's Rows collection and create a ProductRow
    ProductsBLL productsAPI = new ProductsBLL();
    Northwind.ProductsDataTable products = new Northwind.ProductsDataTable();
    foreach (GridViewRow gvRow in ProductsGrid.Rows)
    {
        // Create a new ProductRow instance
        int productID = Convert.ToInt32(ProductsGrid.DataKeys[gvRow.RowIndex].Value);
        
        Northwind.ProductsDataTable currentProductDataTable = 
            productsAPI.GetProductByProductID(productID);
        if (currentProductDataTable.Rows.Count > 0)
        {
            Northwind.ProductsRow product = currentProductDataTable[0];
            // Programmatically access the form field elements in the 
            // current GridViewRow
            TextBox productName = (TextBox)gvRow.FindControl("ProductName");
            DropDownList categories = 
                (DropDownList)gvRow.FindControl("Categories");
            TextBox unitPrice = (TextBox)gvRow.FindControl("UnitPrice");
            CheckBox discontinued = 
                (CheckBox)gvRow.FindControl("Discontinued");
            // Assign the user-entered values to the current ProductRow
            product.ProductName = productName.Text.Trim();
            if (categories.SelectedIndex == 0) 
                product.SetCategoryIDNull(); 
            else 
                product.CategoryID = Convert.ToInt32(categories.SelectedValue);
            if (unitPrice.Text.Trim().Length == 0) 
                product.SetUnitPriceNull(); 
            else 
                product.UnitPrice = Convert.ToDecimal(unitPrice.Text);
            product.Discontinued = discontinued.Checked;
            // Import the ProductRow into the products DataTable
            products.ImportRow(product);
        }
    }
    // Now have the BLL update the products data using a transaction
    productsAPI.UpdateProductsWithTransaction(products);
}

BatchMethodAlternate beginnt, indem ein neues leeres ProductsDataTable namens erstellt wird products. Anschließend wird die GridView-Sammlung Rows durchlaufen und für jede Zeile die jeweiligen Produktinformationen mithilfe der BLL-Methode s GetProductByProductID(productID) abgerufen. Für die abgerufene ProductsRow instance werden die Eigenschaften auf die gleiche Weise wie BatchUpdateaktualisiert, aber nach dem Aktualisieren der Zeile wird sie über die DataTable-Methode in ImportRow(DataRow)importiertproducts``ProductsDataTable.

Enthält nach Abschluss products der foreach Schleife eine ProductsRow instance für jede Zeile in GridView. Da jede der ProductsRow Instanzen der products hinzugefügt wurde (statt aktualisiert), wenn wir sie blind an die UpdateWithTransaction -Methode übergeben, versucht der ProductsTableAdapter , jeden der Datensätze in die Datenbank einzufügen. Stattdessen müssen wir angeben, dass jede dieser Zeilen geändert (nicht hinzugefügt) wurde.

Dies kann erreicht werden, indem Sie der BLL eine neue Methode mit dem Namen UpdateProductsWithTransactionhinzufügen. UpdateProductsWithTransaction, wie unten dargestellt, legt die der einzelnen ProductsRow Instanzen im ProductsDataTable an fest Modified und übergibt dann die ProductsDataTable an die DAL s-MethodeUpdateWithTransaction.RowState

public int UpdateProductsWithTransaction(Northwind.ProductsDataTable products)
{
    // Mark each product as Modified
    products.AcceptChanges();
    foreach (Northwind.ProductsRow product in products)
        product.SetModified();
    // Update the data via a transaction
    return UpdateWithTransaction(products);
}

Zusammenfassung

GridView bietet integrierte Funktionen für die Zeilenbearbeitung, unterstützt jedoch nicht das Erstellen vollständig bearbeitbarer Schnittstellen. Wie wir in diesem Tutorial gesehen haben, sind solche Schnittstellen möglich, erfordern aber etwas Arbeit. Um ein GridView-Objekt zu erstellen, in dem jede Zeile bearbeitbar ist, müssen wir die GridView-Felder in TemplateFields konvertieren und die Bearbeitungsoberfläche innerhalb der ItemTemplate s definieren. Darüber hinaus müssen alle Schaltflächen-Websteuerelemente vom Typ "Alle aktualisieren" getrennt von GridView zur Seite hinzugefügt werden. Diese Buttons-Ereignishandler Click müssen die GridView-Auflistung Rows aufzählen, die Änderungen in einem ProductsDataTablespeichern und die aktualisierten Informationen an die entsprechende BLL-Methode übergeben.

Im nächsten Tutorial erfahren Sie, wie Sie eine Schnittstelle zum Löschen von Batchs erstellen. Insbesondere enthält jede GridView-Zeile ein Kontrollkästchen, und anstelle der Schaltflächen Alle aktualisieren haben wir die Schaltflächen Ausgewählte Zeilen löschen.

Viel Spaß beim Programmieren!

Zum Autor

Scott Mitchell, Autor von sieben ASP/ASP.NET-Büchern und Gründer von 4GuysFromRolla.com, arbeitet seit 1998 mit Microsoft-Webtechnologien. Scott arbeitet als unabhängiger Berater, Trainer und Autor. Sein neuestes Buch ist Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Er kann unter mitchell@4GuysFromRolla.comoder über seinen Blog erreicht werden, der unter http://ScottOnWriting.NETzu finden ist.

Besonderer Dank an

Diese Tutorialreihe wurde von vielen hilfreichen Prüfern überprüft. Hauptprüfer für dieses Tutorial waren Teresa Murphy und David Suru. Möchten Sie meine bevorstehenden MSDN-Artikel lesen? Wenn dies der Fall ist, legen Sie eine Zeile unter abmitchell@4GuysFromRolla.com.