Durchführen von Batchupdates (C#)
von Scott Mitchell
Erfahren Sie, wie Sie eine vollständig bearbeitbare DataList erstellen, in der sich alle zugehörigen Elemente im Bearbeitungsmodus befinden und deren Werte gespeichert werden können, indem Sie auf der Seite auf eine Schaltfläche "Alle aktualisieren" klicken.
Einführung
Im vorherigen Tutorial haben wir untersucht, wie eine DataList auf Elementebene erstellt wird. Wie die standardmäßig bearbeitbare GridView enthielt jedes Element in der DataList eine Schaltfläche Bearbeiten, die das Element beim Klicken bearbeitbar macht. Während diese Bearbeitung auf Elementebene gut für Daten funktioniert, die nur gelegentlich aktualisiert werden, erfordern bestimmte Anwendungsfälle, dass der Benutzer viele Datensätze bearbeitet. Wenn ein Benutzer Dutzende von Datensätzen bearbeiten muss und gezwungen ist, auf Bearbeiten zu klicken, seine Änderungen vorzunehmen und für jeden Einzelnen auf Aktualisieren zu klicken, kann die Menge des Klickens seine Produktivität beeinträchtigen. In solchen Situationen besteht eine bessere Option darin, eine vollständig bearbeitbare DataList bereitzustellen, bei der sich alle Elemente im Bearbeitungsmodus befinden und deren Werte durch Klicken auf die Schaltfläche Alle aktualisieren auf der Seite bearbeitet werden können (siehe Abbildung 1).
Abbildung 1: Jedes Element in einer vollständig bearbeitbaren DataList kann geändert werden (Klicken Sie, um das bild in voller Größe anzuzeigen)
In diesem Tutorial wird untersucht, wie Benutzern das Aktualisieren von Adressinformationen von Lieferanten mithilfe einer vollständig bearbeitbaren DataList ermöglicht wird.
Schritt 1: Erstellen der bearbeitbaren Benutzeroberfläche in der DataList-Elementvorlage
Im vorherigen Tutorial, in dem eine bearbeitbare Standard-DataList auf Elementebene erstellt wurde, haben wir zwei Vorlagen verwendet:
ItemTemplate
enthält die schreibgeschützte Benutzeroberfläche (die Label-Websteuerelemente zum Anzeigen von Produktnamen und -preisen).EditItemTemplate
enthält die Benutzeroberfläche im Bearbeitungsmodus (die beiden TextBox-Websteuerelemente).
Die DataList-Eigenschaft EditItemIndex
diktiert, was DataListItem
(falls vorhanden) mit EditItemTemplate
gerendert wird. Insbesondere wird der DataListItem
, dessen ItemIndex
Wert mit der DataList-Eigenschaft EditItemIndex
übereinstimmt, mithilfe von EditItemTemplate
gerendert. Dieses Modell funktioniert gut, wenn nur ein Element gleichzeitig bearbeitet werden kann, fällt aber beim Erstellen einer vollständig bearbeitbaren DataList auseinander.
Für eine vollständig bearbeitbare DataList sollen alle s mithilfe der DataListItem
bearbeitbaren Schnittstelle gerendert werden. Die einfachste Möglichkeit, dies zu erreichen, besteht darin, die bearbeitbare Schnittstelle in zu ItemTemplate
definieren. Zum Ändern der Lieferantenadresseninformationen enthält die bearbeitbare Schnittstelle den Lieferantennamen als Text und dann TextBoxes für die Werte "Adresse", "Ort" und "Land/Region".
Öffnen Sie zunächst die BatchUpdate.aspx
Seite, fügen Sie ein DataList-Steuerelement hinzu, und legen Sie dessen ID
Eigenschaft auf fest Suppliers
. Wählen Sie im Smarttag dataList ein neues ObjectDataSource-Steuerelement mit dem Namen SuppliersDataSource
hinzu.
Abbildung 2: Erstellen eines neuen ObjectDataSource-Namens SuppliersDataSource
(Klicken Sie hier, um das bild in voller Größe anzuzeigen)
Konfigurieren Sie objectDataSource zum Abrufen von Daten mithilfe der SuppliersBLL
Methode der Klasse s GetSuppliers()
(siehe Abbildung 3). Wie im vorherigen Tutorial arbeiten wir nicht mit den Lieferanteninformationen über die ObjectDataSource, sondern direkt mit der Geschäftslogikebene zusammen. Legen Sie daher die Dropdownliste auf der Registerkarte UPDATE auf (Keine) fest (siehe Abbildung 4).
Abbildung 3: Abrufen von Lieferanteninformationen mithilfe der GetSuppliers()
-Methode (Klicken Sie hier, um das bild in voller Größe anzuzeigen)
Abbildung 4: Legen Sie die Drop-Down Liste auf der Registerkarte UPDATE auf (Keine) fest (Klicken Sie, um das bild in voller Größe anzuzeigen)
Nach Abschluss des Assistenten generiert Visual Studio automatisch die DataList s ItemTemplate
, um jedes von der Datenquelle zurückgegebene Datenfeld in einem Label-Websteuerelement anzuzeigen. Wir müssen diese Vorlage so ändern, dass sie stattdessen die Bearbeitungsschnittstelle bereitstellt. Die ItemTemplate
kann über die Designer mithilfe der Option Vorlagen bearbeiten über das Smarttag des DataList-Smarttags oder direkt über die deklarative Syntax angepasst werden.
Nehmen Sie sich einen Moment Zeit, um eine Bearbeitungsoberfläche zu erstellen, die den Namen des Lieferanten als Text anzeigt, aber TextBoxes für die Adress-, Orts- und Länder-/Regionswerte des Lieferanten enthält. Nachdem Sie diese Änderungen vorgenommen haben, sollte die deklarative Syntax Ihrer Seite wie folgt aussehen:
<asp:DataList ID="Suppliers" runat="server" DataKeyField="SupplierID"
DataSourceID="SuppliersDataSource">
<ItemTemplate>
<h4><asp:Label ID="CompanyNameLabel" runat="server"
Text='<%# Eval("CompanyName") %>' /></h4>
<table border="0">
<tr>
<td class="SupplierPropertyLabel">Address:</td>
<td class="SupplierPropertyValue">
<asp:TextBox ID="Address" runat="server"
Text='<%# Eval("Address") %>' />
</td>
</tr>
<tr>
<td class="SupplierPropertyLabel">City:</td>
<td class="SupplierPropertyValue">
<asp:TextBox ID="City" runat="server"
Text='<%# Eval("City") %>' />
</td>
</tr>
<tr>
<td class="SupplierPropertyLabel">Country:</td>
<td class="SupplierPropertyValue">
<asp:TextBox ID="Country" runat="server"
Text='<%# Eval("Country") %>' />
</td>
</tr>
</table>
<br />
</ItemTemplate>
</asp:DataList>
<asp:ObjectDataSource ID="SuppliersDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetSuppliers" TypeName="SuppliersBLL">
</asp:ObjectDataSource>
Hinweis
Wie im vorherigen Tutorial muss auch für DataList in diesem Tutorial der Ansichtszustand aktiviert sein.
ItemTemplate
Ich verwende zwei neue CSS-Klassen und SupplierPropertyValue
, die der Styles.css
-Klasse hinzugefügt und so konfiguriert wurden, SupplierPropertyLabel
dass sie dieselben Stileinstellungen wie die ProductPropertyLabel
CSS-Klassen und ProductPropertyValue
verwenden.
.ProductPropertyLabel, .SupplierPropertyLabel
{
font-weight: bold;
text-align: right;
}
.ProductPropertyValue, .SupplierPropertyValue
{
padding-right: 35px;
}
Nachdem Sie diese Änderungen vorgenommen haben, besuchen Sie diese Seite über einen Browser. Wie Abbildung 5 zeigt, zeigt jedes DataList-Element den Lieferantennamen als Text an und verwendet TextBoxes, um die Adresse, den Ort und das Land/die Region anzuzeigen.
Abbildung 5: Jeder Lieferant in der DataList ist bearbeitbar (Klicken Sie hier, um das bild in voller Größe anzuzeigen)
Schritt 2: Hinzufügen einer Schaltfläche "Alle aktualisieren"
Während für jeden Lieferanten in Abbildung 5 die Felder Adresse, Ort und Land/Region in einem Textfeld angezeigt werden, ist derzeit keine Schaltfläche Aktualisieren verfügbar. Anstelle einer Schaltfläche Aktualisieren pro Element gibt es bei vollständig bearbeitbaren DataLists in der Regel eine einzelne Schaltfläche Alle aktualisieren auf der Seite, die beim Klicken alle Datensätze in der DataList aktualisiert. In diesem Tutorial fügen Sie zwei Schaltflächen Alle aktualisieren hinzu– eine oben auf der Seite und eine am unteren Rand (obwohl das Klicken auf eine der schaltflächen denselben Effekt hat).
Beginnen Sie mit dem Hinzufügen eines Button-Websteuerelements über der DataList, und legen Sie dessen ID
Eigenschaft auf fest UpdateAll1
. Fügen Sie als Nächstes das zweite Button-Websteuerelement unter der DataList hinzu, und legen Sie es ID
auf fest UpdateAll2
. Legen Sie die Text
Eigenschaften für die beiden Schaltflächen auf Alle aktualisieren fest. Erstellen Sie schließlich Ereignishandler für beide Buttons-Ereignisse Click
. Anstatt die Updatelogik in jedem der Ereignishandler zu duplizieren, lassen Sie diese Logik in eine dritte Methode umgestalten, UpdateAllSupplierAddresses
wobei die Ereignishandler einfach diese dritte Methode aufrufen.
protected void UpdateAll1_Click(object sender, EventArgs e)
{
UpdateAllSupplierAddresses();
}
protected void UpdateAll2_Click(object sender, EventArgs e)
{
UpdateAllSupplierAddresses();
}
private void UpdateAllSupplierAddresses()
{
// TODO: Write code to update _all_ of the supplier addresses in the DataList
}
Abbildung 6 zeigt die Seite, nachdem die Schaltflächen Alle aktualisieren hinzugefügt wurden.
Abbildung 6: Zwei Aktualisierungsschaltflächen wurden der Seite hinzugefügt (Klicken Sie, um das Bild in voller Größe anzuzeigen)
Schritt 3: Aktualisieren aller Adressinformationen von Lieferanten
Da alle DataList-Elemente die Bearbeitungsoberfläche anzeigen und die Schaltflächen Alle aktualisieren hinzugefügt werden, bleibt nur noch das Schreiben des Codes zum Ausführen der Batchaktualisierung. Insbesondere müssen wir die DataList-Elemente durchschleifen und die Methode s UpdateSupplierAddress
der SuppliersBLL
Klasse für jedes aufrufen.
Auf die Sammlung von DataListItem
Instanzen, die die DataList erstellen, kann über die DataList-Eigenschaft Items
zugegriffen werden. Mit einem Verweis auf ein DataListItem
können wir das entsprechende SupplierID
aus der DataKeys
Auflistung abrufen und programmgesteuert auf die TextBox-Websteuerelemente innerhalb von ItemTemplate
verweisen, wie der folgende Code veranschaulicht:
private void UpdateAllSupplierAddresses()
{
// Create an instance of the SuppliersBLL class
SuppliersBLL suppliersAPI = new SuppliersBLL();
// Iterate through the DataList's items
foreach (DataListItem item in Suppliers.Items)
{
// Get the supplierID from the DataKeys collection
int supplierID = Convert.ToInt32(Suppliers.DataKeys[item.ItemIndex]);
// Read in the user-entered values
TextBox address = (TextBox)item.FindControl("Address");
TextBox city = (TextBox)item.FindControl("City");
TextBox country = (TextBox)item.FindControl("Country");
string addressValue = null, cityValue = null, countryValue = null;
if (address.Text.Trim().Length > 0)
addressValue = address.Text.Trim();
if (city.Text.Trim().Length > 0)
cityValue = city.Text.Trim();
if (country.Text.Trim().Length > 0)
countryValue = country.Text.Trim();
// Call the SuppliersBLL class's UpdateSupplierAddress method
suppliersAPI.UpdateSupplierAddress
(supplierID, addressValue, cityValue, countryValue);
}
}
Wenn der Benutzer auf eine der Schaltflächen Alle aktualisieren klickt, durchläuft die UpdateAllSupplierAddresses
-Methode jede DataListItem
in der Suppliers
DataList und ruft die SuppliersBLL
Klasse s-Methode UpdateSupplierAddress
auf, wobei die entsprechenden Werte übergeben werden. Ein nicht eingegebener Wert für Adress-, Orts- oder Länder-/Regionsdurchläufe ist ein Wert von Nothing
von UpdateSupplierAddress
(anstelle einer leeren Zeichenfolge), was zu einer Datenbank NULL
für die Felder des zugrunde liegenden Datensatzes führt.
Hinweis
Als Erweiterung können Sie der Seite ein status Label-Websteuerelement hinzufügen, das eine Bestätigungsmeldung bereitstellt, nachdem das Batchupdate ausgeführt wurde.
Aktualisieren nur der Adressen, die geändert wurden
Der für dieses Tutorial verwendete Batchupdatealgorithmus ruft die UpdateSupplierAddress
Methode für jeden Lieferanten in der DataList auf, unabhängig davon, ob seine Adressinformationen geändert wurden. Während solche blinden Updates normalerweise kein Leistungsproblem sind, können sie zu überflüssigen Datensätzen führen, wenn Sie Änderungen an der Datenbanktabelle überwachen. Wenn Sie beispielsweise Trigger verwenden, um alle UPDATE
s in der Suppliers
Tabelle in einer Überwachungstabelle aufzuzeichnen, wird jedes Mal, wenn ein Benutzer auf die Schaltfläche Alle aktualisieren klickt, ein neuer Überwachungsdatensatz für jeden Lieferanten im System erstellt, unabhängig davon, ob der Benutzer Änderungen vorgenommen hat.
Die ADO.NET DataTable- und DataAdapter-Klassen sind so konzipiert, dass sie Batchupdates unterstützen, bei denen nur geänderte, gelöschte und neue Datensätze zu einer Datenbankkommunikation führen. Jede Zeile in der DataTable verfügt über eine RowState
Eigenschaft , die angibt, ob die Zeile der DataTable hinzugefügt, gelöscht, geändert wurde oder unverändert bleibt. Wenn eine DataTable anfänglich aufgefüllt wird, werden alle Zeilen unverändert markiert. Wenn Sie den Wert einer der Zeilenspalten ändern, wird die Zeile als geändert markiert.
In der SuppliersBLL
Klasse aktualisieren wir die angegebenen Adressinformationen des Lieferanten, indem wir zuerst den einzelnen Lieferantendatensatz in einen SuppliersDataTable
lesen und dann die Address
Spaltenwerte , City
und Country
mit dem folgenden Code festlegen:
public bool UpdateSupplierAddress
(int supplierID, string address, string city, string country)
{
Northwind.SuppliersDataTable suppliers =
Adapter.GetSupplierBySupplierID(supplierID);
if (suppliers.Count == 0)
// no matching record found, return false
return false;
else
{
Northwind.SuppliersRow supplier = suppliers[0];
if (address == null)
supplier.SetAddressNull();
else
supplier.Address = address;
if (city == null)
supplier.SetCityNull();
else
supplier.City = city;
if (country == null)
supplier.SetCountryNull();
else
supplier.Country = country;
// Update the supplier Address-related information
int rowsAffected = Adapter.Update(supplier);
// Return true if precisely one row was updated,
// otherwise false
return rowsAffected == 1;
}
}
Dieser Code weist naiv die übergebenen Adress-, Orts- und Länder-/Regionswerte dem SuppliersRow
in SuppliersDataTable
zu, unabhängig davon, ob sich die Werte geändert haben oder nicht. Diese Änderungen führen dazu, dass die SuppliersRow
s-Eigenschaft RowState
als geändert markiert wird. Wenn die Data Access Layer-Methode Update
aufgerufen wird, erkennt sie, dass die SupplierRow
geändert wurde, und sendet daher einen UPDATE
Befehl an die Datenbank.
Stellen Sie sich jedoch vor, wir haben dieser Methode Code hinzugefügt, um nur die übergebenen Werte für Adresse, Ort und Land/Region zuzuweisen, wenn sie sich von den SuppliersRow
vorhandenen Werten unterscheiden. Wenn Adresse, Ort und Land/Region mit den vorhandenen Daten identisch sind, werden keine Änderungen vorgenommen, und die SupplierRow
s RowState
werden als unverändert markiert. Das Nettoergebnis ist, dass beim Aufruf der DAL-Methode Update
kein Datenbankaufruf erfolgt, da der SuppliersRow
nicht geändert wurde.
Um diese Änderung zu erzwingen, ersetzen Sie die Anweisungen, die die übergebenen Werte für Adresse, Ort und Land/Region blind zuweisen, durch den folgenden Code:
// Only assign the values to the SupplierRow's column values if they differ
if (address == null && !supplier.IsAddressNull())
supplier.SetAddressNull();
else if ((address != null && supplier.IsAddressNull()) ||
(!supplier.IsAddressNull() &&
string.Compare(supplier.Address, address) != 0))
supplier.Address = address;
if (city == null && !supplier.IsCityNull())
supplier.SetCityNull();
else if ((city != null && supplier.IsCityNull()) ||
(!supplier.IsCityNull() && string.Compare(supplier.City, city) != 0))
supplier.City = city;
if (country == null && !supplier.IsCountryNull())
supplier.SetCountryNull();
else if ((country != null && supplier.IsCountryNull()) ||
(!supplier.IsCountryNull() &&
string.Compare(supplier.Country, country) != 0))
supplier.Country = country;
Mit diesem hinzugefügten Code sendet die DAL-Methode Update
eine UPDATE
Anweisung an die Datenbank nur für die Datensätze, deren adressbezogene Werte geändert wurden.
Alternativ können wir nachverfolgen, ob es Unterschiede zwischen den übergebenen Adressfeldern und den Datenbankdaten gibt, und, falls keine vorhanden sind, einfach den Aufruf der DAL-Methode Update
umgehen. Dieser Ansatz funktioniert gut, wenn Sie die direkte DB-Methode verwenden, da die direkte DB-Methode keine instance übergeben SuppliersRow
wird, deren RowState
Überprüfung überprüft werden kann, ob ein Datenbankaufruf tatsächlich erforderlich ist.
Hinweis
Jedes Mal, wenn die UpdateSupplierAddress
Methode aufgerufen wird, wird ein Aufruf der Datenbank ausgeführt, um Informationen zum aktualisierten Datensatz abzurufen. Wenn dann Änderungen an den Daten vorgenommen werden, wird ein weiterer Aufruf der Datenbank ausgeführt, um die Tabellenzeile zu aktualisieren. Dieser Workflow kann optimiert werden, indem eine UpdateSupplierAddress
Methodenüberladung erstellt wird, die eine EmployeesDataTable
instance akzeptiert, die alle Änderungen von der BatchUpdate.aspx
Seite enthält. Anschließend könnte sie einen Aufruf der Datenbank ausführen, um alle Datensätze aus der Suppliers
Tabelle abzurufen. Die beiden Resultsets könnten dann aufgelistet werden, und nur die Datensätze, in denen Änderungen aufgetreten sind, konnten aktualisiert werden.
Zusammenfassung
In diesem Tutorial haben wir erfahren, wie Sie eine vollständig bearbeitbare DataList erstellen, die es einem Benutzer ermöglicht, die Adressinformationen für mehrere Lieferanten schnell zu ändern. Zunächst wurde die Bearbeitungsschnittstelle für ein TextBox-Websteuerelement für die Adress-, Orts- und Länder-/Regionswerte des Lieferanten in der DataList definiert ItemTemplate
. Als Nächstes haben wir die Schaltflächen Alle aktualisieren oberhalb und unterhalb der DataList hinzugefügt. Nachdem ein Benutzer seine Änderungen vorgenommen und auf eine der Schaltflächen Alle aktualisieren geklickt hat, werden die DataListItem
s aufgelistet, und es wird ein Aufruf der Methode der SuppliersBLL
Klasse s UpdateSupplierAddress
ausgeführt.
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 Stunden. Er kann unter mitchell@4GuysFromRolla.comoder über seinen Blog erreicht werden, der unter http://ScottOnWriting.NETzu finden ist.
Besonderen Dank an
Diese Tutorialreihe wurde von vielen hilfreichen Prüfern überprüft. Hauptprüfer für dieses Tutorial waren Zack Jones und Ken Pespisa. Möchten Sie meine anstehenden MSDN-Artikel lesen? Wenn dies der Fall ist, legen Sie eine Zeile unter abmitchell@4GuysFromRolla.com.