Sdílet prostřednictvím


Vytvoření nových uložených procedur prvků TableAdapter typových sad dat (VB)

od Scott Mitchell

Stáhnout PDF

V předchozích kurzech jsme vytvořili příkazy SQL v kódu a předali příkazy do databáze, která se má spustit. Alternativním přístupem je použití uložených procedur, kdy jsou příkazy SQL předdefinované v databázi. V tomto kurzu se naučíme, jak může průvodce TableAdapter pro nás generovat nové uložené procedury.

Úvod

Vrstva přístupu k datům (DAL) pro tyto kurzy používá typy datových sad. Jak je popsáno v kurzu Vytvoření vrstvy přístupu k datům , typové datové sady se skládají z datových tabulek a objektů TableAdapter se silnými typy. Tabulky DataTables představují logické entity v systému, zatímco TableAdapters interagují s podkladovou databází pro provádění přístupu k datům. To zahrnuje naplnění datových tabulek dat daty, spouštění dotazů, které vracejí skalární data, a vkládání, aktualizaci a odstraňování záznamů z databáze.

Příkazy SQL spouštěné sadou TableAdapters můžou být ad hoc příkazy SQL, jako je SELECT columnList FROM TableName, nebo uložené procedury. TableAdaptery v naší architektuře používají ad hoc příkazy SQL. Mnoho vývojářů a správců databází ale upřednostňuje uložené procedury před ad hoc příkazy SQL z důvodů zabezpečení, udržovatelnosti a aktualizovatelnosti. Jiní dávají přednost ad hoc příkazům SQL pro jejich flexibilitu. Ve své vlastní práci dávám přednost uloženým procedurám před ad hoc příkazy SQL, ale rozhodl jsem se použít ad hoc příkazy SQL ke zjednodušení předchozích kurzů.

Když definujete Objekt TableAdapter nebo přidáváte nové metody, průvodce TableAdapter s usnadňuje vytváření nových uložených procedur nebo použití existujících uložených procedur, stejně jako při použití příkazů SQL ad hoc. V tomto tutoriálu se podíváme, jak průvodce TableAdapter automaticky vygeneruje uložené procedury. V dalším kurzu se podíváme na to, jak nakonfigurovat metody TableAdapter s tak, aby používaly existující nebo ručně vytvořené uložené procedury.

Poznámka:

Podívejte se na blogový příspěvek Roba Howarda Don t Use Stored Procedures Yet? a blogový příspěvek Franse BoumyStored Procedures are Bad, M Kay? pro živou debatu o výhodách a nevýhodách uložených procedur a ad-hoc SQL.

Základy uložených procedur

Funkce jsou konstruktorem, který je společný pro všechny programovací jazyky. Funkce je kolekce příkazů, které se spouští při zavolání funkce. Funkce můžou přijímat vstupní parametry a volitelně vracet hodnotu. Uložené procedury jsou databázové konstrukce, které sdílejí mnoho podobností s funkcemi v programovacích jazycích. Uložená procedura se skládá ze sady příkazů T-SQL, které se spouští při volání uložené procedury. Uložená procedura může přijímat nula až mnoho vstupních parametrů a může vracet skalární hodnoty, výstupní parametry nebo nejčastěji sady výsledků z dotazů SELECT.

Poznámka:

Uložené procedury jsou často označovány jako „sprocs“ nebo „SPs“.

Uložené procedury se vytvářejí pomocí CREATE PROCEDURE příkazu T-SQL. Například následující skript T-SQL vytvoří uloženou proceduru s názvem GetProductsByCategoryID, která přijímá jeden parametr s názvem @CategoryID a vrátí pole ProductID, ProductName, UnitPrice a Discontinued z těchto sloupců v tabulce Products, které mají odpovídající hodnotu CategoryID.

CREATE PROCEDURE GetProductsByCategoryID
(
    @CategoryID int
)
AS
SELECT ProductID, ProductName, UnitPrice, Discontinued
FROM Products
WHERE CategoryID = @CategoryID

Po vytvoření této uložené procedury ji můžete volat pomocí následující syntaxe:

EXEC GetProductsByCategory categoryID

Poznámka:

V dalším kurzu prozkoumáme vytváření uložených procedur prostřednictvím integrovaného vývojového prostředí sady Visual Studio. V tomto kurzu ale necháme průvodce TableAdapter automaticky generovat uložené procedury pro nás.

Kromě pouhého vrácení dat se uložené procedury často používají k provádění více databázových příkazů v rámci jedné transakce. Uložená procedura s názvem DeleteCategory, například, může vzít parametr @CategoryID a provést dva DELETE příkazy: první, jeden odstranit související produkty a druhý odstranit zadanou kategorii. Více příkazů v rámci uložené procedury nejsou automaticky zabaleny v rámci transakce. Další příkazy T-SQL je potřeba vydat, aby se zajistilo, že více příkazů v uložené proceduře je považováno za jednu atomickou operaci. V následujícím kurzu se naučíme, jak zahrnout příkazy uložené procedury do transakce.

Při použití uložených procedur v architektuře metody vrstvy přístupu k datům vyvolávají konkrétní uloženou proceduru místo vydání příkazu AD hoc SQL. Tím se centralizuje umístění příkazů SQL spuštěných (v databázi) místo toho, aby bylo definováno v architektuře aplikace. Tato centralizace pravděpodobně usnadňuje hledání, analýzu a ladění dotazů a poskytuje mnohem jasnější obrázek o tom, kde a jak se databáze používá.

Další informace o základech uložených procedur najdete v části Dodatečné informace na konci tohoto tutoriálu.

Krok 1: Vytvoření scénáře webových stránek.

Než začneme s diskuzí o vytváření DAL pomocí uložených procedur, věnujme nejprve chvíli vytvoření ASP.NET stránek v našem webovém projektu, které budeme potřebovat pro tento a několik dalších výukových programů. Začněte přidáním nové složky s názvem AdvancedDAL. Dále do této složky přidejte následující ASP.NET stránky a nezapomeňte přidružit každou stránku ke stránce předlohy Site.master :

  • Default.aspx
  • NewSprocs.aspx
  • ExistingSprocs.aspx
  • JOINs.aspx
  • AddingColumns.aspx
  • ComputedColumns.aspx
  • EncryptingConfigSections.aspx
  • ManagedFunctionsAndSprocs.aspx

Přidejte stránky ASP.NET pro scénáře pokročilé vrstvy přístupu k datům

Obrázek 1: Přidat stránky ASP.NET pro scénáře pokročilé vrstvy přístupu k datům do tutoriálů

Stejně jako v ostatních složkách Default.aspx se ve AdvancedDAL složce zobrazí seznam kurzů v jeho části. Vzpomeňte si, že SectionLevelTutorialListing.ascx uživatelské řízení tuto funkci poskytuje. Tento uživatelský ovládací prvek přidejte do Default.aspx přetažením z Průzkumníka řešení do návrhového zobrazení stránky.

Přidání uživatelského ovládacího prvku SectionLevelTutorialListing.ascx do Default.aspx

Obrázek 2: Přidání SectionLevelTutorialListing.ascx uživatelského ovládacího prvku do Default.aspx (kliknutím zobrazíte obrázek v plné velikosti)

Nakonec přidejte tyto stránky jako položky do Web.sitemap souboru. Konkrétně přidejte následující značení po práci s dávkovými daty <siteMapNode>:

<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 aktualizaci Web.sitemapsi chvíli prohlédněte web kurzů prostřednictvím prohlížeče. Nabídka vlevo teď obsahuje položky pro pokročilé kurzy scénářů DAL.

Mapa webu teď obsahuje položky pro kurzy pokročilých scénářů DAL.

Obrázek 3: Mapa webu teď obsahuje položky pro kurzy pokročilých scénářů DAL

Krok 2: Konfigurace prvku TableAdapter pro vytvoření nových uložených procedur

Abychom si ukázali vytvoření vrstvy přístupu k datům, která místo ad hoc příkazů SQL používá uložené procedury, vytvoříme ve složce pojmenovanou NorthwindWithSprocs.xsdnovou typovou datovou sadu~/App_Code/DAL. Vzhledem k tomu, že jsme tento proces podrobně prošli v předchozích kurzech, budeme rychle procházet zde uvedené kroky. Pokud se zablokujete nebo potřebujete další podrobné pokyny k vytvoření a konfiguraci typové datové sady, projděte si kurz Vytvoření vrstvy přístupu k datům .

Přidejte do projektu novou datovou sadu tak, že kliknete pravým tlačítkem myši na DAL složku, zvolíte Přidat novou položku a vyberete šablonu Datové sady, jak je znázorněno na obrázku 4.

Přidání nové typové datové sady do projektu s názvem NorthwindWithSprocs.xsd

Obrázek 4: Přidání nové typové datové sady do projektu s názvem NorthwindWithSprocs.xsd (kliknutím zobrazíte obrázek s plnou velikostí)

Tím vytvoříte novou typovou datovou sadu, otevřete její návrháře, vytvoříte nový TableAdapter a spustíte průvodce konfigurací TableAdapter. Průvodce konfigurací TableAdapter s prvním krokem nás požádá, abychom vybrali databázi, se kterými chcete pracovat. Připojovací řetězec k databázi Northwind by měl být uvedený v rozevíracím seznamu. Vyberte tuto možnost a klikněte na Další.

Na této další obrazovce můžeme zvolit, jak má Objekt TableAdapter přistupovat k databázi. V předchozích kurzech jsme vybrali první možnost– Použít příkazy SQL. Pro účely tohoto kurzu vyberte druhou možnost, Vytvořte nové uložené procedury a klikněte na Další.

Instruujte TableAdapter, aby vytvořil nové uložené procedury.

Obrázek 5: Pokyn k vytvoření nových uložených procedur v sadě TableAdapter (kliknutím zobrazíte obrázek s plnou velikostí)

Stejně jako při použití ad hoc příkazů SQL se v následujícím kroku zobrazí výzva k zadání SELECT příkazu pro hlavní dotaz TableAdapter. Místo použití SELECT příkazu, který zde zadáte k přímému provedení ad hoc dotazu, průvodce TableAdapter s vytvoří uloženou proceduru, která obsahuje tento SELECT dotaz.

Pro tento parametr TableAdapter použijte následující SELECT dotaz:

SELECT ProductID, ProductName, SupplierID, CategoryID, 
       QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, 
       ReorderLevel, Discontinued
FROM Products

Zadejte dotaz SELECT.

Obrázek 6: Zadejte SELECT dotaz (kliknutím zobrazíte obrázek s plnou velikostí)

Poznámka:

Výše uvedený dotaz se mírně liší od hlavního ProductsTableAdapter dotazu v Northwind typové datové sadě. Vzpomeňte si, že ProductsTableAdapter v Northwind zadané datové sadě jsou dvě korelované poddotazy, které vrátí název kategorie a název společnosti pro každou kategorii a dodavatele produktů. V nadcházejícím tutoriálu o aktualizaci TableAdapteru na použití JOINů se podíváme na přidání těchto souvisejících dat do tohoto TableAdapteru.

Chvíli klikněte na tlačítko Upřesnit možnosti. Odsud můžeme určit, jestli má průvodce také generovat příkazy insert, update a delete pro TableAdapter, zda použít optimistickou souběžnost a zda by se měla tabulka dat aktualizovat po vložení a aktualizacích. Ve výchozím nastavení je zaškrtnuto políčko Generovat příkazy Insert, Update a Delete. Nechte ho zaškrtnuté. Pro tento návod nechte možnosti Použít optimistickou souběžnost nezaškrtnuté.

Při automatickém vytvoření uložených procedur průvodcem TableAdapter se zobrazí, že je možnost Aktualizovat tabulku dat ignorována. Bez ohledu na to, jestli je toto políčko zaškrtnuté, výsledné vložené a aktualizované procedury načtou právě vložený nebo právě aktualizovaný záznam, jak uvidíme v kroku 3.

Možnost Vygenerovat příkazy Insert, Update a Delete ponechte zaškrtnuté.

Obrázek 7: Ponechte zaškrtnutou možnost vygenerovat příkazy Vložit, Aktualizovat a Odstranit

Poznámka:

Pokud je zaškrtnuto políčko Použít optimistickou souběžnost, průvodce přidá do WHERE klauzule další podmínky, které brání aktualizaci dat, pokud došlo ke změnám v jiných polích. Další informace o použití integrované funkce řízení optimistické souběžnosti TableAdapteru najdete v tutoriálu Implementace optimistické souběžnosti.

Po zadání dotazu a potvrzení, že je zaškrtnuto SELECT políčko Generovat příkazy Vložit, Aktualizovat a Odstranit, klepněte na tlačítko Další. Tato další obrazovka zobrazená na obrázku 8 zobrazí výzvu k zadání názvů uložených procedur, které průvodce vytvoří pro výběr, vložení, aktualizaci a odstranění dat. Změňte názvy uložených procedur na Products_Select, Products_Insert, Products_Updatea Products_Delete.

Přejmenování uložených procedur

Obrázek 8: Přejmenování uložených procedur (kliknutím zobrazíte obrázek s plnou velikostí)

Chcete-li zobrazit T-SQL, který průvodce TableAdapter použije k vytvoření čtyř uložených procedur, klikněte na tlačítko Náhled skriptu SQL. V dialogovém okně Náhled skriptu SQL můžete skript uložit do souboru nebo ho zkopírovat do schránky.

Náhled na skript SQL použitý k vytvoření uložených procedur

Obrázek 9: Náhled skriptu SQL použitého k vygenerování uložených procedur

Po pojmenování uložených procedur klepněte na tlačítko Další, abyste pojmenovali metody odpovídajícího objektu TableAdapter. Stejně jako při použití ad hoc příkazů SQL můžeme vytvořit metody, které vyplní existující tabulku DataTable nebo vrátí novou. Můžeme také určit, jestli má model TableAdapter obsahovat DB-Direct vzor pro vkládání, aktualizaci a odstraňování záznamů. Nechejte zaškrtnutá všechna tři políčka, ale přejmenujte metodu Return a DataTable na GetProducts (jak je znázorněno na obrázku 10).

Pojmenujte metody Fill a GetProducts.

Obrázek 10: Pojmenování metod Fill a GetProducts (kliknutím zobrazíte obrázek s plnou velikostí)

Kliknutím na tlačítko Další zobrazíte souhrn kroků, které průvodce provede. Dokončete průvodce kliknutím na tlačítko Dokončit. Po dokončení průvodce se vrátíte do Návrháře sady dat, která by teď měla obsahovat ProductsDataTable.

Návrhář datové sady zobrazuje nově přidanou tabulku ProductsDataTable.

Obrázek 11: Návrhář datové sady zobrazuje nově přidaný ProductsDataTable obrázek (kliknutím zobrazíte obrázek s plnou velikostí).

Krok 3: Zkoumání nově vytvořených uložených procedur

Průvodce TableAdapter použitý v kroku 2 automaticky vytvořil uložené procedury pro výběr, vkládání, aktualizaci a odstraňování dat. Tyto uložené procedury je možné zobrazit nebo upravit v sadě Visual Studio tak, že přejdete do Průzkumníka serveru a přejdete do složky Uložené procedury databáze. Jak ukazuje obrázek 12, databáze Northwind obsahuje čtyři nové uložené procedury: Products_Delete, Products_Insert, Products_Selecta Products_Update.

Čtyři uložené procedury vytvořené v kroku 2 najdete ve složce Uložené procedury databáze.

Obrázek 12: Čtyři uložené procedury vytvořené v kroku 2 najdete ve složce Uložené procedury databáze.

Poznámka:

Pokud průzkumníka serveru nevidíte, přejděte do nabídky Zobrazení a zvolte možnost Průzkumník serveru. Pokud nevidíte uložené procedury související s produktem přidané z kroku 2, zkuste kliknout pravým tlačítkem myši na složku Uložené procedury a zvolit Aktualizovat.

Pokud chcete zobrazit nebo upravit uloženou proceduru, poklikejte na jeho název v Průzkumníku serveru nebo případně klikněte pravým tlačítkem myši na uloženou proceduru a zvolte Otevřít. Obrázek 13 znázorňuje uloženou proceduru Products_Delete, když je otevřená.

Uložené procedury je možné otevřít a upravit v sadě Visual Studio.

Obrázek 13: Uložené procedury je možné otevřít a upravit v sadě Visual Studio (kliknutím zobrazíte obrázek s plnou velikostí).

Obsah uložených Products_DeleteProducts_Select procedur je poměrně jednoduchý. Uložené procedury Products_Insert a Products_Update naproti tomu vyžadují bližší kontrolu, protože obě provádějí SELECT příkaz po svých INSERT a UPDATE příkazech. Například následující SQL tvoří uloženou proceduru Products_Insert :

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())

Uložená procedura přijímá jako vstupní parametry Products sloupce vrácené dotazem SELECT zadaným v průvodci TableAdapter s a tyto hodnoty se používají v INSERT příkazu. Po příkazu INSERT se dotaz SELECT použije k vrácení Products hodnot sloupců (včetně ProductID) nově přidaného záznamu. Tato funkce aktualizace je užitečná při přidávání nového záznamu pomocí vzoru Dávkové aktualizace, protože automaticky aktualizuje nově přidané ProductRow vlastnosti instancí ProductID s automaticky navyšovanými hodnotami přiřazenými databází.

Tuto funkci ilustruje následující kód. Obsahuje ProductsTableAdapter a ProductsDataTable vytvořený pro typovou datovou sadu NorthwindWithSprocs. Do databáze se přidá nový produkt vytvořením ProductsRow instance, zadáním jeho hodnot a voláním Metody TableAdapter s Update předáním ProductsDataTable. Interně metoda TableAdapter s Update vyčísluje ProductsRow instance v předané DataTable (v tomto příkladu existuje pouze jeden - ten, který jsme právě přidali) a provede příslušný příkaz insert, update nebo delete. V tomto případě Products_Insert se spustí uložená procedura, která do tabulky přidá nový záznam Products a vrátí podrobnosti nově přidaného záznamu. Hodnota ProductsRow instance ProductID se pak aktualizuje. Po dokončení metody Update můžeme prostřednictvím vlastnosti ProductsRowProductID získat přístup k hodnotě nově přidaného záznamu 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

Uložená procedura Products_Update obsahuje podobně SELECT příkaz za UPDATE příkazem.

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)

Všimněte si, že tato uložená procedura obsahuje dva vstupní parametry pro ProductID: @Original_ProductID a @ProductID. Tato funkce umožňuje scénáře, ve kterých se může změnit primární klíč. Například v databázi zaměstnanců může každý záznam zaměstnance jako primární klíč použít číslo sociálního pojištění zaměstnance. Aby bylo možné změnit číslo sociálního pojištění stávajícího zaměstnance, musí být zadáno nové číslo sociálního pojištění i původní. Products U tabulky není taková funkce nutná, protože ProductID sloupec je IDENTITY sloupec a nikdy by se neměl měnit. Ve skutečnosti UPDATE příkaz v uložené proceduře Products_Update neobsahuje sloupec ProductID ve svém seznamu sloupců. @Original_ProductID Zatímco se tedy používá v klauzuli s WHERE příkazu UPDATE, je nadbytečný pro tabulku Products a může být nahrazen parametrem @ProductID. Při úpravě parametrů uložených procedur je důležité aktualizovat také metody TableAdapter, které používají uloženou proceduru.

Krok 4: Úprava parametrů uložených procedur a aktualizace objektu TableAdapter

Vzhledem k tomu, že @Original_ProductID je parametr nadbytečný, pojďme ho Products_Update úplně odebrat z uložené procedury. Otevřete uloženou proceduru Products_Update@Original_ProductID , odstraňte parametr a v WHERE klauzuli UPDATE příkazu změňte název parametru použitý z @Original_ProductID na @ProductID. Po provedení těchto změn by T-SQL v rámci uložené procedury měl vypadat takto:

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)

Pokud chcete tyto změny uložit do databáze, klikněte na ikonu Uložit na panelu nástrojů nebo stiskněte Ctrl+S. V tomto okamžiku Products_Update uložená procedura neočekává @Original_ProductID vstupní parametr, ale TableAdapter je nakonfigurován tak, aby předal takový parametr. Parametry, které TableAdapter odešle do Products_Update uložené procedury, můžete zobrazit tak, že v Návrháři datové sady vyberete TableAdapter, přejdete do okna Vlastnosti a kliknete na tlačítko s třemi tečkami v kolekci UpdateCommand s Parameters. Zobrazí se dialogové okno Editor kolekcí parametrů zobrazené na obrázku 14.

Editor kolekce parametrů uvádí parametry použité při volání uložené procedury Products_Update.

Obrázek 14: Editor kolekce parametrů uvádí parametry, které se předají uložené proceduře Products_Update

Tento parametr odsud můžete odebrat jednoduše tak @Original_ProductID , že vyberete parametr ze seznamu členů a kliknete na tlačítko Odebrat.

Případně můžete aktualizovat parametry použité pro všechny metody tak, že v Návrháři kliknete pravým tlačítkem myši na TableAdapter a zvolíte Konfigurovat. Tím se zobrazí průvodce konfigurací TableAdapter, který obsahuje uložené procedury používané k výběru, vkládání, aktualizaci a odstraňování, spolu s parametry, které uložené procedury očekávají. Pokud kliknete na rozevírací seznam Aktualizovat, uvidíte očekávané vstupní parametry uložených procedur Products_Update, které již nezahrnují @Original_ProductID (viz obrázek 15). Kliknutím na tlačítko Dokončit automaticky aktualizujte kolekci parametrů používanou objektem TableAdapter.

Případně můžete použít Průvodce konfigurací TableAdapter k aktualizaci kolekcí parametrů metod.

Obrázek 15: Případně můžete pomocí Průvodce konfigurací TableAdapter aktualizovat kolekce parametrů metod (kliknutím zobrazíte obrázek v plné velikosti).

Krok 5: Přidání dalších metod TableAdapter

Jak je znázorněno v kroku 2, při vytváření nového objektu TableAdapter je snadné mít odpovídající uložené procedury automaticky vygenerovány. Totéž platí při přidávání dalších metod do objektu TableAdapter. Abychom to mohli ilustrovat, pojďme přidat metodu do vytvořeného GetProductByProductID(productID)ProductsTableAdapter v kroku 2. Tato metoda převezme jako vstup ProductID hodnotu a vrátí podrobnosti o zadaném produktu.

Začněte tak, že kliknete pravým tlačítkem na objekt TableAdapter a v místní nabídce zvolíte Přidat dotaz.

Přidání nového dotazu do prvku TableAdapter

Obrázek 16: Přidání nového dotazu do prvku TableAdapter

Tím se spustí Průvodce konfigurací dotazu TableAdapter, který nejprve vyzve, jak má TableAdapter přistupovat k databázi. Chcete-li vytvořit novou uloženou proceduru, zvolte možnost Vytvořit novou uloženou proceduru a klepněte na tlačítko Další.

Volba možnosti Vytvořit novou uloženou proceduru

Obrázek 17: Volba možnosti Vytvořit novou uloženou proceduru (Kliknutím zobrazíte obrázek s plnou velikostí)

Na další obrazovce je třeba identifikovat typ dotazu, který se má provést, ať už vrátí sadu řádků nebo jednu skalární hodnotu, nebo provede příkaz UPDATE, INSERT nebo DELETE. Vzhledem k tomu, že metoda GetProductByProductID(productID) vrátí řádek, ponechte možnost SELEKT, která vrací řádek, vybranou a stiskněte Další.

Výběr možnosti SELECT, která vrací možnost řádku

Obrázek 18: Zvolte výběr, který vrací možnost řádku (kliknutím zobrazíte obrázek s plnou velikostí)

Na další obrazovce se zobrazí hlavní dotaz TableAdapter, který zobrazí jenom název uložené procedury (dbo.Products_Select). Nahraďte název uložené procedury následujícím SELECT příkazem, který vrátí všechna pole produktu pro zadaný produkt:

SELECT ProductID, ProductName, SupplierID, CategoryID, 
       QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, 
       ReorderLevel, Discontinued
FROM Products
WHERE ProductID = @ProductID

Nahraďte název uložené procedury dotazem SELECT.

Obrázek 19: Nahrazení názvu uložené procedury dotazem SELECT (kliknutím zobrazíte obrázek s plnou velikostí)

Na následující obrazovce se zobrazí dotaz, jestli chcete pojmenovat uloženou proceduru, která se vytvoří. Zadejte název Products_SelectByProductID a klikněte na Další.

Pojmenujte novou uloženou proceduru Products_SelectByProductID

Obrázek 20: Pojmenování nové uložené procedury Products_SelectByProductID (kliknutím zobrazíte obrázek v plné velikosti)

Poslední krok průvodce nám umožňuje změnit názvy vygenerovaných metod a také indikovat, jestli se má použít vzor Fill a DataTable, Return a DataTable nebo obojí. Pro tuto metodu ponechte obě možnosti zaškrtnuté, ale přejmenujte metody na FillByProductID a GetProductByProductID. Kliknutím na tlačítko Další zobrazíte souhrn kroků, které průvodce provede, a potom kliknutím na tlačítko Dokončit průvodce dokončete.

Přejmenování metod TableAdapter na FillByProductID a GetProductByProductID

Obrázek 21: Přejmenování metod TableAdapter s na FillByProductID a GetProductByProductID (kliknutím zobrazíte obrázek s plnou velikostí)

Po dokončení průvodce má TableAdapter k dispozici novou metodu, GetProductByProductID(productID) která při vyvolání spustí uloženou Products_SelectByProductID proceduru, která byla právě vytvořena. Chvíli si prohlédněte tuto novou uloženou proceduru z Průzkumníka serveru tak, že přejdete do složky Uložené procedury a otevřete Products_SelectByProductID ji (pokud ji nevidíte, klikněte pravým tlačítkem myši na složku Uložené procedury a zvolte Aktualizovat).

Všimněte si, že uložená SelectByProductID procedura přebírá @ProductID jako vstupní parametr a spouští SELECT příkaz, který jsme zadali v průvodci.

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: Vytvoření třídy vrstvy obchodní logiky

V rámci série kurzů jsme se snažili zachovat vrstvenou architekturu, ve které prezentační vrstva provedla všechna svá volání do vrstvy obchodní logiky (BLL). Abychom mohli toto rozhodnutí o návrhu dodržovat, musíme nejprve vytvořit třídu BLL pro novou typovou datovou sadu, než budeme mít přístup k datům produktu z prezentační vrstvy.

Ve složce ~/App_Code/BLL vytvořte nový soubor třídy ProductsBLLWithSprocs.vb a přidejte do něj následující kód:

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

Tato třída napodobuje sémantiku třídy z předchozích kurzů, ale používá objekty ProductsTableAdapter a ProductsDataTable z NorthwindWithSprocs DataSet. Například, místo toho, aby byl příkaz na začátku souboru třídy, jako je tomu u Imports NorthwindTableAdapters, třída ProductsBLLWithSprocs používá Imports NorthwindWithSprocsTableAdapters. Podobně jsou objekty ProductsDataTable a ProductsRow použité v této třídě označeny předponou NorthwindWithSprocs oboru názvů. Třída ProductsBLLWithSprocs poskytuje dvě metody přístupu k datům GetProducts a GetProductByProductIDa metody pro přidání, aktualizaci a odstranění jedné instance produktu.

Krok 7: Práce s datovouNorthwindWithSprocssadou z prezentační vrstvy

V tuto chvíli jsme vytvořili DAL, který používá uložené procedury pro přístup k podkladovým datům databáze a jejich úpravu. Také jsme vytvořili základní BLL s metodami pro načtení všech produktů nebo konkrétního produktu spolu s metodami pro přidávání, aktualizaci a odstraňování produktů. Abychom tento kurz zaokrouhlili, vytvoříme ASP.NET stránku, která používá třídu BLL s ProductsBLLWithSprocs k zobrazení, aktualizaci a odstraňování záznamů.

Otevřete stránku ve AdvancedDAL složce a přetáhněte GridView z panelu nástrojů do návrháře a přiřadíme ji název Products. Z inteligentní značky GridView zvolte vytvořit vazbu na nový ObjectDataSource s názvem ProductsDataSource. Nakonfigurujte ObjectDataSource tak, aby používal ProductsBLLWithSprocs třídu, jak je znázorněno na obrázku 22.

Konfigurujte ObjectDataSource pro použití třídy ProductsBLLWithSprocs

Obrázek 22: Konfigurace ObjectDataSource pro použití ProductsBLLWithSprocs třídy (kliknutím zobrazíte obrázek s plnou velikostí)

Rozevírací seznam na kartě SELECT má dvě možnosti GetProducts a GetProductByProductID. Vzhledem k tomu, že chceme zobrazit všechny produkty v GridView, zvolte metodu GetProducts . Rozevírací seznamy na kartách UPDATE, INSERT a DELETE mají pouze jednu metodu. Ujistěte se, že každý z těchto rozevíracích seznamů má vybranou příslušnou metodu, a klikněte na tlačítko Dokončit.

Po dokončení průvodce ObjectDataSource Visual Studio přidá BoundFields a CheckBoxField do GridView pro datová pole produktů. Zapněte integrovanou úpravu a odstraňování funkcí GridView tak, že zaškrtnete možnosti Povolit úpravy a Povolit odstranění, které jsou přítomné na inteligentní značce.

Stránka obsahuje Objekt GridView s povolenou podporou úprav a odstraňování.

Obrázek 23: Stránka obsahuje objekt GridView s povolenou podporou úprav a odstraňování (kliknutím zobrazíte obrázek v plné velikosti).

Jak jsme probrali v předchozích kurzech, při dokončení průvodce pro ObjectDataSource Visual Studio nastaví vlastnost OldValuesParameterFormatString na původní hodnotu original_{0}. To je potřeba vrátit k výchozí hodnotě {0} , aby funkce úprav dat fungovaly správně vzhledem k parametrům očekávaným metodami v naší BLL. Proto nezapomeňte nastavit vlastnost OldValuesParameterFormatString na {0} nebo zcela odebrat vlastnost z deklarativní syntaxe.

Po dokončení Průvodce konfigurací zdroje dat, zapnutí podpory úprav a odstranění v GridView a vrácení vlastnosti ObjectDataSource OldValuesParameterFormatString na výchozí hodnotu by deklarativní značka vaší stránky měla vypadat podobně jako následující:

<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>

V tuto chvíli bychom mohli GridView upravit přizpůsobením rozhraní pro úpravy tak, aby zahrnovalo ověření, přičemž sloupce CategoryID a SupplierID by se vykreslovaly jako rozbalovací seznamy atd. Můžeme také přidat potvrzení na straně klienta na tlačítko Odstranit, a doporučuji vám, abyste si vzali čas na implementaci těchto vylepšení. Vzhledem k tomu, že tato témata jsou popsána v předchozích kurzech, nebudeme je zde znovu pokrývat.

Bez ohledu na to, jestli gridview vylepšíte nebo ne, otestujte základní funkce stránky v prohlížeči. Jak je znázorněno na obrázku 24, stránka obsahuje seznam produktů v objektu GridView, který poskytuje možnosti úprav a odstraňování jednotlivých řádků.

Produkty lze zobrazit, upravit a odstranit z GridView.

Obrázek 24: Produkty lze zobrazit, upravit a odstranit z Objektu GridView (kliknutím zobrazíte obrázek v plné velikosti).

Shrnutí

Objekty TableAdapter v typové datové sadě mají přístup k datům z databáze pomocí příkazů SQL ad hoc nebo prostřednictvím uložených procedur. Při práci s uloženými procedurami je možné použít existující uložené procedury nebo průvodce TableAdapter lze instruovat k vytvoření nových uložených procedur na SELECT základě dotazu. V tomto kurzu jsme prozkoumali, jak pro nás uložené procedury automaticky vytvořit.

Zatímco automatické generování uložených procedur pomáhá ušetřit čas, existují určité případy, kdy uložená procedura vytvořená průvodcem neodpovídá tomu, co bychom vytvořili sami. Jedním z příkladů je uložená Products_Update procedura, která očekávala oba @Original_ProductID i @ProductID vstupní parametry, i když @Original_ProductID byl parametr nadbytečný.

V mnoha scénářích už můžou být uložené procedury vytvořené nebo je můžeme chtít sestavit ručně, aby měly lepší kontrolu nad příkazy uložených procedur. V obou případech bychom chtěli dát objektu TableAdapter pokyn, aby pro své metody používal existující uložené procedury. V dalším kurzu se dozvíme, jak toho dosáhnout.

Šťastné programování!

Další čtení

Další informace o tématech probíraných v tomto kurzu najdete v následujících zdrojích informací:

O autorovi

Scott Mitchell, autor sedmi knih ASP/ASP.NET a zakladatel 4GuysFromRolla.com, pracuje s webovými technologiemi Microsoftu od roku 1998. Scott pracuje jako nezávislý konzultant, trenér a spisovatel. Jeho nejnovější kniha je Sams Naučte se sami ASP.NET 2.0 během 24 hodin. Může být dosažitelný na mitchell@4GuysFromRolla.comadrese .

Zvláštní díky

Tato série kurzů byla zkontrolována mnoha užitečnými recenzenty. Vedoucím recenzentem pro tento tutoriál byl Hilton Geisenow. Chcete si projít nadcházející články MSDN? Pokud ano, napište mi zprávu na mitchell@4GuysFromRolla.com.