Tworzenie nowych procedur składowanych dla elementów TableAdapter typizowanego zestawu danych (VB)
Autor : Scott Mitchell
We wcześniejszych samouczkach utworzyliśmy instrukcje SQL w kodzie i przekazaliśmy instrukcje do bazy danych do wykonania. Alternatywną metodą jest użycie procedur składowanych, w których instrukcje SQL są wstępnie zdefiniowane w bazie danych. Z tego samouczka dowiesz się, jak kreator tableAdapter generuje dla nas nowe procedury składowane.
Wprowadzenie
W tych samouczkach warstwa dostępu do danych (DAL) używa typów zestawów danych. Zgodnie z opisem w samouczku Tworzenie warstwy dostępu do danych typizowane zestawy danych składają się z silnie typiowanych tabel DataTable i TableAdapters. Tabele DataTable reprezentują jednostki logiczne w systemie, podczas gdy interfejs tableAdapters z bazą danych w celu wykonania pracy nad dostępem do danych. Obejmuje to wypełnianie tabel DataTable danymi, wykonywanie zapytań, które zwracają dane skalarne, oraz wstawianie, aktualizowanie i usuwanie rekordów z bazy danych.
Polecenia SQL wykonywane przez elementy TableAdapters mogą być instrukcjami ad hoc JĘZYKA SQL, takimi jak SELECT columnList FROM TableName
, lub procedury składowane. Klasy TableAdapters w naszej architekturze używają instrukcji ad hoc JĘZYKA SQL. Wielu deweloperów i administratorów baz danych preferuje jednak procedury składowane w przypadku instrukcji ad hoc JĘZYKA SQL w celu zapewnienia bezpieczeństwa, możliwości konserwacji i możliwości aktualizacji. Inni zdecydowanie preferują ad hoc instrukcje SQL dla ich elastyczności. W mojej pracy faworyzuję procedury składowane za pomocą instrukcji AD-hoc SQL, ale zdecydowałem się użyć instrukcji ad hoc SQL, aby uprościć wcześniejsze samouczki.
Podczas definiowania obiektu TableAdapter lub dodawania nowych metod kreator tableAdapter ułatwia tworzenie nowych procedur składowanych lub używanie istniejących procedur składowanych, tak jak w przypadku używania instrukcji AD hoc JĘZYKA SQL. W tym samouczku sprawdzimy, jak kreator tableAdapter automatycznie generuje procedury składowane. W następnym samouczku przyjrzymy się, jak skonfigurować metody tableAdapter do używania istniejących lub ręcznie utworzonych procedur składowanych.
Uwaga
Zobacz wpis Rob Howard na blogu Don t Use Stored Procedures Yet? i Frans Bouma wpis w blogu Stored Procedures are Bad, M Kay? for a lively debaty na temat zalet i wad procedur składowanych i ad hoc SQL.
Podstawy procedury składowanej
Funkcje są konstrukcją wspólną dla wszystkich języków programowania. Funkcja to kolekcja instrukcji, które są wykonywane po wywołaniu funkcji. Funkcje mogą akceptować parametry wejściowe i mogą opcjonalnie zwracać wartość. Procedury składowane to konstrukcje bazy danych, które mają wiele podobieństw do funkcji w językach programowania. Procedura składowana składa się z zestawu instrukcji języka T-SQL wykonywanych podczas wywoływanej procedury składowanej. Procedura składowana może akceptować zero do wielu parametrów wejściowych i może zwracać wartości skalarne, parametry wyjściowe lub, najczęściej, zestawy wyników z SELECT
zapytań.
Uwaga
Procedury składowane są często określane jako sprocs lub SPs .
Procedury składowane są tworzone przy użyciu instrukcji CREATE PROCEDURE
języka T-SQL. Na przykład poniższy skrypt języka T-SQL tworzy procedurę składowaną o nazwie , która przyjmuje jeden parametr o GetProductsByCategoryID
nazwie i zwraca ProductID
pola , ProductName
, UnitPrice
i Discontinued
tych kolumn w Products
tabeli, które mają zgodną CategoryID
wartość:@CategoryID
CREATE PROCEDURE GetProductsByCategoryID
(
@CategoryID int
)
AS
SELECT ProductID, ProductName, UnitPrice, Discontinued
FROM Products
WHERE CategoryID = @CategoryID
Po utworzeniu tej procedury składowanej można ją wywołać przy użyciu następującej składni:
EXEC GetProductsByCategory categoryID
Uwaga
W następnym samouczku przeanalizujemy tworzenie procedur składowanych za pomocą środowiska IDE programu Visual Studio. Na potrzeby tego samouczka zezwolimy jednak kreatorowi TableAdapter na automatyczne generowanie procedur składowanych.
Oprócz zwykłego zwracania danych procedury składowane są często używane do wykonywania wielu poleceń bazy danych w zakresie jednej transakcji. Procedura składowana o nazwie DeleteCategory
, na przykład może przyjmować parametr @CategoryID
i wykonywać dwie DELETE
instrukcje: po pierwsze, jedną, aby usunąć powiązane produkty, a drugą usuwającą określoną kategorię. Wiele instrukcji w ramach procedury składowanej nie jest automatycznie opakowanych w ramach transakcji. Aby upewnić się, że wiele poleceń procedury składowanej jest traktowanych jako operacja niepodzielna, należy wydać dodatkowe polecenia języka T-SQL. W kolejnym samouczku zobaczymy, jak opakowować polecenia procedury składowanej w zakresie transakcji.
W przypadku korzystania z procedur składowanych w ramach architektury metody warstwy dostępu do danych wywołują określoną procedurę składowaną, a nie wydając instrukcję ad hoc JĘZYKA SQL. Centralizuje to lokalizację wykonywanych instrukcji SQL (w bazie danych) zamiast definiować ją w architekturze aplikacji. Ta centralizacja prawdopodobnie ułatwia znajdowanie, analizowanie i dostrajanie zapytań oraz zapewnia znacznie jaśniejszy obraz miejsca i sposobu użycia bazy danych.
Aby uzyskać więcej informacji na temat podstaw procedury składowanej, zapoznaj się z zasobami w sekcji Dalsze informacje na końcu tego samouczka.
Krok 1. Tworzenie zaawansowanych scenariuszy warstwy dostępu do danych stron sieci Web
Zanim zaczniemy dyskusję na temat tworzenia dal przy użyciu procedur składowanych, najpierw poświęćmy chwilę, aby utworzyć strony ASP.NET w naszym projekcie witryny internetowej, których będziemy potrzebować w tym celu i kolejnych kilku samouczkach. Zacznij od dodania nowego folderu o nazwie AdvancedDAL
. Następnie dodaj do tego folderu następujące strony ASP.NET, aby skojarzyć każdą stronę ze stroną wzorcową Site.master
:
Default.aspx
NewSprocs.aspx
ExistingSprocs.aspx
JOINs.aspx
AddingColumns.aspx
ComputedColumns.aspx
EncryptingConfigSections.aspx
ManagedFunctionsAndSprocs.aspx
Rysunek 1. Dodawanie stron ASP.NET na potrzeby samouczków dotyczących zaawansowanych scenariuszy warstwy dostępu do danych
Podobnie jak w przypadku innych folderów, Default.aspx
w AdvancedDAL
folderze zostanie wyświetlona lista samouczków w jego sekcji. Pamiętaj, że kontrolka SectionLevelTutorialListing.ascx
użytkownika udostępnia tę funkcję. 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 strony jako wpisy do Web.sitemap
pliku. W szczególności dodaj następujące znaczniki po pracy z danymi <siteMapNode>
wsadowymi:
<siteMapNode url="~/AdvancedDAL/Default.aspx"
title="Advanced DAL Scenarios"
description="Explore a number of advanced Data Access Layer scenarios.">
<siteMapNode url="~/AdvancedDAL/NewSprocs.aspx"
title="Creating New Stored Procedures for TableAdapters"
description="Learn how to have the TableAdapter wizard automatically
create and use stored procedures." />
<siteMapNode url="~/AdvancedDAL/ExistingSprocs.aspx"
title="Using Existing Stored Procedures for TableAdapters"
description="See how to plug existing stored procedures into a
TableAdapter." />
<siteMapNode url="~/AdvancedDAL/JOINs.aspx"
title="Returning Data Using JOINs"
description="Learn how to augment your DataTables to work with data
returned from multiple tables via a JOIN query." />
<siteMapNode url="~/AdvancedDAL/AddingColumns.aspx"
title="Adding DataColumns to a DataTable"
description="Master adding new columns to an existing DataTable." />
<siteMapNode url="~/AdvancedDAL/ComputedColumns.aspx"
title="Working with Computed Columns"
description="Explore how to work with computed columns when using
Typed DataSets." />
<siteMapNode url="~/AdvancedDAL/EncryptingConfigSections.aspx"
title="Protected Connection Strings in Web.config"
description="Protect your connection string information in
Web.config using encryption." />
<siteMapNode url="~/AdvancedDAL/ManagedFunctionsAndSprocs.aspx"
title="Creating Managed SQL Functions and Stored Procedures"
description="See how to create SQL functions and stored procedures
using managed code." />
</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 dla zaawansowanych samouczków dotyczących scenariuszy DAL.
Rysunek 3. Mapa witryny zawiera teraz wpisy dla samouczków zaawansowanych scenariuszy DAL
Krok 2. Konfigurowanie obiektu TableAdapter w celu utworzenia nowych procedur składowanych
Aby zademonstrować tworzenie warstwy dostępu do danych, która używa procedur składowanych zamiast instrukcji ad hoc SQL, utwórzmy nowy typizowanego zestawu danych w ~/App_Code/DAL
folderze o nazwie NorthwindWithSprocs.xsd
. Ponieważ szczegółowo omówiliśmy ten proces w poprzednich samouczkach, przejdziemy szybko przez kroki opisane tutaj. Jeśli utkniesz lub potrzebujesz dalszych instrukcji krok po kroku dotyczących tworzenia i konfigurowania typu zestawu danych, zapoznaj się z samouczkiem Tworzenie warstwy dostępu do danych .
Dodaj nowy zestaw danych do projektu, klikając prawym przyciskiem myszy DAL
folder, wybierając polecenie Dodaj nowy element i wybierając szablon Zestaw danych, jak pokazano na rysunku 4.
Rysunek 4. Dodawanie nowego typu zestawu danych do projektu o nazwie NorthwindWithSprocs.xsd
(kliknij, aby wyświetlić obraz pełnowymiarowy)
Spowoduje to utworzenie nowego typu zestawu danych, otwarcie jego Projektant, utworzenie nowego obiektu TableAdapter i uruchomienie Kreatora konfiguracji tableAdapter. Pierwszy krok Kreatora konfiguracji tableAdapter prosi nas o wybranie bazy danych do pracy. Lista rozwijana powinna zawierać parametry połączenia do bazy danych Northwind. Wybierz to i kliknij przycisk Dalej.
Na następnym ekranie możemy wybrać sposób uzyskiwania dostępu do bazy danych przez narzędzie TableAdapter. W poprzednich samouczkach wybraliśmy pierwszą opcję Użyj instrukcji SQL. Na potrzeby tego samouczka wybierz drugą opcję Utwórz nowe procedury składowane, a następnie kliknij przycisk Dalej.
Rysunek 5. Poinstruowanie obiektu TableAdapter o utworzeniu nowych procedur składowanych (kliknij, aby wyświetlić obraz o pełnym rozmiarze)
Podobnie jak w przypadku korzystania z instrukcji ad hoc SQL, w poniższym kroku zostanie wyświetlony monit o podanie SELECT
instrukcji dla głównego zapytania tableAdapter. Jednak zamiast używania instrukcji wprowadzonej SELECT
tutaj w celu bezpośredniego wykonania zapytania ad hoc kreator tableAdapter utworzy procedurę składowaną zawierającą to SELECT
zapytanie.
Użyj następującego SELECT
zapytania dla tego elementu TableAdapter:
SELECT ProductID, ProductName, SupplierID, CategoryID,
QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
ReorderLevel, Discontinued
FROM Products
Rysunek 6. Wprowadź SELECT
zapytanie (kliknij, aby wyświetlić obraz pełnowymiarowy)
Uwaga
Powyższe zapytanie nieco różni się od głównego zapytania ProductsTableAdapter
w typowym zestawie Northwind
danych. Pamiętaj, że element ProductsTableAdapter
w zestawie Northwind
danych typed zawiera dwa skorelowane podzapytania, aby przywrócić nazwę kategorii i nazwę firmy dla każdej kategorii i dostawcy produktu. W nadchodzącym samouczku Aktualizowanie elementu TableAdapter do korzystania z numerów JOINs przyjrzymy się dodaniu tych powiązanych danych do tego elementu TableAdapter.
Poświęć chwilę, aby kliknąć przycisk Opcje zaawansowane. W tym miejscu można określić, czy kreator powinien również generować instrukcje wstawiania, aktualizacji i usuwania dla klasy TableAdapter, czy używać optymistycznej współbieżności, oraz czy tabela danych powinna być odświeżona po wstawieniu i aktualizacjach. Opcja Generuj instrukcje Wstaw, Aktualizuj i Usuń jest domyślnie zaznaczona. Pozostaw to zaznaczone. W tym samouczku pozostaw niezaznaczone opcje Użyj optymistycznej współbieżności.
Jeśli procedury składowane są tworzone automatycznie przez kreatora TableAdapter, wydaje się, że opcja Odśwież tabelę danych jest ignorowana. Niezależnie od tego, czy to pole wyboru jest zaznaczone, wynikowe procedury wstawiania i aktualizowania pobierają właśnie wstawiony lub właśnie zaktualizowany rekord, jak zobaczymy w kroku 3.
Rysunek 7. Pozostaw zaznaczoną opcję Generowanie instrukcji Wstaw, Aktualizuj i Usuń
Uwaga
Jeśli opcja Użyj optymistycznej współbieżności jest zaznaczona, kreator doda dodatkowe warunki do WHERE
klauzuli, która uniemożliwia aktualizowanie danych w przypadku zmian w innych polach. Zapoznaj się z samouczkiem Implementowanie optymistycznej współbieżności , aby uzyskać więcej informacji na temat korzystania z wbudowanej funkcji sterowania współbieżnością w programie TableAdapter.
Po wprowadzeniu zapytania i potwierdzeniu, że jest zaznaczona opcja Generuj SELECT
instrukcje Wstaw, Aktualizuj i Usuń, kliknij przycisk Dalej. Na następnym ekranie pokazanym na rysunku 8 zostanie wyświetlony monit o nazwy procedur składowanych, które kreator utworzy do wybierania, wstawiania, aktualizowania i usuwania danych. Zmień nazwy tych procedur składowanych na Products_Select
, Products_Insert
, Products_Update
i Products_Delete
.
Rysunek 8. Zmienianie nazwy procedur składowanych (kliknij, aby wyświetlić obraz pełnowymiarowy)
Aby wyświetlić język T-SQL kreator TableAdapter będzie używany do tworzenia czterech procedur składowanych, kliknij przycisk Podgląd skryptu SQL. W oknie dialogowym Podgląd skryptu SQL można zapisać skrypt w pliku lub skopiować go do schowka.
Rysunek 9. Podgląd skryptu SQL używanego do generowania procedur składowanych
Po nazewnictwie procedur składowanych kliknij przycisk Dalej, aby nazwać odpowiednie metody TableAdapter. Podobnie jak w przypadku korzystania z instrukcji AD-hoc SQL, możemy utworzyć metody, które wypełniają istniejącą tabelę DataTable lub zwracają nową. Możemy również określić, czy tabelaAdapter powinna zawierać wzorzec DB-Direct do wstawiania, aktualizowania i usuwania rekordów. Pozostaw zaznaczone wszystkie trzy pola wyboru, ale zmień nazwę metody Return a DataTable na GetProducts
(jak pokazano na rysunku 10).
Rysunek 10. Nadaj metodom Fill
nazwę i GetProducts
(kliknij, aby wyświetlić obraz pełnowymiarowy)
Kliknij przycisk Dalej, aby wyświetlić podsumowanie kroków, które wykona kreator. Ukończ pracę kreatora, klikając przycisk Zakończ. Po zakończeniu pracy kreatora nastąpi powrót do Projektant zestawu danych, który powinien teraz zawierać ProductsDataTable
element .
Rysunek 11. Zestaw danych Projektant Pokazuje nowo dodane ProductsDataTable
(kliknij, aby wyświetlić obraz pełnowymiarowy)
Krok 3. Badanie nowo utworzonych procedur składowanych
Kreator TableAdapter używany w kroku 2 automatycznie utworzył procedury składowane do wybierania, wstawiania, aktualizowania i usuwania danych. Te procedury składowane można wyświetlać lub modyfikować za pomocą programu Visual Studio, przechodząc do Eksploratora serwera i przechodzenia do szczegółów w folderze Procedury składowane bazy danych. Jak pokazano na rysunku 12, baza danych Northwind zawiera cztery nowe procedury składowane: Products_Delete
, , Products_Insert
Products_Select
i Products_Update
.
Rysunek 12. Cztery procedury składowane utworzone w kroku 2 można znaleźć w folderze Procedury składowane bazy danych
Uwaga
Jeśli nie widzisz Eksploratora serwera, przejdź do menu Widok i wybierz opcję Eksplorator serwera. Jeśli nie widzisz procedur składowanych związanych z produktem dodanych z kroku 2, spróbuj kliknąć prawym przyciskiem myszy folder Procedury składowane i wybrać polecenie Odśwież.
Aby wyświetlić lub zmodyfikować procedurę składowaną, kliknij dwukrotnie jego nazwę w Eksploratorze serwera lub, alternatywnie, kliknij prawym przyciskiem myszy procedurę składowaną i wybierz polecenie Otwórz. Rysunek 13 przedstawia procedurę składowaną po otwarciu Products_Delete
.
Rysunek 13. Procedury składowane można otwierać i modyfikować z poziomu programu Visual Studio (kliknij, aby wyświetlić obraz pełnowymiarowy)
Zawartość Products_Delete
procedur składowanych i jest Products_Select
dość prosta. Procedury Products_Insert
składowane i Products_Update
, z drugiej strony, uzasadniają ściślejszą kontrolę, ponieważ obie wykonują SELECT
oświadczenie po ich INSERT
oświadczeniach i UPDATE
. Na przykład następujący kod SQL składa się z Products_Insert
procedury składowanej:
ALTER PROCEDURE dbo.Products_Insert
(
@ProductName nvarchar(40),
@SupplierID int,
@CategoryID int,
@QuantityPerUnit nvarchar(20),
@UnitPrice money,
@UnitsInStock smallint,
@UnitsOnOrder smallint,
@ReorderLevel smallint,
@Discontinued bit
)
AS
SET NOCOUNT OFF;
INSERT INTO [Products] ([ProductName], [SupplierID], [CategoryID], [QuantityPerUnit],
[UnitPrice], [UnitsInStock], [UnitsOnOrder], [ReorderLevel], [Discontinued])
VALUES (@ProductName, @SupplierID, @CategoryID, @QuantityPerUnit, @UnitPrice,
@UnitsInStock, @UnitsOnOrder, @ReorderLevel, @Discontinued);
SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice,
UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued
FROM Products
WHERE (ProductID = SCOPE_IDENTITY())
Procedura składowana przyjmuje jako parametry Products
wejściowe kolumny zwrócone przez SELECT
zapytanie określone w kreatorze TableAdapter, a te wartości są używane w INSERT
instrukcji . Po instrukcji INSERT
SELECT
zapytanie służy do zwracania Products
wartości kolumn (w tym ProductID
) nowo dodanego rekordu. Ta funkcja odświeżania jest przydatna podczas dodawania nowego rekordu przy użyciu wzorca aktualizacji usługi Batch, ponieważ automatycznie aktualizuje nowo dodane ProductRow
właściwości wystąpień ProductID
przy użyciu automatycznie przyrostowanych wartości przypisanych przez bazę danych.
Poniższy kod ilustruje tę funkcję. Zawiera element ProductsTableAdapter
i ProductsDataTable
utworzony dla NorthwindWithSprocs
typu zestawu danych. Nowy produkt jest dodawany do bazy danych, tworząc ProductsRow
wystąpienie, podając jego wartości i wywołując metodę TableAdapter Update
, przekazując ProductsDataTable
element . Wewnętrznie metoda TableAdapter Update
wylicza ProductsRow
wystąpienia w przekazanej tabeli DataTable (w tym przykładzie jest tylko jedna — ta, która właśnie została dodana) i wykonuje odpowiednie polecenie wstawiania, aktualizacji lub usuwania. W takim przypadku Products_Insert
procedura składowana jest wykonywana, która dodaje nowy rekord do Products
tabeli i zwraca szczegóły nowo dodanego rekordu. Wartość ProductsRow
wystąpienia ProductID
jest następnie aktualizowana. Po zakończeniu Update
metody możemy uzyskać dostęp do nowo dodanej wartości rekordu ProductID
za pośrednictwem ProductsRow
właściwości s ProductID
.
' Create the ProductsTableAdapter and ProductsDataTable
Dim productsAPI As New NorthwindWithSprocsTableAdapters.ProductsTableAdapter
Dim products As New NorthwindWithSprocs.ProductsDataTable
' Create a new ProductsRow instance and set its properties
Dim product As NorthwindWithSprocs.ProductsRow = products.NewProductsRow()
product.ProductName = "New Product"
product.CategoryID = 1 ' Beverages
product.Discontinued = False
' Add the ProductsRow instance to the DataTable
products.AddProductsRow(product)
' Update the DataTable using the Batch Update pattern
productsAPI.Update(products)
' At this point, we can determine the value of the newly-added record's ProductID
Dim newlyAddedProductIDValue as Integer = product.ProductID
Procedura Products_Update
składowana podobnie zawiera instrukcję SELECT
po jego UPDATE
instrukcji.
ALTER PROCEDURE dbo.Products_Update
(
@ProductName nvarchar(40),
@SupplierID int,
@CategoryID int,
@QuantityPerUnit nvarchar(20),
@UnitPrice money,
@UnitsInStock smallint,
@UnitsOnOrder smallint,
@ReorderLevel smallint,
@Discontinued bit,
@Original_ProductID int,
@ProductID int
)
AS
SET NOCOUNT OFF;
UPDATE [Products]
SET [ProductName] = @ProductName, [SupplierID] = @SupplierID,
[CategoryID] = @CategoryID, [QuantityPerUnit] = @QuantityPerUnit,
[UnitPrice] = @UnitPrice, [UnitsInStock] = @UnitsInStock,
[UnitsOnOrder] = @UnitsOnOrder, [ReorderLevel] = @ReorderLevel,
[Discontinued] = @Discontinued
WHERE (([ProductID] = @Original_ProductID));
SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit,
UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued
FROM Products
WHERE (ProductID = @ProductID)
Należy pamiętać, że ta procedura składowana zawiera dwa parametry wejściowe dla ProductID
: @Original_ProductID
i @ProductID
. Ta funkcja umożliwia scenariusze, w których klucz podstawowy może zostać zmieniony. Na przykład w bazie danych pracowników każdy rekord pracownika może używać numeru ubezpieczenia społecznego pracownika jako klucza podstawowego. Aby zmienić istniejący numer ubezpieczenia społecznego pracownika, należy podać zarówno nowy numer ubezpieczenia społecznego, jak i oryginalny. W przypadku Products
tabeli takie funkcje nie są potrzebne, ponieważ kolumna ProductID
jest kolumną IDENTITY
i nigdy nie powinna być zmieniana. W rzeczywistości UPDATE
instrukcja w procedurze Products_Update
składowanej nie zawiera ProductID
kolumny na liście kolumn. Tak więc, chociaż @Original_ProductID
jest używany w klauzuli instrukcji UPDATE
WHERE
, jest on zbędny dla Products
tabeli i może zostać zastąpiony przez @ProductID
parametr . Podczas modyfikowania parametrów procedury składowanej ważne jest również zaktualizowanie metod TableAdapter korzystających z tej procedury składowanej.
Krok 4. Modyfikowanie parametrów procedury składowanej i aktualizowanie tabeliAdapter
@Original_ProductID
Ponieważ parametr jest nadmiarowy, całkowicie usuńmy go z Products_Update
procedury składowanej. Otwórz procedurę Products_Update
składowaną, usuń @Original_ProductID
parametr i w WHERE
klauzuli instrukcji zmień nazwę parametru UPDATE
używaną z @Original_ProductID
na @ProductID
. Po wprowadzeniu tych zmian język T-SQL w procedurze składowanej powinien wyglądać następująco:
ALTER PROCEDURE dbo.Products_Update
(
@ProductName nvarchar(40),
@SupplierID int,
@CategoryID int,
@QuantityPerUnit nvarchar(20),
@UnitPrice money,
@UnitsInStock smallint,
@UnitsOnOrder smallint,
@ReorderLevel smallint,
@Discontinued bit,
@ProductID int
)
AS
SET NOCOUNT OFF;
UPDATE [Products] SET [ProductName] = @ProductName, [SupplierID] = @SupplierID,
[CategoryID] = @CategoryID, [QuantityPerUnit] = @QuantityPerUnit,
[UnitPrice] = @UnitPrice, [UnitsInStock] = @UnitsInStock,
[UnitsOnOrder] = @UnitsOnOrder, [ReorderLevel] = @ReorderLevel,
[Discontinued] = @Discontinued
WHERE (([ProductID] = @ProductID));
SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit,
UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued
FROM Products
WHERE (ProductID = @ProductID)
Aby zapisać te zmiany w bazie danych, kliknij ikonę Zapisz na pasku narzędzi lub naciśnij klawisze Ctrl+S. W tym momencie Products_Update
procedura składowana nie oczekuje parametru wejściowego @Original_ProductID
, ale parametr TableAdapter jest skonfigurowany do przekazywania takiego parametru. Parametry, które usługa TableAdapter wyśle do Products_Update
procedury składowanej, wybierając element TableAdapter w zestawie danych Projektant, przechodząc do okno Właściwości, a następnie klikając wielokropek w UpdateCommand
Parameters
kolekcji. Spowoduje to wyświetlenie okna dialogowego Kolekcja parametrów Redaktor pokazanego na rysunku 14.
Rysunek 14. Kolekcja parametrów Redaktor Listy parametrów używanych do Products_Update
procedury składowanej
Ten parametr można usunąć tutaj, po prostu wybierając @Original_ProductID
parametr z listy elementów członkowskich i klikając przycisk Usuń.
Możesz też odświeżyć parametry używane dla wszystkich metod, klikając prawym przyciskiem myszy tabelę w Projektant i wybierając pozycję Konfiguruj. Spowoduje to wyświetlenie kreatora konfiguracji tableAdapter z listą procedur składowanych używanych do wybierania, wstawiania, aktualizowania i usuwania wraz z parametrami oczekiwanymi przez procedury składowane. Jeśli klikniesz listę rozwijaną Aktualizacja, zobaczysz Products_Update
oczekiwane parametry wejściowe procedur składowanych, które nie są już uwzględniane @Original_ProductID
(zobacz Rysunek 15). Po prostu kliknij przycisk Zakończ, aby automatycznie zaktualizować kolekcję parametrów używaną przez tableAdapter.
Rysunek 15. Możesz też użyć Kreatora konfiguracji tableAdapter, aby odświeżyć kolekcje parametrów metod (kliknij, aby wyświetlić obraz pełnowymiarowy)
Krok 5. Dodawanie dodatkowych metod TableAdapter
Jak pokazano w kroku 2, podczas tworzenia nowej tabeliAdapter łatwo jest automatycznie wygenerować odpowiednie procedury składowane. To samo dotyczy dodawania dodatkowych metod do klasy TableAdapter. Aby to zilustrować, dodajmy metodę GetProductByProductID(productID)
do utworzonej ProductsTableAdapter
w kroku 2. Ta metoda będzie przyjmować jako wartość wejściową ProductID
i zwracać szczegóły dotyczące określonego produktu.
Zacznij od kliknięcia prawym przyciskiem myszy tabeli TableAdapter i wybrania pozycji Dodaj zapytanie z menu kontekstowego.
Rysunek 16. Dodawanie nowego zapytania do tabeli TableAdapter
Spowoduje to uruchomienie kreatora konfiguracji zapytań TableAdapter, który najpierw monituje o uzyskanie dostępu do bazy danych przez narzędzie TableAdapter. Aby utworzyć nową procedurę składowaną, wybierz opcję Utwórz nową procedurę składowaną i kliknij przycisk Dalej.
Rysunek 17. Wybierz opcję Utwórz nową procedurę składowaną (kliknij, aby wyświetlić obraz pełnowymiarowy)
Na następnym ekranie zostanie wyświetlony monit o zidentyfikowanie typu zapytania do wykonania, czy zwróci zestaw wierszy, czy pojedynczą wartość skalarną, czy też wykona instrukcję UPDATE
, INSERT
lub DELETE
. GetProductByProductID(productID)
Ponieważ metoda zwróci wiersz, pozostaw opcję SELECT, która zwraca zaznaczoną opcję wiersza i naciśnij przycisk Dalej.
Rysunek 18. Wybierz pozycję SELECT, która zwraca opcję wiersza (kliknij, aby wyświetlić obraz pełnowymiarowy)
Na następnym ekranie zostanie wyświetlone główne zapytanie TableAdapter, które zawiera tylko nazwę procedury składowanej (dbo.Products_Select
). Zastąp nazwę procedury składowanej następującą SELECT
instrukcją, która zwraca wszystkie pola produktu dla określonego produktu:
SELECT ProductID, ProductName, SupplierID, CategoryID,
QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
ReorderLevel, Discontinued
FROM Products
WHERE ProductID = @ProductID
Rysunek 19. Zastąp nazwę procedury składowanej zapytaniem SELECT
(kliknij, aby wyświetlić obraz pełnowymiarowy)
Na kolejnym ekranie zostanie wyświetlona prośba o nadenie nazwy procedurze składowanej, która zostanie utworzona. Wprowadź nazwę Products_SelectByProductID
i kliknij przycisk Dalej.
Rysunek 20. Nadaj nowej procedurze Products_SelectByProductID
składowanej nazwę (kliknij, aby wyświetlić obraz pełnowymiarowy)
Ostatni krok kreatora umożliwia zmianę wygenerowanych nazw metod, a także wskazanie, czy należy użyć wzorca Fill a DataTable, Return a DataTable pattern, or both . W przypadku tej metody pozostaw zaznaczone obie opcje, ale zmień nazwę metod na FillByProductID
i GetProductByProductID
. Kliknij przycisk Dalej, aby wyświetlić podsumowanie kroków, które kreator wykona, a następnie kliknij przycisk Zakończ, aby zakończyć pracę kreatora.
Rysunek 21. Zmiana nazwy metod FillByProductID
tableAdapter na i GetProductByProductID
(kliknij, aby wyświetlić obraz pełnowymiarowy)
Po zakończeniu pracy kreatora tabelaAdapter ma dostępną nową metodę, GetProductByProductID(productID)
która po wywołaniu wykona właśnie utworzoną procedurę Products_SelectByProductID
składowaną. Poświęć chwilę, aby wyświetlić tę nową procedurę składowaną z Eksploratora serwera, przechodząc do szczegółów folderu Procedury składowane i otwierając Products_SelectByProductID
(jeśli nie jest ona widoczna, kliknij prawym przyciskiem myszy folder Procedury składowane i wybierz polecenie Odśwież).
Należy pamiętać, że SelectByProductID
procedura składowana przyjmuje @ProductID
jako parametr wejściowy i wykonuje instrukcję SELECT
wprowadzoną w kreatorze.
ALTER PROCEDURE dbo.Products_SelectByProductID
(
@ProductID int
)
AS
SET NOCOUNT ON;
SELECT ProductID, ProductName, SupplierID, CategoryID,
QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
ReorderLevel, Discontinued
FROM Products
WHERE ProductID = @ProductID
Krok 6. Tworzenie klasy warstwy logiki biznesowej
W całej serii samouczków staraliśmy się zachować warstwową architekturę, w której warstwa prezentacji wykonała wszystkie jej wywołania do warstwy logiki biznesowej (BLL). Aby zachować zgodność z tą decyzją projektową, najpierw musimy utworzyć klasę BLL dla nowego typu zestawu danych, zanim będziemy mogli uzyskać dostęp do danych produktów z warstwy prezentacji.
Utwórz nowy plik klasy o nazwie ProductsBLLWithSprocs.vb
w folderze ~/App_Code/BLL
i dodaj do niego następujący kod:
Imports NorthwindWithSprocsTableAdapters
<System.ComponentModel.DataObject()> _
Public Class ProductsBLLWithSprocs
Private _productsAdapter As ProductsTableAdapter = Nothing
Protected ReadOnly Property Adapter() As ProductsTableAdapter
Get
If _productsAdapter Is Nothing Then
_productsAdapter = New ProductsTableAdapter()
End If
Return _productsAdapter
End Get
End Property
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Select, True)> _
Public Function GetProducts() As NorthwindWithSprocs.ProductsDataTable
Return Adapter.GetProducts()
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Select, False)> _
Public Function GetProductByProductID(ByVal productID As Integer) _
As NorthwindWithSprocs.ProductsDataTable
Return Adapter.GetProductByProductID(productID)
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Insert, True)> _
Public Function AddProduct _
(ByVal productName As String, ByVal supplierID As Nullable(Of Integer), _
ByVal categoryID As Nullable(Of Integer), ByVal quantityPerUnit As String, _
ByVal unitPrice As Nullable(Of Decimal), _
ByVal unitsInStock As Nullable(Of Short), _
ByVal unitsOnOrder As Nullable(Of Short), _
ByVal reorderLevel As Nullable(Of Short), _
ByVal discontinued As Boolean) _
As Boolean
' Create a new ProductRow instance
Dim products As New NorthwindWithSprocs.ProductsDataTable()
Dim product As NorthwindWithSprocs.ProductsRow = products.NewProductsRow()
product.ProductName = productName
If Not supplierID.HasValue Then
product.SetSupplierIDNull()
Else
product.SupplierID = supplierID.Value
End If
If Not categoryID.HasValue Then
product.SetCategoryIDNull()
Else
product.CategoryID = categoryID.Value
End If
If quantityPerUnit Is Nothing Then
product.SetQuantityPerUnitNull()
Else
product.QuantityPerUnit = quantityPerUnit
End If
If Not unitPrice.HasValue Then
product.SetUnitPriceNull()
Else
product.UnitPrice = unitPrice.Value
End If
If Not unitsInStock.HasValue Then
product.SetUnitsInStockNull()
Else
product.UnitsInStock = unitsInStock.Value
End If
If Not unitsOnOrder.HasValue Then
product.SetUnitsOnOrderNull()
Else
product.UnitsOnOrder = unitsOnOrder.Value
End If
If Not reorderLevel.HasValue Then
product.SetReorderLevelNull()
Else
product.ReorderLevel = reorderLevel.Value
End If
product.Discontinued = discontinued
' Add the new product
products.AddProductsRow(product)
Dim rowsAffected As Integer = Adapter.Update(products)
' Return true if precisely one row was inserted, otherwise false
Return rowsAffected = 1
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Update, True)> _
Public Function UpdateProduct
(ByVal productName As String, ByVal supplierID As Nullable(Of Integer), _
ByVal categoryID As Nullable(Of Integer), ByVal quantityPerUnit As String, _
ByVal unitPrice As Nullable(Of Decimal), _
ByVal unitsInStock As Nullable(Of Short), _
ByVal unitsOnOrder As Nullable(Of Short), _
ByVal reorderLevel As Nullable(Of Short), _
ByVal discontinued As Boolean, ByVal productID As Integer) _
As Boolean
Dim products As NorthwindWithSprocs.ProductsDataTable = _
Adapter.GetProductByProductID(productID)
If products.Count = 0 Then
' no matching record found, return false
Return False
End If
Dim product As NorthwindWithSprocs.ProductsRow = products(0)
product.ProductName = productName
If Not supplierID.HasValue Then
product.SetSupplierIDNull()
Else
product.SupplierID = supplierID.Value
End If
If Not categoryID.HasValue Then
product.SetCategoryIDNull()
Else
product.CategoryID = categoryID.Value
End If
If quantityPerUnit Is Nothing Then
product.SetQuantityPerUnitNull()
Else
product.QuantityPerUnit = quantityPerUnit
End If
If Not unitPrice.HasValue Then
product.SetUnitPriceNull()
Else
product.UnitPrice = unitPrice.Value
End If
If Not unitsInStock.HasValue Then
product.SetUnitsInStockNull()
Else
product.UnitsInStock = unitsInStock.Value
End If
If Not unitsOnOrder.HasValue Then
product.SetUnitsOnOrderNull()
Else
product.UnitsOnOrder = unitsOnOrder.Value
End If
If Not reorderLevel.HasValue Then
product.SetReorderLevelNull()
Else
product.ReorderLevel = reorderLevel.Value
End If
product.Discontinued = discontinued
' Update the product record
Dim rowsAffected As Integer = Adapter.Update(product)
' Return true if precisely one row was updated, otherwise false
Return rowsAffected = 1
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Delete, True)> _
Public Function DeleteProduct(ByVal productID As Integer) As Boolean
Dim rowsAffected As Integer = Adapter.Delete(productID)
' Return true if precisely one row was deleted, otherwise false
Return rowsAffected = 1
End Function
End Class
Ta klasa naśladuje ProductsBLL
semantyka klas z wcześniejszych samouczków, ale używa ProductsTableAdapter
obiektów i ProductsDataTable
z zestawu NorthwindWithSprocs
danych. Na przykład zamiast instrukcji Imports NorthwindTableAdapters
na początku pliku klasy, tak jak ProductsBLL
w przypadku ProductsBLLWithSprocs
, klasa używa klasy Imports NorthwindWithSprocsTableAdapters
. Podobnie ProductsDataTable
obiekty i ProductsRow
używane w tej klasie są poprzedzone przestrzenią NorthwindWithSprocs
nazw. Klasa ProductsBLLWithSprocs
udostępnia dwie metody dostępu do danych i GetProducts
GetProductByProductID
, oraz umożliwiające dodawanie, aktualizowanie i usuwanie pojedynczego wystąpienia produktu.
Krok 7. Praca z zestawemNorthwindWithSprocs
danych z warstwy prezentacji
W tym momencie utworzyliśmy dal, który używa procedur składowanych do uzyskiwania dostępu do bazowych danych i modyfikowania ich. Utworzyliśmy również podstawowe BLL z metodami pobierania wszystkich produktów lub określonego produktu wraz z metodami dodawania, aktualizowania i usuwania produktów. Aby zaokrąglić ten samouczek, utwórzmy stronę ASP.NET, która używa klasy BLL do wyświetlania ProductsBLLWithSprocs
, aktualizowania i usuwania rekordów.
NewSprocs.aspx
Otwórz stronę w folderze AdvancedDAL
i przeciągnij element GridView z przybornika do Projektant, nadając mu Products
nazwę . W tagu inteligentnym GridView wybierz powiązanie go z nowym obiektem ObjectDataSource o nazwie ProductsDataSource
. Skonfiguruj obiekt ObjectDataSource do używania klasy, jak pokazano na rysunku ProductsBLLWithSprocs
22.
Rysunek 22. Konfigurowanie obiektu ObjectDataSource do używania ProductsBLLWithSprocs
klasy (kliknij, aby wyświetlić obraz w pełnym rozmiarze)
Lista rozwijana na karcie SELECT ma dwie opcje: GetProducts
i GetProductByProductID
. Ponieważ chcemy wyświetlić wszystkie produkty w elementy GridView, wybierz metodę GetProducts
. Listy rozwijane na kartach UPDATE, INSERT i DELETE mają tylko jedną metodę. Upewnij się, że każda z tych list rozwijanych ma wybraną odpowiednią metodę, a następnie kliknij przycisk Zakończ.
Po zakończeniu pracy kreatora ObjectDataSource program Visual Studio doda pola BoundFields i CheckBoxField do kontrolki GridView dla pól danych produktu. Włącz wbudowane funkcje edytowania i usuwania kontrolki GridView, zaznaczając opcje Włącz edytowanie i Włącz usuwanie w tagu inteligentnym.
Rysunek 23. Strona zawiera kontrolkę GridView z włączoną obsługą edycji i usuwania (kliknij, aby wyświetlić obraz w pełnym rozmiarze)
Jak wspomniano w poprzednich samouczkach, po zakończeniu pracy kreatora ObjectDataSource program Visual Studio ustawia OldValuesParameterFormatString
właściwość na original_{0}. Należy przywrócić jego wartość domyślną, {0} aby funkcje modyfikacji danych działały prawidłowo, biorąc pod uwagę parametry oczekiwane przez metody w naszej usłudze BLL. W związku z tym należy ustawić OldValuesParameterFormatString
właściwość na {0} lub całkowicie usunąć właściwość ze składni deklaratywnej.
Po zakończeniu pracy kreatora Konfigurowanie źródła danych włączenie edytowania i usuwania obsługi w kodzie GridView i zwrócenie właściwości ObjectDataSource OldValuesParameterFormatString
do jej wartości domyślnej znaczniki deklaratywne strony powinny wyglądać podobnie do następujących:
<asp:GridView ID="Products" runat="server" AutoGenerateColumns="False"
DataKeyNames="ProductID" DataSourceID="ProductsDataSource">
<Columns>
<asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
<asp:BoundField DataField="ProductID" HeaderText="ProductID"
InsertVisible="False" ReadOnly="True"
SortExpression="ProductID" />
<asp:BoundField DataField="ProductName" HeaderText="ProductName"
SortExpression="ProductName" />
<asp:BoundField DataField="SupplierID" HeaderText="SupplierID"
SortExpression="SupplierID" />
<asp:BoundField DataField="CategoryID" HeaderText="CategoryID"
SortExpression="CategoryID" />
<asp:BoundField DataField="QuantityPerUnit" HeaderText="QuantityPerUnit"
SortExpression="QuantityPerUnit" />
<asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice"
SortExpression="UnitPrice" />
<asp:BoundField DataField="UnitsInStock" HeaderText="UnitsInStock"
SortExpression="UnitsInStock" />
<asp:BoundField DataField="UnitsOnOrder" HeaderText="UnitsOnOrder"
SortExpression="UnitsOnOrder" />
<asp:BoundField DataField="ReorderLevel" HeaderText="ReorderLevel"
SortExpression="ReorderLevel" />
<asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
SortExpression="Discontinued" />
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
DeleteMethod="DeleteProduct" InsertMethod="AddProduct"
SelectMethod="GetProducts" TypeName="ProductsBLLWithSprocs"
UpdateMethod="UpdateProduct">
<DeleteParameters>
<asp:Parameter Name="productID" Type="Int32" />
</DeleteParameters>
<UpdateParameters>
<asp:Parameter Name="productName" Type="String" />
<asp:Parameter Name="supplierID" Type="Int32" />
<asp:Parameter Name="categoryID" Type="Int32" />
<asp:Parameter Name="quantityPerUnit" Type="String" />
<asp:Parameter Name="unitPrice" Type="Decimal" />
<asp:Parameter Name="unitsInStock" Type="Int16" />
<asp:Parameter Name="unitsOnOrder" Type="Int16" />
<asp:Parameter Name="reorderLevel" Type="Int16" />
<asp:Parameter Name="discontinued" Type="Boolean" />
<asp:Parameter Name="productID" Type="Int32" />
</UpdateParameters>
<InsertParameters>
<asp:Parameter Name="productName" Type="String" />
<asp:Parameter Name="supplierID" Type="Int32" />
<asp:Parameter Name="categoryID" Type="Int32" />
<asp:Parameter Name="quantityPerUnit" Type="String" />
<asp:Parameter Name="unitPrice" Type="Decimal" />
<asp:Parameter Name="unitsInStock" Type="Int16" />
<asp:Parameter Name="unitsOnOrder" Type="Int16" />
<asp:Parameter Name="reorderLevel" Type="Int16" />
<asp:Parameter Name="discontinued" Type="Boolean" />
</InsertParameters>
</asp:ObjectDataSource>
W tym momencie możemy uporządkować kontrolkę GridView, dostosowując interfejs edycji w celu uwzględnienia walidacji, renderowania CategoryID
kolumn i SupplierID
jako listy DropDownLists itd. Możemy również dodać potwierdzenie po stronie klienta do przycisku Usuń i zachęcam do podjęcia czasu na wdrożenie tych ulepszeń. Ponieważ te tematy zostały omówione w poprzednich samouczkach, nie omówimy ich ponownie tutaj.
Niezależnie od tego, czy ulepszysz widok GridView, czy nie, przetestuj podstawowe funkcje strony w przeglądarce. Jak pokazano na rysunku 24, strona wyświetla listę produktów w elementy GridView, które zapewniają możliwość edytowania i usuwania poszczególnych wierszy.
Rysunek 24. Produkty można wyświetlać, edytować i usuwać z widoku GridView (kliknij, aby wyświetlić obraz o pełnym rozmiarze)
Podsumowanie
Klasy TableAdapters w typowanym zestawie danych mogą uzyskiwać dostęp do danych z bazy danych przy użyciu instrukcji AD-hoc SQL lub procedur składowanych. Podczas pracy z procedurami składowanymi można użyć istniejących procedur składowanych lub kreator tableAdapter może zostać poinstruowany o utworzenie nowych procedur składowanych na SELECT
podstawie zapytania. W tym samouczku przedstawiono sposób automatycznego tworzenia procedur składowanych.
Chociaż automatyczne generowanie procedur składowanych pomaga zaoszczędzić czas, istnieją pewne przypadki, w których procedura składowana utworzona przez kreatora nie jest zgodna z tym, co utworzylibyśmy samodzielnie. Przykładem jest Products_Update
procedura składowana, która oczekiwała zarówno @Original_ProductID
parametrów, jak i @ProductID
parametrów wejściowych, mimo że @Original_ProductID
parametr był zbędny.
W wielu scenariuszach procedury składowane mogły już zostać utworzone lub możemy chcieć utworzyć je ręcznie, aby mieć dokładnszy stopień kontroli nad poleceniami procedury składowanej. W obu przypadkach chcemy poinstruować tableAdapter, aby używał istniejących procedur składowanych dla swoich metod. Zobaczymy, jak to zrobić w następnym samouczku.
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:
- Tworzenie i obsługa procedur składowanych
- Procedury składowane: omówienie
- Tworzenie procedury składowanej
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łównym recenzentem tego samouczka był Hilton Geisenow. Chcesz przejrzeć nadchodzące artykuły MSDN? Jeśli tak, upuść mi wiersz pod adresemmitchell@4GuysFromRolla.com .
Opinia
https://aka.ms/ContentUserFeedback.
Dostępne już wkrótce: W 2024 r. będziemy stopniowo wycofywać zgłoszenia z serwisu GitHub jako mechanizm przesyłania opinii na temat zawartości i zastępować go nowym systemem opinii. Aby uzyskać więcej informacji, sprawdź:Prześlij i wyświetl opinię dla