Opakowywanie modyfikacji bazy danych w ramach transakcji (VB)
Autor : Scott Mitchell
Ten samouczek jest pierwszym z czterech, który analizuje aktualizowanie, usuwanie i wstawianie partii danych. W tym samouczku dowiesz się, jak transakcje bazy danych umożliwiają wykonywanie modyfikacji wsadowych jako niepodzielnej operacji, co gwarantuje, że wszystkie kroki zakończyły się powodzeniem lub wszystkimi krokami.
Wprowadzenie
Jak widzieliśmy, począwszy od samouczka Omówienie wstawiania, aktualizowania i usuwania danych , kontrolka GridView zapewnia wbudowaną obsługę edytowania i usuwania na poziomie wiersza. Za pomocą kilku kliknięć myszy można utworzyć rozbudowany interfejs modyfikacji danych bez konieczności pisania wiersza kodu, o ile jesteś zawartością edycji i usuwania dla poszczególnych wierszy. Jednak w niektórych scenariuszach jest to niewystarczające i musimy zapewnić użytkownikom możliwość edytowania lub usuwania partii rekordów.
Na przykład większość internetowych klientów poczty e-mail używa siatki, aby wyświetlić listę każdej wiadomości, w której każdy wiersz zawiera pole wyboru wraz z informacjami o wiadomościach e-mail (temat, nadawca itd.). Ten interfejs pozwala użytkownikowi usunąć wiele komunikatów, sprawdzając je, a następnie klikając przycisk Usuń wybrane komunikaty. Interfejs edycji wsadowej jest idealny w sytuacjach, w których użytkownicy często edytują wiele różnych rekordów. Zamiast wymuszać na użytkowniku kliknięcie przycisku Edytuj, wprowadź zmiany, a następnie kliknij przycisk Aktualizuj dla każdego rekordu, który należy zmodyfikować, interfejs edycji wsadowej renderuje każdy wiersz za pomocą interfejsu edycji. Użytkownik może szybko zmodyfikować zestaw wierszy, które należy zmienić, a następnie zapisać te zmiany, klikając przycisk Aktualizuj wszystko. W tym zestawie samouczków dowiesz się, jak tworzyć interfejsy do wstawiania, edytowania i usuwania partii danych.
Podczas wykonywania operacji wsadowych należy określić, czy niektóre operacje w partii powinny być możliwe, aby zakończyły się powodzeniem, podczas gdy inne kończą się niepowodzeniem. Rozważ usunięcie wsadowego interfejsu — co powinno się zdarzyć, jeśli pierwszy wybrany rekord zostanie pomyślnie usunięty, ale drugi kończy się niepowodzeniem, powiedzmy, z powodu naruszenia ograniczenia klucza obcego? Czy pierwsze usunięcie rekordu powinno zostać wycofane lub jest dopuszczalne, aby pierwszy rekord pozostał usunięty?
Jeśli chcesz, aby operacja wsadowa została potraktowana jako operacja niepodzielna, w której wszystkie kroki zakończyły się powodzeniem lub wszystkie kroki zakończyły się niepowodzeniem, należy rozszerzyć warstwę dostępu do danych, aby uwzględnić obsługę transakcji bazy danych. Transakcje bazy danych gwarantują niepodzielność zestawu INSERT
instrukcji , UPDATE
i DELETE
wykonywanych w ramach transakcji i są funkcją obsługiwaną przez większość nowoczesnych systemów baz danych.
W tym samouczku dowiesz się, jak rozszerzyć zakres dal na użycie transakcji bazy danych. Kolejne samouczki przeanalizują implementację stron internetowych pod kątem wstawiania, aktualizowania i usuwania interfejsów wsadowych. Zacznijmy!
Uwaga
Podczas modyfikowania danych w transakcji wsadowej niepodzielność nie zawsze jest potrzebna. W niektórych scenariuszach może być akceptowalne, że niektóre modyfikacje danych kończą się powodzeniem, a inne w tej samej partii kończą się niepowodzeniem, na przykład podczas usuwania zestawu wiadomości e-mail z internetowego klienta poczty e-mail. Jeśli w trakcie procesu usuwania wystąpi błąd bazy danych, prawdopodobnie dopuszczalne jest, aby te rekordy przetwarzane bez błędów pozostały usunięte. W takich przypadkach funkcja DAL nie musi być modyfikowana w celu obsługi transakcji bazy danych. Istnieją jednak inne scenariusze operacji wsadowych, w których niepodzielność jest niezbędna. Gdy klient przenosi swoje fundusze z jednego konta bankowego do drugiego, należy wykonać dwie operacje: środki muszą zostać odliczone od pierwszego konta, a następnie dodane do drugiego. Chociaż bank może nie mieć nic przeciwko pierwszemu krokowi odnieść sukces, ale drugi krok nie powiedzie się, jego klienci zrozumiale byliby zdenerwowani. Zachęcam do pracy z tym samouczkiem i zaimplementowania ulepszeń do obsługi transakcji bazy danych, nawet jeśli nie planujesz ich używać w partiach wstawiania, aktualizowania i usuwania interfejsów, które będziemy kompilować w kolejnych trzech samouczkach.
Omówienie transakcji
Większość baz danych obejmuje obsługę transakcji, które umożliwiają grupowanie wielu poleceń bazy danych w jedną jednostkę logiczną pracy. Polecenia bazy danych składające się z transakcji są gwarantowane jako niepodzielne, co oznacza, że wszystkie polecenia zakończy się niepowodzeniem lub wszystkie zostaną wykonane pomyślnie.
Ogólnie rzecz biorąc, transakcje są implementowane za pomocą instrukcji SQL przy użyciu następującego wzorca:
- Wskazuje początek transakcji.
- Wykonaj instrukcje SQL, które składają się na transakcję.
- Jeśli wystąpi błąd w jednej z instrukcji z kroku 2, wycofaj transakcję.
- Jeśli wszystkie instrukcje z kroku 2 zakończą się bez błędu, zatwierdź transakcję.
Instrukcje SQL służące do tworzenia, zatwierdzania i wycofywania transakcji można wprowadzać ręcznie podczas pisania skryptów SQL lub tworzenia procedur składowanych albo za pomocą środków programistycznych przy użyciu ADO.NET lub klas w System.Transactions
przestrzeni nazw. W tym samouczku przeanalizujemy tylko zarządzanie transakcjami przy użyciu ADO.NET. W przyszłym samouczku przyjrzymy się, jak używać procedur składowanych w warstwie dostępu do danych, w tym czasie zapoznamy się z instrukcjami SQL dotyczącymi tworzenia, wycofywania i zatwierdzania transakcji. W międzyczasie zapoznaj się z tematem Zarządzanie transakcjami w SQL Server procedur składowanych, aby uzyskać więcej informacji.
Uwaga
KlasaTransactionScope
w System.Transactions
przestrzeni nazw umożliwia deweloperom programowe zawijanie serii instrukcji w zakresie transakcji i obejmuje obsługę złożonych transakcji obejmujących wiele źródeł, takich jak dwie różne bazy danych, a nawet heterogeniczne typy magazynów danych, takie jak baza danych microsoft SQL Server, baza danych Oracle i usługa sieci Web. Postanowiłem użyć transakcji ADO.NET na potrzeby tego samouczka zamiast TransactionScope
klasy, ponieważ ADO.NET jest bardziej specyficzna dla transakcji bazy danych, a w wielu przypadkach jest znacznie mniej obciążana zasobami. Ponadto w niektórych scenariuszach TransactionScope
klasa używa koordynatora transakcji rozproszonych firmy Microsoft (MSDTC). Problemy z konfiguracją, implementacją i wydajnością związane z witryną MSDTC sprawiają, że jest to dość wyspecjalizowany i zaawansowany temat oraz wykracza poza zakres tych samouczków.
Podczas pracy z dostawcą SqlClient w ADO.NET transakcje są inicjowane za pośrednictwem wywołania SqlConnection
metody klasy, BeginTransaction
która zwraca SqlTransaction
obiekt. Instrukcje modyfikacji danych, które makijaż transakcji są umieszczane w try...catch
bloku. Jeśli wystąpi błąd w instrukcji w try
bloku, wykonywanie jest transferowane do catch
bloku, w którym transakcja może zostać wycofana za pośrednictwem SqlTransaction
metody object sRollback
. Jeśli wszystkie instrukcje zakończą się pomyślnie, wywołanie SqlTransaction
metody obiektu Commit
na końcu try
bloku zatwierdza transakcję. Poniższy fragment kodu ilustruje ten wzorzec.
' Create the SqlTransaction object
Dim myTransaction As SqlTransaction = SqlConnectionObject.BeginTransaction();
Try
'
' ... Perform the database transaction�s data modification statements...
'
' If we reach here, no errors, so commit the transaction
myTransaction.Commit()
Catch
' If we reach here, there was an error, so rollback the transaction
myTransaction.Rollback()
Throw
End Try
Domyślnie klasy TableAdapters w typowanym zestawie danych nie używają transakcji. Aby zapewnić obsługę transakcji, musimy rozszerzyć klasy TableAdapter w celu uwzględnienia dodatkowych metod, które używają powyższego wzorca do wykonywania serii instrukcji modyfikacji danych w zakresie transakcji. W kroku 2 zobaczymy, jak używać klas częściowych do dodawania tych metod.
Krok 1. Tworzenie stron sieci Web danych wsadowych
Zanim zaczniemy eksplorować sposób rozszerzania dal w celu obsługi transakcji bazy danych, najpierw poświęćmy chwilę na utworzenie ASP.NET stron internetowych, które będą potrzebne w tym samouczku i trzech poniższych. Zacznij od dodania nowego folderu o nazwie BatchData
, a następnie dodaj następujące strony ASP.NET, kojarząc każdą stronę ze stroną wzorcową Site.master
.
Default.aspx
Transactions.aspx
BatchUpdate.aspx
BatchDelete.aspx
BatchInsert.aspx
Rysunek 1. Dodawanie stron ASP.NET dla samouczków SqlDataSource-Related
Podobnie jak w przypadku innych folderów, użyje kontrolki SectionLevelTutorialListing.ascx
użytkownika, Default.aspx
aby wyświetlić listę samouczków w jej sekcji. W związku z tym dodaj tę kontrolkę użytkownika, Default.aspx
przeciągając ją z Eksplorator rozwiązań na widok projektowy strony.
Rysunek 2. Dodawanie kontrolki SectionLevelTutorialListing.ascx
użytkownika do (Default.aspx
kliknij, aby wyświetlić obraz pełnowymiarowy)
Na koniec dodaj te cztery strony jako wpisy do Web.sitemap
pliku. W szczególności dodaj następujące znaczniki po dostosowaniu mapy <siteMapNode>
witryny :
<siteMapNode title="Working with Batched Data"
url="~/BatchData/Default.aspx"
description="Learn how to perform batch operations as opposed to
per-row operations.">
<siteMapNode title="Adding Support for Transactions"
url="~/BatchData/Transactions.aspx"
description="See how to extend the Data Access Layer to support
database transactions." />
<siteMapNode title="Batch Updating"
url="~/BatchData/BatchUpdate.aspx"
description="Build a batch updating interface, where each row in a
GridView is editable." />
<siteMapNode title="Batch Deleting"
url="~/BatchData/BatchDelete.aspx"
description="Explore how to create an interface for batch deleting
by adding a CheckBox to each GridView row." />
<siteMapNode title="Batch Inserting"
url="~/BatchData/BatchInsert.aspx"
description="Examine the steps needed to create a batch inserting
interface, where multiple records can be created at the
click of a button." />
</siteMapNode>
Po zaktualizowaniu Web.sitemap
programu poświęć chwilę, aby wyświetlić witrynę internetową samouczków za pośrednictwem przeglądarki. Menu po lewej stronie zawiera teraz elementy do pracy z samouczkami dotyczącymi danych wsadowych.
Rysunek 3. Mapa witryny zawiera teraz wpisy dotyczące pracy z samouczkami dotyczącymi danych wsadowych
Krok 2. Aktualizowanie warstwy dostępu do danych w celu obsługi transakcji bazy danych
Jak już wspomniano w pierwszym samouczku, tworzenie warstwy dostępu do danych, typizowane zestawy danych w naszym dal składają się z tabel DataTables i TableAdapters. Tabele DataTable przechowują dane, podczas gdy elementy TableAdapters zapewniają funkcjonalność odczytywania danych z bazy danych do tabel DataTables, aktualizowania bazy danych za pomocą zmian wprowadzonych w tabelach DataTable i tak dalej. Pamiętaj, że klasy TableAdapters udostępniają dwa wzorce aktualizacji danych, które są określane jako Batch Update i DB-Direct. W przypadku wzorca aktualizacji usługi Batch element TableAdapter jest przekazywany do zestawu danych, tabeli DataTable lub kolekcji elementów DataRows. Te dane są wyliczane i są wykonywane dla każdego wstawionego, zmodyfikowanego lub usuniętego InsertCommand
UpdateCommand
wiersza , lub DeleteCommand
. W przypadku wzorca DB-Direct element TableAdapter jest zamiast tego przekazywany wartości kolumn niezbędnych do wstawiania, aktualizowania lub usuwania pojedynczego rekordu. Następnie metoda wzorca DB Direct używa tych przekazanych wartości do wykonania odpowiedniej InsertCommand
instrukcji , UpdateCommand
lub DeleteCommand
.
Niezależnie od używanego wzorca aktualizacji metody generowane automatycznie nie używają transakcji. Domyślnie każda operacja wstawiania, aktualizowania lub usuwania wykonywana przez metodę TableAdapter jest traktowana jako pojedyncza dyskretna operacja. Załóżmy na przykład, że wzorzec DB-Direct jest używany przez kod w usłudze BLL do wstawiania dziesięciu rekordów do bazy danych. Ten kod wywoła metodę Insert
TableAdapter dziesięć razy. Jeśli pierwsze pięć wstawień powiedzie się, ale szósty spowodował wyjątek, pierwsze pięć wstawionych rekordów pozostanie w bazie danych. Podobnie, jeśli wzorzec aktualizacji usługi Batch jest używany do wykonywania operacji wstawiania, aktualizacji i usuwania do wstawionych, zmodyfikowanych i usuniętych wierszy w tabeli DataTable, jeśli pierwsze kilka modyfikacji zakończyło się pomyślnie, ale później napotkano błąd, te wcześniejsze modyfikacje, które zostały ukończone, pozostaną w bazie danych.
W niektórych scenariuszach chcemy zapewnić niepodzielność w ramach serii modyfikacji. Aby to osiągnąć, musimy ręcznie rozszerzyć tabelę TableAdapter, dodając nowe metody, które wykonują InsertCommand
, UpdateCommand
i DeleteCommand
s pod parasolem transakcji. W temacie Tworzenie warstwy dostępu do danych przyjrzeliśmy się używaniu klas częściowych do rozszerzania funkcjonalności tabel DataTable w ramach typizowanego zestawu danych. Ta technika może być również używana z elementami TableAdapters.
Typowany zestaw Northwind.xsd
danych znajduje się w App_Code
podfolderze folderu s DAL
. Utwórz podfolder w folderze DAL
o nazwie TransactionSupport
i dodaj nowy plik klasy o nazwie ProductsTableAdapter.TransactionSupport.vb
(zobacz Rysunek 4). Ten plik będzie przechowywać częściową implementację, ProductsTableAdapter
która zawiera metody przeprowadzania modyfikacji danych przy użyciu transakcji.
Rysunek 4. Dodawanie folderu o nazwie TransactionSupport
i pliku klasy o nazwie ProductsTableAdapter.TransactionSupport.vb
Wprowadź następujący kod do ProductsTableAdapter.TransactionSupport.vb
pliku:
Imports System.Data
Imports System.Data.SqlClient
Namespace NorthwindTableAdapters
Partial Public Class ProductsTableAdapter
Private _transaction As SqlTransaction
Private Property Transaction() As SqlTransaction
Get
Return Me._transaction
End Get
Set(ByVal Value As SqlTransaction)
Me._transaction = Value
End Set
End Property
Public Sub BeginTransaction()
' Open the connection, if needed
If Me.Connection.State <> ConnectionState.Open Then
Me.Connection.Open()
End If
' Create the transaction and assign it to the Transaction property
Me.Transaction = Me.Connection.BeginTransaction()
' Attach the transaction to the Adapters
For Each command As SqlCommand In Me.CommandCollection
command.Transaction = Me.Transaction
Next
Me.Adapter.InsertCommand.Transaction = Me.Transaction
Me.Adapter.UpdateCommand.Transaction = Me.Transaction
Me.Adapter.DeleteCommand.Transaction = Me.Transaction
End Sub
Public Sub CommitTransaction()
' Commit the transaction
Me.Transaction.Commit()
' Close the connection
Me.Connection.Close()
End Sub
Public Sub RollbackTransaction()
' Rollback the transaction
Me.Transaction.Rollback()
' Close the connection
Me.Connection.Close()
End Sub
End Class
End Namespace
Słowo Partial
kluczowe w deklaracji klasy tutaj wskazuje kompilatorowi, że składowe dodane w ramach programu mają zostać dodane do ProductsTableAdapter
klasy w NorthwindTableAdapters
przestrzeni nazw. Zanotuj instrukcję Imports System.Data.SqlClient
w górnej części pliku. Ponieważ klasa TableAdapter została skonfigurowana do używania dostawcy SqlClient, wewnętrznie używa SqlDataAdapter
obiektu do wydawania poleceń do bazy danych. W związku z tym musimy użyć SqlTransaction
klasy , aby rozpocząć transakcję, a następnie zatwierdzić ją lub wycofać. Jeśli używasz magazynu danych innego niż Microsoft SQL Server, musisz użyć odpowiedniego dostawcy.
Te metody udostępniają bloki konstrukcyjne potrzebne do uruchamiania, wycofywania i zatwierdzania transakcji. Są one oznaczone , Public
umożliwiając ich korzystanie z poziomu ProductsTableAdapter
klasy z innej klasy w dal lub z innej warstwy w architekturze, takiej jak BLL. BeginTransaction
Otwiera wewnętrzną SqlConnection
tabelę TableAdapter (w razie potrzeby), rozpoczyna transakcję i przypisuje ją do Transaction
właściwości oraz dołącza transakcję do obiektów wewnętrznych SqlDataAdapter
SqlCommand
. CommitTransaction
i RollbackTransaction
wywołać Transaction
odpowiednio metody Commit
i Rollback
obiektów przed zamknięciem obiektu wewnętrznego Connection
.
Krok 3. Dodawanie metod do aktualizowania i usuwania danych w ramach parasola transakcji
Po zakończeniu tych metod możemy dodać metody do ProductsDataTable
lub BLL, które wykonują serię poleceń pod parasolem transakcji. Poniższa metoda używa wzorca aktualizacji usługi Batch do aktualizowania ProductsDataTable
wystąpienia przy użyciu transakcji. Uruchamia transakcję przez wywołanie BeginTransaction
metody , a następnie używa Try...Catch
bloku do wystawiania instrukcji modyfikacji danych. Jeśli wywołanie Adapter
metody obiektu Update
powoduje wyjątek, wykonanie zostanie przeniesione do catch
bloku, w którym transakcja zostanie wycofana, a wyjątek zostanie ponownie zgłoszony. Pamiętaj, że Update
metoda implementuje wzorzec aktualizacji usługi Batch, wyliczając wiersze dostarczonego ProductsDataTable
elementu i wykonując niezbędne elementy InsertCommand
, UpdateCommand
i DeleteCommand
s. Jeśli jedno z tych poleceń spowoduje wystąpienie błędu, transakcja zostanie wycofana, cofając poprzednie modyfikacje wprowadzone w okresie istnienia transakcji. Jeśli instrukcja zostanie ukończona Update
bez błędu, transakcja jest zatwierdzana w całości.
Public Function UpdateWithTransaction _
(ByVal dataTable As Northwind.ProductsDataTable) As Integer
Me.BeginTransaction()
Try
' Perform the update on the DataTable
Dim returnValue As Integer = Me.Adapter.Update(dataTable)
' If we reach here, no errors, so commit the transaction
Me.CommitTransaction()
Return returnValue
Catch
' If we reach here, there was an error, so rollback the transaction
Me.RollbackTransaction()
Throw
End Try
End Function
Dodaj metodę UpdateWithTransaction
do ProductsTableAdapter
klasy za pomocą klasy częściowej w pliku ProductsTableAdapter.TransactionSupport.vb
. Alternatywnie tę metodę można dodać do klasy warstwy ProductsBLL
logiki biznesowej z kilkoma drobnymi zmianami składniowymi. Mianowicie słowo kluczowe w , i musi zostać zastąpione ciągiem Adapter
(pamiętaj, że Adapter
jest to nazwa właściwości typu ProductsBLL
ProductsTableAdapter
).Me.RollbackTransaction()
Me.CommitTransaction()
Me.BeginTransaction()
Me
Metoda UpdateWithTransaction
używa wzorca aktualizacji usługi Batch, ale serię wywołań DB-Direct można również używać w zakresie transakcji, jak pokazano w poniższej metodzie. Metoda DeleteProductsWithTransaction
przyjmuje jako dane wejściowe List(Of T)
typu Integer
, które są elementami ProductID
do usunięcia. Metoda inicjuje transakcję za pośrednictwem wywołania metody BeginTransaction
, a następnie w Try
bloku wykonuje iterację po podanej liście wywołującej metodę wzorca Delete
DB-Direct dla każdej ProductID
wartości. Jeśli którekolwiek z wywołań zakończy się Delete
niepowodzeniem, kontrolka zostanie przeniesiona do Catch
bloku, w którym transakcja zostanie wycofana, a wyjątek zostanie ponownie zgłoszony. Jeśli wszystkie wywołania kończą się Delete
powodzeniem, transakcja zostanie zatwierdzona. Dodaj tę metodę ProductsBLL
do klasy .
Public Sub DeleteProductsWithTransaction _
(ByVal productIDs As System.Collections.Generic.List(Of Integer))
' Start the transaction
Adapter.BeginTransaction()
Try
' Delete each product specified in the list
For Each productID As Integer In productIDs
Adapter.Delete(productID)
Next
' Commit the transaction
Adapter.CommitTransaction()
Catch
' There was an error - rollback the transaction
Adapter.RollbackTransaction()
Throw
End Try
End Sub
Stosowanie transakcji w wielu elementach TableAdapters
Kod związany z transakcjami przeanalizowany w tym samouczku umożliwia traktowanie wielu instrukcji jako ProductsTableAdapter
operacji niepodzielnej. Ale co zrobić, jeśli trzeba wykonać wiele modyfikacji w różnych tabelach bazy danych niepodziealnie? Na przykład podczas usuwania kategorii możemy najpierw ponownie przypisać swoje bieżące produkty do innej kategorii. Te dwa kroki powodują ponowne przypisanie produktów i usunięcie kategorii powinno zostać wykonane jako operacja niepodzielna. Jednak metoda ProductsTableAdapter
zawiera tylko metody modyfikowania Products
tabeli i CategoriesTableAdapter
zawiera tylko metody modyfikowania Categories
tabeli. Więc jak transakcja może obejmować zarówno TableAdapters?
Jedną z opcji jest dodanie metody do CategoriesTableAdapter
nazwy DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID)
i wywołanie tej metody procedury składowanej, która zarówno ponownie przypisuje produkty, jak i usuwa kategorię w zakresie transakcji zdefiniowanej w procedurze składowanej. W przyszłym samouczku przyjrzymy się sposobom rozpoczynania, zatwierdzania i wycofywania transakcji w procedurach składowanych.
Inną opcją jest utworzenie klasy pomocniczej w klasie DAL zawierającej metodę DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID)
. Ta metoda tworzy wystąpienie klasy CategoriesTableAdapter
i ProductsTableAdapter
, a następnie ustawia te dwie właściwości Klasy TableAdapters Connection
na to samo SqlConnection
wystąpienie. W tym momencie jeden z dwóch elementów TableAdapters zainicjowałby transakcję za pomocą wywołania metody BeginTransaction
. Metody TableAdapters służące do ponownego przypisywania produktów i usuwania kategorii będą wywoływane w bloku z zatwierdzoną lub wycofaną transakcją zgodnie Try...Catch
z potrzebami.
Krok 4. DodawanieUpdateWithTransaction
metody do warstwy logiki biznesowej
W kroku 3 dodaliśmy metodę UpdateWithTransaction
do ProductsTableAdapter
metody w dal. Należy dodać odpowiednią metodę do BLL. Chociaż warstwa prezentacji może wywołać metodę bezpośrednio do warstwy DAL, aby wywołać UpdateWithTransaction
metodę, te samouczki mają na celu zdefiniowanie architektury warstwowej, która izoluje dal od warstwy prezentacji. W związku z tym zaleca się kontynuowanie tego podejścia.
ProductsBLL
Otwórz plik klasy i dodaj metodę o nazwie UpdateWithTransaction
, która po prostu wywołuje metodę DAL w dół do odpowiedniej metody DAL. Teraz powinny istnieć dwie nowe metody w pliku ProductsBLL
: UpdateWithTransaction
, które właśnie dodano, i DeleteProductsWithTransaction
, które zostały dodane w kroku 3.
Public Function UpdateWithTransaction _
(ByVal products As Northwind.ProductsDataTable) As Integer
Return Adapter.UpdateWithTransaction(products)
End Function
Public Sub DeleteProductsWithTransaction _
(ByVal productIDs As System.Collections.Generic.List(Of Integer))
' Start the transaction
Adapter.BeginTransaction()
Try
' Delete each product specified in the list
For Each productID As Integer In productIDs
Adapter.Delete(productID)
Next
' Commit the transaction
Adapter.CommitTransaction()
Catch
' There was an error - rollback the transaction
Adapter.RollbackTransaction()
Throw
End Try
End Sub
Uwaga
Te metody nie obejmują atrybutu przypisanego DataObjectMethodAttribute
do większości innych metod w ProductsBLL
klasie, ponieważ będziemy wywoływać te metody bezpośrednio z klas ASP.NET stron kodowych. Pamiętaj, że DataObjectMethodAttribute
służy do flagowania metod, które powinny być wyświetlane w kreatorze Konfigurowanie źródła danych objectDataSource i na jakiej karcie (SELECT, UPDATE, INSERT lub DELETE). Ponieważ w kodzie GridView nie ma wbudowanej obsługi edycji lub usuwania wsadowego, będziemy musieli wywołać te metody programowo, zamiast korzystać z podejścia deklaratywnego bez użycia kodu.
Krok 5. Niepodzielne aktualizowanie danych bazy danych z warstwy prezentacji
Aby zilustrować efekt transakcji podczas aktualizowania partii rekordów, utwórzmy interfejs użytkownika, który wyświetla listę wszystkich produktów w kontrolce GridView i zawiera kontrolkę Sieci Web przycisku, która po kliknięciu ponownie przypisuje wartości produktów CategoryID
. W szczególności ponowne przypisanie kategorii będzie przebiegać tak, aby pierwsze kilka produktów zostało przypisanych do prawidłowej CategoryID
wartości, podczas gdy inne zostały celowo przypisane nieistniejącej CategoryID
wartości. Jeśli spróbujemy zaktualizować bazę danych przy użyciu produktu, którego CategoryID
nie pasuje do istniejącej kategorii s CategoryID
, wystąpi naruszenie ograniczenia klucza obcego i zostanie zgłoszony wyjątek. W tym przykładzie zobaczymy, że w przypadku korzystania z transakcji wyjątek zgłoszony przez naruszenie ograniczenia klucza obcego spowoduje wycofanie poprzednich prawidłowych CategoryID
zmian. Jeśli jednak nie używasz transakcji, modyfikacje początkowych kategorii pozostaną.
Zacznij od otwarcia Transactions.aspx
strony w folderze BatchData
i przeciągnięcia kontrolki GridView z przybornika do Projektant. Ustaw parametr ID
na Products
i z tagu inteligentnego, powiąż go z nową wartością ObjectDataSource o nazwie ProductsDataSource
. Skonfiguruj obiekt ObjectDataSource, aby ściągnąć dane z ProductsBLL
metody klasy s GetProducts
. Będzie to kontrolka GridView tylko do odczytu, dlatego ustaw listy rozwijane na kartach UPDATE, INSERT i DELETE na wartość (Brak), a następnie kliknij przycisk Zakończ.
Rysunek 5. Konfigurowanie obiektu ObjectDataSource do używania ProductsBLL
GetProducts
metody klasy (kliknij, aby wyświetlić obraz w pełnym rozmiarze)
Rysunek 6. Ustawianie Drop-Down Listy na kartach UPDATE, INSERT i DELETE na wartość (Brak) (kliknij, aby wyświetlić obraz o pełnym rozmiarze)
Po ukończeniu pracy kreatora Konfigurowanie źródła danych program Visual Studio utworzy pola BoundFields i CheckBoxField dla pól danych produktu. Usuń wszystkie te pola z wyjątkiem ProductID
właściwości , , CategoryID
ProductName
i CategoryName
i ProductName
CategoryName
BoundFields HeaderText
odpowiednio na Product (Produkt) i Category (Kategoria). W tagu inteligentnym zaznacz opcję Włącz stronicowanie. Po wprowadzeniu tych modyfikacji znaczniki deklaratywne gridView i ObjectDataSource powinny wyglądać następująco:
<asp:GridView ID="Products" runat="server" AllowPaging="True"
AutoGenerateColumns="False" DataKeyNames="ProductID"
DataSourceID="ProductsDataSource">
<Columns>
<asp:BoundField DataField="ProductID" HeaderText="ProductID"
InsertVisible="False" ReadOnly="True"
SortExpression="ProductID" />
<asp:BoundField DataField="ProductName" HeaderText="Product"
SortExpression="ProductName" />
<asp:BoundField DataField="CategoryID" HeaderText="CategoryID"
SortExpression="CategoryID" />
<asp:BoundField DataField="CategoryName" HeaderText="Category"
SortExpression="CategoryName" />
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetProducts" TypeName="ProductsBLL">
</asp:ObjectDataSource>
Następnie dodaj trzy kontrolki Sieci Web przycisku nad kontrolką GridView. Ustaw pierwszą właściwość Text przycisku na Odśwież siatkę, drugą na Modyfikuj kategorie (WITH TRANSACTION), a trzecią z nich na Modyfikuj kategorie (BEZ TRANSAKCJI).
<p>
<asp:Button ID="RefreshGrid" runat="server" Text="Refresh Grid" />
</p>
<p>
<asp:Button ID="ModifyCategoriesWithTransaction" runat="server"
Text="Modify Categories (WITH TRANSACTION)" />
</p>
<p>
<asp:Button ID="ModifyCategoriesWithoutTransaction" runat="server"
Text="Modify Categories (WITHOUT TRANSACTION)" />
</p>
W tym momencie widok Projekt w programie Visual Studio powinien wyglądać podobnie do zrzutu ekranu pokazanego na rysunku 7.
Rysunek 7. Strona zawiera kontrolki sieci Web GridView i trzy przyciski (kliknij, aby wyświetlić obraz pełnowymiarowy)
Utwórz programy obsługi zdarzeń dla każdego z trzech zdarzeń przycisków Click
i użyj następującego kodu:
Protected Sub RefreshGrid_Click _
(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles RefreshGrid.Click
Products.DataBind()
End Sub
Protected Sub ModifyCategoriesWithTransaction_Click _
(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles ModifyCategoriesWithTransaction.Click
' Get the set of products
Dim productsAPI As New ProductsBLL()
Dim productsData As Northwind.ProductsDataTable = productsAPI.GetProducts()
' Update each product's CategoryID
For Each product As Northwind.ProductsRow In productsData
product.CategoryID = product.ProductID
Next
' Update the data using a transaction
productsAPI.UpdateWithTransaction(productsData)
' Refresh the Grid
Products.DataBind()
End Sub
Protected Sub ModifyCategoriesWithoutTransaction_Click _
(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles ModifyCategoriesWithoutTransaction.Click
' Get the set of products
Dim productsAPI As New ProductsBLL()
Dim productsData As Northwind.ProductsDataTable = productsAPI.GetProducts()
' Update each product's CategoryID
For Each product As Northwind.ProductsRow In productsData
product.CategoryID = product.ProductID
Next
' Update the data WITHOUT using a transaction
Dim productsAdapter As New NorthwindTableAdapters.ProductsTableAdapter()
productsAdapter.Update(productsData)
' Refresh the Grid
Products.DataBind()
End Sub
Procedura obsługi zdarzeń przycisku odświeżania Click
po prostu ponownie powiąż dane z kontrolką GridView, wywołując metodę Products
GridView DataBind
.
Druga procedura obsługi zdarzeń ponownie przypisuje produkty CategoryID
i używa nowej metody transakcji z BLL do wykonywania aktualizacji bazy danych pod parasolem transakcji. Należy pamiętać, że każdy produkt CategoryID
jest dowolnie ustawiony na tę samą wartość co jego ProductID
. Będzie to działać dobrze w przypadku pierwszych kilku produktów, ponieważ te produkty mają ProductID
wartości, które mają miejsce mapowania na prawidłowe CategoryID
s. Ale gdy ProductID
zacznie się zbyt duży, to przypadkowe nakładanie się ProductID
s i CategoryID
nie ma już zastosowania.
Trzecia Click
procedura obsługi zdarzeń aktualizuje produkty CategoryID
w taki sam sposób, ale wysyła aktualizację do bazy danych przy użyciu metody domyślnej ProductsTableAdapter
Update
. Ta Update
metoda nie opakowuje serii poleceń w ramach transakcji, dlatego te zmiany są wprowadzane przed pierwszym napotkanym błędem naruszenia ograniczeń klucza obcego będzie się powtarzać.
Aby zademonstrować to zachowanie, odwiedź tę stronę za pośrednictwem przeglądarki. Początkowo powinna zostać wyświetlona pierwsza strona danych, jak pokazano na rysunku 8. Następnie kliknij przycisk Modyfikuj kategorie (WITH TRANSACTION). Spowoduje to wycofanie i próbę zaktualizowania wszystkich wartości produktów CategoryID
, ale spowoduje naruszenie ograniczenia klucza obcego (zobacz Rysunek 9).
Rysunek 8. Produkty są wyświetlane w widoku GridView z możliwością stronicowania (kliknij, aby wyświetlić obraz w pełnym rozmiarze)
Rysunek 9. Ponowne przypisywanie kategorii powoduje naruszenie ograniczenia klucza obcego (kliknij, aby wyświetlić obraz w pełnym rozmiarze)
Teraz naciśnij przycisk Wstecz przeglądarki, a następnie kliknij przycisk Odśwież siatkę. Podczas odświeżania danych powinny być widoczne dokładnie te same dane wyjściowe, co pokazano na rysunku 8. Oznacza to, że mimo że niektóre produkty CategoryID
zostały zmienione na wartości prawne i zaktualizowane w bazie danych, zostały one wycofane, gdy doszło do naruszenia ograniczenia klucza obcego.
Teraz spróbuj kliknąć przycisk Modyfikuj kategorie (BEZ TRANSAKCJI). Spowoduje to błąd naruszenia ograniczenia tego samego klucza obcego (patrz Rysunek 9), ale tym razem te produkty, których CategoryID
wartości zostały zmienione na wartość prawną, nie zostaną wycofane. Naciśnij przycisk Wstecz przeglądarki, a następnie przycisk Odśwież siatkę. Jak pokazano na rysunku 10, CategoryID
liczba pierwszych ośmiu produktów została ponownie przypisana. Na przykład na rysunku 8 Chang miał CategoryID
wartość 1, ale na rysunku 10 został ponownie przydzielony do wartości 2.
Rysunek 10. Niektóre wartości produktów CategoryID
zostały zaktualizowane, podczas gdy inne nie (kliknij, aby wyświetlić obraz w pełnym rozmiarze)
Podsumowanie
Domyślnie metody tableAdapter nie opakowują wykonanych instrukcji bazy danych w zakresie transakcji, ale przy odrobinie pracy możemy dodać metody, które spowodują utworzenie, zatwierdzenie i wycofanie transakcji. W tym samouczku utworzyliśmy trzy takie metody w ProductsTableAdapter
klasie : BeginTransaction
, CommitTransaction
i RollbackTransaction
. Zobaczyliśmy, jak używać tych metod wraz z blokiem Try...Catch
, aby utworzyć serię instrukcji modyfikacji danych niepodzielne. W szczególności utworzyliśmy metodę UpdateWithTransaction
w ProductsTableAdapter
pliku , która używa wzorca aktualizacji usługi Batch do wykonania niezbędnych modyfikacji wierszy dostarczonego ProductsDataTable
elementu . Dodaliśmy również metodę DeleteProductsWithTransaction
do ProductsBLL
klasy w bibliotece List
ProductID
BLL, która akceptuje wartości jako dane wejściowe i wywołuje metodę Delete
wzorca DB-Direct dla każdego ProductID
elementu . Obie metody zaczynają się od utworzenia transakcji, a następnie wykonania instrukcji modyfikacji danych w Try...Catch
bloku. Jeśli wystąpi wyjątek, transakcja zostanie wycofana, w przeciwnym razie zostanie zatwierdzona.
Krok 5 zilustrował wpływ transakcyjnych aktualizacji wsadowych w porównaniu z aktualizacjami wsadowym, które nie korzystały z transakcji. W następnych trzech samouczkach będziemy opierać się na podstawach określonych w tym samouczku i utworzyć interfejsy użytkownika do wykonywania aktualizacji, usuwania i wstawiania wsadowego.
Szczęśliwe programowanie!
Dalsze informacje
Aby uzyskać więcej informacji na temat tematów omówionych w tym samouczku, zapoznaj się z następującymi zasobami:
- Transakcje wykonane łatwo:
System.Transactions
- TransactionScope i DataAdapters
- Korzystanie z transakcji bazy danych Oracle na platformie .NET
Informacje o autorze
Scott Mitchell, autor siedmiu książek ASP/ASP.NET i założyciel 4GuysFromRolla.com, współpracuje z technologiami internetowymi firmy Microsoft od 1998 roku. Scott pracuje jako niezależny konsultant, trener i pisarz. Jego najnowsza książka to Sams Teach Yourself ASP.NET 2.0 w ciągu 24 godzin. Można do niego dotrzeć pod adresem mitchell@4GuysFromRolla.com. Lub za pośrednictwem swojego bloga, który można znaleźć na stronie http://ScottOnWriting.NET.
Specjalne podziękowania
Ta seria samouczków została przejrzyona przez wielu przydatnych recenzentów. Głównymi recenzentami tego samouczka byli Dave Gardner, Hilton Giesenow i Teresa Murphy. Chcesz przejrzeć nadchodzące artykuły MSDN? Jeśli tak, upuść mi wiersz pod adresemmitchell@4GuysFromRolla.com .