Pokročilá práce s daty v ASP.NET 2.0

6. díl - Pokročilá práce s daty v ASP.NET 2.0

Tomáš Herceg       8. 12. 2007       C#, VB.NET, ASP.NET WebForms, HTTP/HTML, JavaScript       10906 zobrazení

V tomto díle si ukážeme, jak přidávat, upravovat a mazat záznamy v databázové tabulce. Představíme si také použití komponent DropDownList, RadioButtonList a detailně se budeme věnovat komponentě FormView a jejím důležitým vlastnostem. Jako ukázkový příklad napíšeme aplikaci pro evidenci hudebních CD.

V minulém díle jsme se naučili přidávat záznamy do databáze a použili jsme k tomuto účelu komponentu FormView. Již minule jsem naznačil, že tato komponenta umí záznamy i upravovat a mazat, takže se to dnes naučíme. Napíšeme si jednoduchou webovou aplikaci, která bude sloužit k evidenci hudebních CD. Abychom se moc nezdržovali, připravil jsem vám již databázi, kterou byste si měli stáhnout. Vytvořte si novou webovou aplikaci a rozbalte staženou databázi do složky App_Data.

Něco málo o konfiguraci ASP.NET aplikací

Abychom mohli s databází pracovat, musíme ji také zaregistrovat do konfiguračního souboru web.config. V minulých dílech jsme to udělali pomocí klikacího průvodce, ale není na škodu umět to i ručně. Otevřete tedy ve Visual Studiu soubor web.config a najděte řádek <connectionStrings />. Nahraďte jej tímto kódem:

    <connectionStrings>
<
add name="ConnectionString" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\CdCatalog.mdf;Integrated Security=True;User Instance=True"
providerName="System.Data.SqlClient" />
</
connectionStrings>

Do sekce connectionStrings jsme tedy přidali nový connectionString s názvem ConnectionString. atribut connectionString obsahuje cestu k SQL serveru, cestu k souboru s databází a informace o tom, jak se máme k souboru přihlašovat. Vlastnost providerName říká, která třída se má použít pro práci s databází.

Připomínám, že v konfiguračním souboru aplikace se uchovávají všechna možná i nemožná nastavení. Výhodou je, že jeho struktura je poměrně snadno rozšiřitelná, a díky tomu si do něj můžeme přidat vlastní sekce, ve kterých budeme konfigurovat naše vlastní součásti aplikace, třeba nějaké vlastní komponenty atd. Nyní jsme do něj přidali informace o tom, kde je databáze a jak se k ní máme dostat. Doplňuji, že soubor web.config je zvenčí nedostupný, takže do něj můžete klidně ukládat hesla k databázím, žádný uživatel zvenku se k nim nemůže dostat (pokud je tedy server dobře zabezpečený).

Vytvoření MasterPage a stránky s přehledem alb

Z projektu smažte soubor Default.aspx a přidejte do něj novou položku MasterPage.master. Při přidávání odškrtněte políčko Place code in separate file, aby se nám zbytečně nevytvářely dva soubory. Pravým tlačítkem v podokně Solution Explorer na naši MasterPage klikněte a vyberte položku Add Content Page, aby se nám znovu vytvořila stránka Default.aspx a zaintegrovala se nám do naší MasterPage.

Přepněte se do nové stránky Default.aspx a přidejte do ní komponenty SqlDataSource a GridView. V režimu Design rozbalte podokno úloh komponenty SqlDataSource (je to taková ta malinká šipečka v horním pravém rohu) a vyberte možnost Configure Data Source. V dialogu vyberte náš ConnectionString, který jsme ručně přidali do konfigurace, pro sestavení dotazu zaškrtněte v tabulce Albums hvězdičku, aby se vybíraly všechny sloupce.

Nastavení SQL dotazu

Potvrďte další kroky průvodce a po jeho skončení již budeme mít SqlDataSource nastaven.

V databázi máme tři tabulky - Albums obsahuje seznam alb, Genres obsahuje seznam žánrů hudby a Songs obsahuje skladby pro každé album. U každého alba uchováváme jeho název (Title), autora hudby (Author), interpreta (Interpret), rok vydání (Year), datum pořízení alba (DateBought), hodnocení (Rating) a žánr (GenreId), což je cizí klíč do tabulky žánrů. Rozbalte tedy podokno úloh komponenty GridView a vyberte SqlDataSource1 v rozbalovacím seznamu. Samy se nám vygenerovaly sloupce, nejjednodušší bude upravit je v okně kódu než v klikacím okénku.

Přepněte se teď do režimu Source a upravte hodnotu vlastnosti SelectCommand komponenty SqlDataSource1, nastavte jí jako hodnotu tento SQL příkaz:

SELECT [Albums].[AlbumId], [Albums].[Title] AS [Title], [Author], [Interpret], [Year], [DateBought], [Rating], [Genres].[Title] AS [Genre] FROM [Albums] LEFT JOIN [Genres] ON [Albums].[GenreId] = [Genres].[GenreId]

Protože chceme zobrazit název žánru pro každý zobrazený disk a tento název je uložen v jiné tabulce, spojil jsem v našem SQL dotazu příkazem LEFT JOIN tabulky Genres a Albums. Název žánru najdeme ve virtuálním sloupci Genre vytvořeném v dotazu. Pokud příkaz JOIN neznáte, přečtěte si článek Úvod do jazyka SQL. Aby se data v komponentě GridView zobrazila nějak rozumně, definici komponenty GridView změňte takto:

    <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="AlbumId" DataSourceID="SqlDataSource1" Width="800px">
<Columns>
<asp:HyperlinkField DataTextField="Title" DataNavigateUrlFields="AlbumId" DataNavigateUrlFormatString="~/AlbumDetail.aspx?id={0}" HeaderText="Název alba" SortExpression="Title" />
<asp:BoundField DataField="Author" HeaderText="Autor hudby" SortExpression="Author" />
<asp:BoundField DataField="Interpret" HeaderText="Interpret" SortExpression="Interpret" />
<asp:BoundField DataField="Genre" HeaderText="Žánr" SortExpression="GenreId" />
<asp:BoundField DataField="Rating" HeaderText="Hodnocení" SortExpression="Rating" />
</Columns>
</asp:GridView>

Z názvu alba uděláme odkaz na stránku AlbumDetail.aspx, kde zobrazíme detaily daného alba. ID alba předáme v QueryStringu (parametr v URL) v parametru id. Adresa tedy bude např. AlbumDetail.aspx?id=1. Tuto stránku napíšeme nyní a budeme ji používat i k editaci a přidávání nových alb. Pokud aplikaci spustíte, bude stránka Default.aspx vypadat zhruba takto:

 Seznam alb

Zobrazení detailů alba

Vytvořte novou stránku vybráním volby Add Content Page v kontextové nabídce souboru MasterPage.master a přejmenujte ji na AlbumDetail.aspx. Přidejte do ní komponentu SqlDataSource a dále pak komponentu FormView, která dovede pracovat s jedním záznamem. Spusťte opět průvodce pro konfiguraci SQL příkazu a okna nastavte podle obrázků:

Nastavení SQL dotazu

Dále klikněte na tlačítko WHERE, protože potřebujeme specifikovat v dotazu podmínku, aby se nám zobrazil jen jeden konkrétní požadovaný záznam, a to ten, který vytáhneme z parametru v URL.

Přidání podmínky pro parametr z URL

Vyplňte zakroužkovaná pole a klikněte na tlačítko Add. Pak okno potvrďte tlačítkem OK. Do dotazu se doplní podmínka WHERE [AlbumId] = @AlbumId a parametr @AlbumId v SQL dotazu se automaticky vytáhne z parametru v URL.

Ještě než budete pokračovat dále, klikněte na tlačítko Advanced a podle obrázku zaškrtněte volbu Generate INSERT, UPDATE and DELETE statements. Tím se nám automaticky vygenerují i příkazy pro přidávání, úpravy a mazání záznamů v tabulce Albums.

Pokročilé možnosti dotazu

Dokončete průvodce a naše komponenta SqlDataSource1 bude nastavena tak, jak potřebujeme. Když se podíváme na kód, který nám pro SqlDataSource1 průvodce vygeneroval, bude vypadat takto:

    <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:ConnectionString %>" 
SelectCommand="SELECT * FROM [Albums] WHERE ([AlbumId] = @AlbumId)"
DeleteCommand="DELETE FROM [Albums] WHERE [AlbumId] = @AlbumId"
InsertCommand="INSERT INTO [Albums] ([GenreId], [Title], [Author], [Interpret], [Year], [DateBought], [Rating]) VALUES (@GenreId, @Title, @Author, @Interpret, @Year, @DateBought, @Rating)"
UpdateCommand="UPDATE [Albums] SET [GenreId] = @GenreId, [Title] = @Title, [Author] = @Author, [Interpret] = @Interpret, [Year] = @Year, [DateBought] = @DateBought, [Rating] = @Rating WHERE [AlbumId] = @AlbumId">
<SelectParameters>
<asp:QueryStringParameter Name="AlbumId" QueryStringField="id" Type="Int32" />
</SelectParameters>
<DeleteParameters>
<asp:Parameter Name="AlbumId" Type="Int32" />
</DeleteParameters>
<UpdateParameters>
<asp:Parameter Name="GenreId" Type="Int32" />
<asp:Parameter Name="Title" Type="String" />
<asp:Parameter Name="Author" Type="String" />
<asp:Parameter Name="Interpret" Type="String" />
<asp:Parameter Name="Year" Type="Int32" />
<asp:Parameter Name="DateBought" Type="DateTime" />
<asp:Parameter Name="Rating" Type="Byte" />
<asp:Parameter Name="AlbumId" Type="Int32" />
</UpdateParameters>
<InsertParameters>
<asp:Parameter Name="GenreId" Type="Int32" />
<asp:Parameter Name="Title" Type="String" />
<asp:Parameter Name="Author" Type="String" />
<asp:Parameter Name="Interpret" Type="String" />
<asp:Parameter Name="Year" Type="Int32" />
<asp:Parameter Name="DateBought" Type="DateTime" />
<asp:Parameter Name="Rating" Type="Byte" />
</InsertParameters>
</asp:SqlDataSource>

Vidíme, že komponenta má nastaveny příslušné SQL příkazy ve vlastnostech SelectCommand, InsertCommand, DeleteCommand a UpdateCommand. Každý příkaz obsahuje několik příslušných parametrů, jejichž hodnoty se pošlou spolu s dotazem a dostanou se tak do příslušné tabulky. Každý parametr je včetně jeho tatového typu popsán v sekci SelectParameters, InsertParameters, DeleteParameters a UpdateParameters. Pokud je parametr deklarován značkou asp:QueryStringParameter, pak se jeho hodnota vezme z QueryStringu, tedy z parametru v URL dané stránky. Název parametru je specifikován vlastností QueryStringField. ASP.NET má i další druhy parametrů, které dokážou vytáhnout hodnotu z cookies, z nějakých komponent atd., můžete si napsat i vlastní typy parametrů. asp:Parameter je normální parametr, který nemá žádnou specifickou funkci, hodnoty těchto parametrů v sekcích InsertParameters, DeleteParameters a UpdateParameters nastaví automaticky komponenta FormView podle akce, kterou právě provádíme. Před vložením záznamu tedy FormView podle hodnot z formuláře vyplní parametry v sekci InsertParameters a pak zavolá na komponentě SqlDataSource příslušný INSERT příkaz. To samé platí i pro úpravy záznamů.

Zobrazení informací o albu

Protože opět budeme chtít vypsat název žánru místo jeho ID, opravte opět hodnotu vlastnosti SelectCommand v komponentě SqlDataSource1 na tuto hodnotu:

SELECT [Albums].[AlbumId], [Albums].[Title] AS [Title], [Albums].[GenreId], [Author], [Interpret], [Year], [DateBought], [Rating], [Genres].[Title] AS [Genre] FROM [Albums] LEFT JOIN [Genres] ON [Albums].[GenreId] = [Genres].[GenreId] WHERE ([AlbumId] = @AlbumId)

Nyní rozbalte podokno úloh komponenty FormView1 a vyberte v rozbalovacím seznamu komponentu SqlDataSource1. Komponenta FormView1 si automaticky zjistí strukturu tabulky podle předaného datového zdroje a podle toho vygeneruje své šablony pro zobrazení, editaci a přidávání záznamů. Tyto šablony si pochopitelně upravíme, aby vypadaly trochu k světu. Přepněte se do režimu Source a sekci ItemTemplate nahraďte tímto kódem:

        <ItemTemplate>
<h1><asp:Label ID="TitleLabel" runat="server" Text='<%# Eval("Title") %>'></asp:Label></h1>
<table>
<tr>
<td>Autor hudby: </td>
<td><asp:Label ID="AuthorLabel" runat="server" Text='<%# Eval("Author") %>' Font-Bold="True"></asp:Label></td>
</tr>
<tr>
<td>Interpret: </td>
<td><asp:Label ID="InterpretLabel" runat="server" Text='<%# Eval("Interpret") %>' Font-Bold="True"></asp:Label></td>
</tr>
<tr>
<td>Žánr: </td>
<td><asp:Label ID="GenreLabel" runat="server" Text='<%# Eval("Genre") %>' Font-Bold="True"></asp:Label></td>
</tr>
<tr>
<td>Rok vydání: </td>
<td><asp:Label ID="YearLabel" runat="server" Text='<%# Eval("Year") %>' Font-Bold="True"></asp:Label></td>
</tr>
<tr>
<td>Datum pořízení: </td>
<td><asp:Label ID="DateBoughtLabel" runat="server" Text='<%# Eval("DateBought", "{0:d. MMMM yyyy}") %>' Font-Bold="True"></asp:Label></td>
</tr>
<tr>
<td>Hodnocení: </td>
<td><asp:Label ID="RatingLabel" runat="server" Text='<%# Eval("Rating") %>' Font-Bold="True"></asp:Label></td>
</tr>
</table>

<asp:LinkButton ID="EditButton" runat="server" CausesValidation="False" CommandName="Edit" Text="[Upravit]"></asp:LinkButton>
<asp:LinkButton ID="DeleteButton" runat="server" CausesValidation="False" CommandName="Delete" Text="[Smazat]" OnClientClick="javascript: return confirm('Opravdu chcete toto album smazat?');"></asp:LinkButton>
<asp:LinkButton ID="NewButton" runat="server" CausesValidation="False" CommandName="New" Text="[Přidat]" PostBackUrl="~/AlbumDetail.aspx"></asp:LinkButton>
</ItemTemplate>

Na tomto kousku kódu by nám nemělo být téměř nic nového. Když vypisujeme datum pořízení, dali jsme jako druhý parametr příkazu Eval i formátovací řetězec d. MMMM yyyy, který zaručí, že se datum vypíše např. 12. listopadu 2007.

Na konci máme 3 tlačítka typu LinkButton. Důležitou jejich vlastností je CommandName - podle její hodnoty komponenta FormView pozná, co má udělat, takže kód neusíme psát my sami. První novinka je u tlačítka pro nové album, a je to vlastnost PostBackUrl. Tato vlastnost obsahuje adresu, na kterou se máme přepnout. Je to kvůli tomu, že když přidáváme nový záznam, nechceme, aby byl v URL parametr id s identifikátorem nějakého jiného záznamu. Nastavení této vlastnosti způsobí, že se formulář odešle na adresu AlbumDetail.aspx a to bez parametru id. To je přesně to, co chceme.

Druhá věc, kterou ještě neznáme, je vlastnost OnClientClick. Její hodnotou je klientský skript (javascript), který se spustí v prohlížeči při kliknutí na tlačítko. Pokud tento skript vrátí hodnotu false, akce se stornuje. confirm je javascriptová funkce, která zobrazí zprávu s předaným textem a s tlačítky OK a Storno. Pokud uživatel klikne na OK, vrátí se true a záznam se díky tomu smaže, pokud uživatel zvolí tlačítko Storno, skript vrátí hodnotu false a nic se nestane.

Poznámka: Komponenta LinkButton vyžaduje, aby prohlížeč podporoval javascript. Pokud prohlížeč javascript neumí, tato komponenta nebude fungovat. V podstatě je to totiž hypertextový odkaz, který odesílá formulář na server. Odesílat formulář na server hypertextovým odkazem ale v čistém HTML bez javascriptu možné není, jde to pouze pomocí tlačítka typu submit. Pokud potřebujete, aby stránka fungovala bez javascriptu, použijte klasickou komponentu Button, která funguje bez problémů. Je také na místě podotknout, že vlastnost OnClientClick bez javascriptu také nefunguje, formulář se v takovém případě odešle vždy. Na druhou stranu v dnešní době, kdy alespoň jednoduché javascripty umí i hloupý mobilní telefon, bych se nepodporou javascriptu moc netrápil. Většina komponent v ASP.NET bez zapnutého javascriptu funguje, LinkButton je jedna z výjimek.

Pokud si zobrazíme detail nějakého alba, bude to nyní vypadat nějak takto:

Zobrazení detailů alba

Šablony pro přidávání a úpravy alba

Protože to, co vygenerovalo Visual Studio jako šablonu pro přidávání a úpravy souborů, se nám samozřejmě nebude líbit ani hodit, zkusíme trochu uživatelům zpříjemnit přidávání záznamů. Upravte tedy šablonu takto:

        <InsertItemTemplate>
<table>
<tr>
<td>Název alba: </td>
<td>
<asp:TextBox ID="TitleTextBox" runat="server" Text='<%# Bind("Title") %>' Width="300px"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ErrorMessage="*" ControlToValidate="TitleTextBox"></asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<td>Autor hudby: </td>
<td>
<asp:TextBox ID="AuthorTextBox" runat="server" Text='<%# Bind("Author") %>' Width="300px"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator2" runat="server" ErrorMessage="*" ControlToValidate="AuthorTextBox"></asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<td>Interpret: </td>
<td>
<asp:TextBox ID="InterpretTextBox" runat="server" Text='<%# Bind("Interpret") %>' Width="300px"></asp:TextBox>
</td>
</tr>
<tr>
<td>Žánr: </td>
<td>
<asp:SqlDataSource ID="SqlDataSource2" runat="server" ConnectionString="<%$ ConnectionStrings:ConnectionString %>"
SelectCommand="SELECT [GenreId], [Title] FROM [Genres] ORDER BY [Title]"></asp:SqlDataSource>
<asp:DropDownList ID="DropDownList1" runat="server" DataSourceID="SqlDataSource2" DataTextField="Title" DataValueField="GenreId" Width="300px" SelectedValue='<%#Bind("GenreId") %>'>
</asp:DropDownList>
</td>
</tr>
<tr>
<td>Rok vydání: </td>
<td>
<asp:TextBox ID="YearTextBox" runat="server" Text='<%# Bind("Year") %>' Width="100px"></asp:TextBox>
<asp:RangeValidator ID="RangeValidator1" runat="server" ErrorMessage="*" ControlToValidate="YearTextBox" MinimumValue="1900" MaximumValue="3000"></asp:RangeValidator>
</td>
</tr>
<tr>
<td>Datum pořízení: </td>
<td>
<br />
<asp:Calendar ID="Calendar1" runat="server" SelectedDate='<%#Bind("DateBought") %>'></asp:Calendar>
<br />
</td>
</tr>
<tr>
<td>Hodnocení: </td>
<td>
<asp:RadioButtonList ID="RadioButtonList1" runat="server" CellPadding="3" RepeatDirection="Horizontal" RepeatLayout="Flow" SelectedValue='<%#Bind("Rating") %>'>
<asp:ListItem>1</asp:ListItem>
<asp:ListItem>2</asp:ListItem>
<asp:ListItem Selected="True">3</asp:ListItem>
<asp:ListItem>4</asp:ListItem>
<asp:ListItem>5</asp:ListItem>
</asp:RadioButtonList>
</td>
</tr>
</table>

<asp:Button ID="InsertCancelButton" runat="server" CausesValidation="False" CommandName="Cancel" UseSubmitBehavior="False" Text="Zrušit"></asp:Button>
<asp:Button ID="InsertButton" runat="server" CausesValidation="True" CommandName="Insert" Text="Přidat album"></asp:Button>
</InsertItemTemplate>

Název alba a autor hudby jsou v databázi povinná pole, přidal jsem tedy za ně validátory RequiredFieldValidator a nastavil jim vlastnost ControlToValidate na ID komponent, které mají kontrolovat. Do pole pro zadání žánru jsem přidal komponenty SqlDataSource a komponentu DropDownList, což je rozbalovací seznam. Vlastnost DataTextField je název sloupce, jehož hodnoty se mají zobrazit v seznamu, a vlastnost DataValueField je název sloupce, jehož hodnoty se uloží do databáze. My zde použijeme hodnotu ze sloupce GenreId, což je ID žánru, do kterého album patří. Vlastnost SelectedValue pak provážeme s parametrem GenreId v komponentě SqlDataSource1 pomocí příkazu Bind, takže tento parametr dostane ID žánru, který uživatel vybere ze seznamu.

Do řádku, kde zadáváme rok vydání, jsem přidal komponentu RangeValidator, která kontroluje, jestli je zapsaná hodnota číslo a jestli není mimo nastavené hranice. Minimální rok je tedy 1900 a maximální 3000. Datum pořízení alba zadáme pomocí kalendáře, před něj a za něj jsem dal ještě značky <br />, aby se kalendář odsadil od toho, co je nad ním a pod ním. Vlastnost SelectedDate provážeme s příslušným parametrem, jehož hodnota se uloží do databáze.

Poslední sloupce s hodnocením jsem osadil komponentou RadioButtonList, do které jsem vložil položky s hodnotami od 1 do 5. Vlastnost RepeatDirection říká, jestli se položky budou řadit vedle sebe (hodnota Horizontal) nebo pod sebe (hodnota Vertical) a vlastnost RepeatLayout říká, jestli se má nebo nemá používat pro rozmístění přepínačů tabulka (v našem případě pro 1 řádek je to zbytečné).

Pak již následují dvě tlačítka, jedno pro přidání alba, druhé pro zrušení operace a návrat na hlavní stránku.

Je jasné, že vypisovat celý tento kód by nemuselo být příliš vhodné, já jsem akorát změnil to, co nám Visual Studio vygenerovalo. Pokud se přepnete do režimu Source a rozbalíte podokno úkolů komponenty FormView1, uvidíte odkaz Edit templates. Pokud na něj kliknete, můžete vizuálně upravovat obsah šablony. Hodí se to, pokud potřebujete nakonfigurovat vnořený SqlDataSource. Je ale dobré oba režimy kombinovat, úplně vše naklikat nejde, resp. jde, ale s nevalným výsledkem. Pro nakonfigurávání komponenty SqlDataSource je ale Design režim ideální.

Úprava šablon - tlačítko Edit templates

Úprava šablon

Pokud se nyní v naší aplikaci rozhodneme přidat nový záznam, upravený formulář bude vypadat již o dost lépe, bude použitelnější a také bude kontrolovat, aby data nebyla zadána chybně.

Formulář pro přidávání nového záznamu

Šablona pro úpravu záznamu

Šablona pro upravování záznamů bude vypadat velice podobně, jediný rozdíl je vlastně jen v tlačítkách na konci. Změňte tedy její kód takto:

        <EditItemTemplate>
<table>
<tr>
<td>Název alba: </td>
<td>
<asp:TextBox ID="TitleTextBox" runat="server" Text='<%# Bind("Title") %>' Width="300px"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ErrorMessage="*" ControlToValidate="TitleTextBox"></asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<td>Autor hudby: </td>
<td>
<asp:TextBox ID="AuthorTextBox" runat="server" Text='<%# Bind("Author") %>' Width="300px"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator2" runat="server" ErrorMessage="*" ControlToValidate="AuthorTextBox"></asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<td>Interpret: </td>
<td>
<asp:TextBox ID="InterpretTextBox" runat="server" Text='<%# Bind("Interpret") %>' Width="300px"></asp:TextBox>
</td>
</tr>
<tr>
<td>Žánr: </td>
<td>
<asp:SqlDataSource ID="SqlDataSource2" runat="server" ConnectionString="<%$ ConnectionStrings:ConnectionString %>"
SelectCommand="SELECT [GenreId], [Title] FROM [Genres] ORDER BY [Title]"></asp:SqlDataSource>
<asp:DropDownList ID="DropDownList1" runat="server" DataSourceID="SqlDataSource2" DataTextField="Title" DataValueField="GenreId" Width="300px" SelectedValue='<%#Bind("GenreId") %>'>
</asp:DropDownList>
</td>
</tr>
<tr>
<td>Rok vydání: </td>
<td>
<asp:TextBox ID="YearTextBox" runat="server" Text='<%# Bind("Year") %>' Width="100px"></asp:TextBox>
<asp:RangeValidator ID="RangeValidator1" runat="server" ErrorMessage="*" ControlToValidate="YearTextBox" MinimumValue="1900" MaximumValue="3000"></asp:RangeValidator>
</td>
</tr>
<tr>
<td>Datum pořízení: </td>
<td>
<br />
<asp:Calendar ID="Calendar1" runat="server" SelectedDate='<%#Bind("DateBought") %>'></asp:Calendar>
<br />
</td>
</tr>
<tr>
<td>Hodnocení: </td>
<td>
<asp:RadioButtonList ID="RadioButtonList1" runat="server" CellPadding="3" RepeatDirection="Horizontal" RepeatLayout="Flow" SelectedValue='<%#Bind("Rating") %>'>
<asp:ListItem>1</asp:ListItem>
<asp:ListItem>2</asp:ListItem>
<asp:ListItem>3</asp:ListItem>
<asp:ListItem>4</asp:ListItem>
<asp:ListItem>5</asp:ListItem>
</asp:RadioButtonList>
</td>
</tr>
</table>

<asp:Button ID="UpdateCancelButton" runat="server" CausesValidation="False" CommandName="Cancel" UseSubmitBehavior="False" Text="Zrušit"></asp:Button>
<asp:Button ID="UpdateButton" runat="server" CausesValidation="True" CommandName="Update" Text="Uložit změny"></asp:Button>
</EditItemTemplate>

Dokončení aneb až sem to šlo bez programování

Aplikace nyní funguje, umí přidávat, upravovat i mazat záznamy, a to jsme vlastně nenapsali jediný řádek kódu. Jediná věc, kterou komponenta FormView neumí, je postarat se o to, co se má stát po přidání záznamu, případně po jeho úpravě či smazání. Přepněte se proto do režimu Design a označte komponentu FormView1. V okně vlastností se přepněte do režimu Events kliknutím na ikonu Events .

Dvakrát klikněte do řádku ItemInserted, aby se nám vytvořila procedura této události. Událost ItemInserted se vyvolá hned poté, co je záznam do databáze přidán. Dovnitř této procedury napište tento kód v jazyce, který jste si zvolili:

Kód v jazyce Visual Basic .NET
        Response.Redirect(ResolveClientUrl("~/Default.aspx"))
Kód v jazyce C#
        Response.Redirect(ResolveClientUrl("~/Default.aspx"));

Ten samý řádek vložte i do události ItemDeleted, která nastane hned poté, co je záznam z databáze odebrán. Response.Redirect je metoda, která provede přesměrování požadavku na zadanou adresu. Funkce ResolveClientUrl převede adresu s vlnovkovou notací na adresu relativní k adrese, na které je aktuálně klient. V této aplikaci jsme mohli napsat rovnou Response.Redirect("Default.aspx"), protože nemáme žádné stránky jinde než v kořenovém adresáři aplikace, ale je dobré si zvyknout používat tyto zápisy adres z jednoho prostého důvodu - ať jsme již na stránce v kterémkoliv adresáři na webu, použitím vlnovkové notace se dostaneme vždy tam, kam potřebujeme. Vlnovka ~ značí kořenový adresář aplikace.

Nakonec musíme ještě ošetřit kliknutí na tlačítka Zpět, chceme se opět přesměrovat na úvodní stránku se seznamem alb. Protože komponenta FormView nemá žádnou událost, která nastane při zrušení akce, nezbývá nám než odchytit přímo událost ItemCommand, která nastane vždy, když odešleme formulář komponentou, která má nastavenou vlastnost CommandName. Vytvořte tedy proceduru události ItemCommand a vložte do ní tento kód:

Kód v jazyce Visual Basic .NET
        If String.Equals(e.CommandName, "Cancel", StringComparison.InvariantCultureIgnoreCase) Then
Response.Redirect(ResolveClientUrl("~/Default.aspx"))
End If
Kód v jazyce C#
        if (String.Equals(e.CommandName, "Cancel", StringComparison.InvariantCultureIgnoreCase)) 
Response.Redirect(ResolveClientUrl(
"~/Default.aspx"));

Tady jste si možná všimnuli zvláštního způsobu porovnávání dvou řetězců přes String.Equals. Tato metoda porovnává dva řetězce, její výhodou je ale to, že můžete zadat i třetí parametr, v našem případě InvariantCultureIgnoreCase, který určuje další parametry porovnávání. V tomto případě nerozlišuje mezi velkými a malými písmeny, což právě potřebujeme, názvy příkazů v komponentách totiž nejsou case-sensitive (Cancel je stejné jako cancel). Mohli bychom napsat také e.CommandName.ToLower() = "cancel", což by fungovalo stejně, ale to umí každý a navíc je to pomalejší, i když u takto krátkých slov je rozdíl neměřitelný. Nicméně String.Equals je systémové řešení a je dobré o něm vědět.

Závěrem

V příštím díle naši aplikaci dokončíme, naučíme se totiž pracovat se vzhledem a se skiny. Ukážeme si také, jak ASP.NET elegantně řeší některé nedomyšlenosti formulářových prvků v HTML a jejich stylování v CSS, které nám mohou nadělat problémy. Naši aplikaci pro evidenci hudebních CD převedeme do trochu "koukatelné" podoby. A samozřejmě přidáme do formulářů zobrazení seznamu skladeb v každém albu a umožníme jej uživatelům upravovat.

 

hodnocení článku

1 bodů / 1 hlasů       Hodnotit mohou jen registrované uživatelé.

 

Mohlo by vás také zajímat

Práce s časovými pásmy a letním časem v aplikaci a databázi - díl 3.: DateTimeOffset v .NET Frameworku

DateTimeOffset je méně využívanou alternativou struktury DateTime v .NET Frameworku. Navíc dovoluje ukládat časové pásmo a pohodlně s ním pracovat.

Co je to .NET Framework 4.5?

V tomto článku se snažím nastínit rozdíl mezi .NET 4 a .NET 4.5 z pohledu zpětné kompatibility.

Windows Azure - díl 6.: Migrace ASP.NET aplikace do Azure WebSite

Dnes si ukážeme, jak zmigrovat existující ASP.NET aplikaci a databázi z webhostingu do Windows Azure.

 

 

Nový příspěvek

 

Diskuse: Pokročilá práce s daty v ASP.NET 2.0

Zdravim, mam problem s predanim vice parametru - toto me nejede - <asp:HyperlinkField DataTextField="WOR_CASH_BOX" DataNavigateUrlFields="WOR_CASH_BOX, WOR_DATE_TIME"

DataNavigateUrlFormatString="~/Detail.aspx?stav={0}&datum={1}"

HeaderText="Stav pokladny" SortExpression="WOR_CASH_BOX" />

vubec se to nezobrazi jako odkaz - v cem by mohl byt problem? Diky

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: Pokročilá práce s daty v ASP.NET 2.0

Děkuji za další pokračování zajímavého a přínosného seriálu a měl bych jednu prosbu, či námět.

Vámi zvolený projekt je sice jednoduchý, ale dostatečně typický na to, aby se dal aplikovat i na jiné úkoly, se kterými se v praxi setkáváme. Aby se ale ještě zvýšila jeho autenticita a přiblížilo se to ještě více podmínkám skutečného nasazení, bylo by dobré, kdybyste v rámci pokračování seriálu a dokončení celého projektu mohl trošičku zabrousit i do aplikování rolí uživatelů. Jelikož se jedná o projekt pro ASP.NET, tedy primárně určený na internet, asi nebudeme chtít, aby přidávat či mazat záznamy mohl každý návštěvník ale pouze někdo, případně aby mohl modifikovat záznam pouze jeho autor atd, a ostatní návštěvníci, aby si mohli sbírku mých CD pouze prohlédnout. Pokud by to tedy nebylo moc nad rámec zamýšlené výuky, přimlouval bych se i za toto doplnění - myslím, že u takto postavených aplikací (které data pouze nesosají z nějakého úložiště) by to k tématu určitě patřilo.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Ano, přesně to mám v plánu. Správa uživatelů přijde co nevidět.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Omlouvám se, ani jsem nepoděkoval za povzbudivou odpověď, takže napravuji a už se těším na pokračování.

A když už jsem v tom psaní, i když to zrovna sem asi moc nepatří, měl bych ještě jednu otázku.

Databáze MS SQL má data uložena v jednom separátním souboru (tak jako to bylo ukázáno v tomto příkladu) a proto stačí na cílový server tento soubor přenést a pouze pro aplikaci nalinkovat v připojovacím řetězci?

Ptám se proto, že se příznám - doposud jsem prakticky ve webových aplikacích pracoval pouze s MySQL a tam datové soubory jako takové přístupné nebyly (i třeba jen z důvodu, že mnohdy MySQL servery běhají na Linuxu, takže prostý přenos nějakého souboru by nebyl z hlediska souborové kompatibility možný) a aktualizace či přenos databáze se tam (mimo ruční editace) běžně dělal přes dump zdrojových dat do texťáku a spuštění vzniklého příkazového souboru na cílové mašině.

V ASP.NET se data v databázích aktualizují přenosem datového souboru, nebo je to otázkou konkrétní konfigurace u daného poskytovatele připojení?

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Záleží na poskytovateli hostingu. Někteří vám umožní nahrát soubor do složky App_Data, někteří soubory ukládají jinam (což je z hlediska výkonu lepší, protože databázové servery bývají čast na jiném počítači než servery webové), ale záleží, jak kde. I MySQL ukládá databáze do nějakých souborů, ale nejsou hned tak vidět. MSSQL standardně ukládá databáze někam do složky Program Files, ale umí soubor s databází připojit odkudkoliv.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Kde je to slubene pokracovanie? Sup sup pisat :)

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Dobrý den, mužete mi poradit, jak zadat do SqlDataSource doConnectionString hodnotu uloženou v Session (uživatel a heslo). Pokouším se o to nějak takto:

<asp:SqlDataSource 
        ID="SqlDataSource1" runat="server" ConnectionString="Server=XEON2\SQLEXPRESS;Initial Catalog=bakalari;User ID=<%#(string)Session["Jmeno"]%> ;Password=<%#(string)Session["Heslo"]%>"
        DataSourceMode="Dataset" 
        SelectCommand="SELECT [TITUL], [PRIJMENI], [JMENO], [ZKRATKA], [FUNKCE], [rodne_c] FROM [ucitele]" 
        UpdateCommand="UPDATE [Ucitele] SET [jmeno] = @jmeno, [prijmeni] = @prijmeni WHERE [Rodne_C] =@Rodne_C">
        <UpdateParameters>
            <asp:Parameter Name="jmeno" />
            <asp:Parameter Name="prijmeni" />
            <asp:Parameter Name="Rodne_C" />
            <asp:Parameter Name="ID" />
        </UpdateParameters>
      
    </asp:SqlDataSource>
nahlásit spamnahlásit spam 0 odpovědětodpovědět

Obávám se, že tohleto nejde. Binding expressions <%# %> mají mnoho omezení - pokud je používáte jako vlastnosti serverových komponent, musí být hodnota vlastnosti pouze binding expression a nikoliv kus textu a expression uvnitř, jako to máte tady. Jedinou možností je nějak do proměnné poskládat connectionString a ten pak na vlastnost ConnectionString napíchnout.

Druhé omezení binding expressionů je to, že se nedají použít všude, ale jen u komponent, na kterých probíhá událost DataBinding. Na tomto konkrétním použití pro skládání connectionStringu se binding expression ani použít nedá, protože to už je pozdě. Tyto expressiony se vyhodnocují až v události DataBinding, kdy komponenta již má vytažená data z databáze a něco s nimi provádí. ConnectionString potřebujete už před tím.

ConnectionString tedy musíte poskládat a nastavit v kódu třeba v události Page_Load, ale ne později (např. v PreRender nebo tak), jinak to nebude fungovat.

Jinak by mě zajímalo, jestli opravdu potřebujete ConnectionString skládat dynamicky za běhu aplikace, jestli nestačí mít jeden natvrdo uvedený v souboru web.config.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Za prvé díky za super seriál, ale chci se zeptat:

V databázi mám v tabulce pole typu Datetime kde výchozí hodnota

(Null) má hodnotu 1.1.3000.

Je nějaký inteligentní způsob jak např. v ItemTemplate

udělat z 1.1.3000 prázdný znak ???

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Buď to změňte na DATETIME, co umí NULL, aktualizujte data a udělejte třeba trigger na insert a update, který bude hodnoty 1.1.3000 konvertovat na NULL.

Tak bych to řešil já, protože mít v databázi takovéhle čuňárny není něco, co mi bylo po srsti.

Pokud to v DB měnit nechcete, pak asi nejjednodušší bude napsat komponentu, která to zařídí. Napíšete třídu, pojmenujete ji třeba DateTimeLiteral, podědíte z Control. Přidáte vlastnost Date typu DateTime a přepíšete metodu RenderControl (nebo Render, teď nevím kterou - tu, která není public, mrkněte do Object Browseru). V této metodě do stránky přes writer.Write vypíšete buď datum, nebo nic.

V šabloně pak použijete něco jako:

<cc:DateTimeLiteral runat="server" Date='<%# Eval("Datum") %>' />

Anebo si na to udělejte funkci, kterou dáte kolem toho evalu, ale pár chytrých komponent je vždy lepší řešení než smečka globálních funkcí.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Děkuji za odpověď, pole DateTime které umí Null by bylo to nejlepší

ale jsem začátečník, a myslel jsem že DateTime tohle neumí (nevím jak to nastavit) ale přesně to s NULL by mi vyhovovalo

Co se týká dědičnosti třídy, to je taky oříšek, musím se hoodně učit :-D Není tady na webu něco co by vystihovalo tento případ.

Pokud bych chtěl placenou konzultaci, dáte mi kontakt? Minule jsem posílal na kontakt tady z webu, ale asi jste byl zaneprázdněn ...

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: Pokročilá práce s daty v ASP.NET 2.0

Dobrý den,

ke každé položce v ListView bych chtěl přidat tlačítko.

Zkoušel jsem <asp:Button ID="Button<%#Eval("id")%>" runat="server" Text="Button" />, ale to bohužel nefunguje. Mohl by mi někdo poradit?

Děkuji

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: Pokročilá práce s daty v ASP.NET 2.0

Mam dotaz jde nejak listview nacist dynamicky ?

Mam stranku kde selektem vyberu jmena tabulek v databazi a pri kliku na jenu znich v datalistu, zobrazim jeji obsah do gridview. Ale jak udelat aby se nacetl patricne i list view ? a mohl jsem pridavat položky. Mozna by to slo jen nejakym SQL dotazem.

Prosim poradte

Dekuji

S pozdravem Vojta

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: Pokročilá práce s daty v ASP.NET 2.0

Zdravím,

mám problém hneď na začiatku tejto "lekcie". Keď v komponente SqlDataSource kliknem na ConfigureDataSource tak dostanem chybovú hlášku "Error occured while getting connestion string information from configuration - Cannot get web application service." Postupoval som pritom presne podla navodu. Kde moze byt chyba? (Predchadzajuce lekcie som spravil a vsetko fungovalo).

Vdaka,

Dave.

PS. mohli by ste prosim dat aj klikaci postup na zakomponovanie databazy, kt. som skopiroval do application data?

PS1: Sorry az teraz som si vsimol prispevok, kde sa hovori o restartovani aplikacie, respektive staci zavriet a otvorit projekt - potom to uz slape. Neexistuje elegantnejsie riesenie???

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Zistil som, že tlačítka majú vlastnosť PostBackUrl, takže sa napr. pre tlačítko Cancel dá priamo v design mode nakliknúť adresa stránky, ktorá sa má načítať keď kliknem na tlačítko Cancel. (kód viď. nižšie) Chcel som sa spýtať, či je nejaký zásadný rozdiel medzi programovacím spôsobom uvádzaným v lekcii alebo týmto mojím. (je mi hneď jasná jedna nevýhoda môjho spôsobu - treba nakliknúť PostBackUrl adresu pre všetky tlačítka, ktoré sú na FormView)

<asp:LinkButton ID="UpdateCancelButton" runat="server" CausesValidation="False" CommandName="Cancel"
                    Text="Cancel" PostBackUrl="~/Default.aspx"></asp:LinkButton>

Vďaka,

Dave.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Pokud nastavíte PostBackUrl, na danou stránku se odešle PostBack, což mimo jiné znamená, že by na tohle měla být stránka připravena a měla by nějak odeslané informace zpracovávat.

Použitím Redirectu se provede klasický GET. Při rušení operace není potřeba stav stránky předávat jinam, zkrátka stačí stránku jen a pouze načíst. PostBack tedy není vhodné řešení, používá se v jiných situacích.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: Pokročilá práce s daty v ASP.NET 2.0

Dobrý den,

prosím o radu s tímto problémem.

Kdyz vytvořím dle návodu stránku AlbumDetai a chci ji spustit, nastane tento problém, který se vypíše v prohlížeči.

DataBinding: 'System.Data.DataRowView' does not contain a property with the name 'Genre'.

Snažil jsem si vyGooglit co se dalo, ale všude jsem našel nejisté odpovědi a navíc z toho stejně nejsem chytrý, co přesně to způsobuje.

Předem děkuji za jakoukoli pomoc.

Pavel B.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Mám stejný problém a nevím co s tím, poraďte prosím.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Ještě to zkontroluju, ale podle mě jste nečetli postup správně. Nejprve v SqlDataSource naklikáme SQL příkazy jen tak a pa musíte ještě nahradit hodnotu vlastnosti SelectCommand SQL příkazem, který je uveden pod tím, a který sloupec Genre obsahuje.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: Pokročilá práce s daty v ASP.NET 2.0

Dobrý den, mám problém při vkládání příspěvků. U sqlDataSource mi nejde nastavit insert a update. Když kliknu na advanced tak check boxy jsou šedivé (nejdou zatrhnout). Mohl by jste mi prosím poradit jak to napravit? Děkuji.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Domnívám se, že za to může tvar SELECT, který se snaží načíst i název kategorie a používá JOIN.

SELECT [Albums].[AlbumId], [Albums].[Title] AS [Title], [Albums].[GenreId], [Author], [Interpret], [Year], [DateBought], [Rating], [Genres].[Title] AS [Genre] FROM [Albums] LEFT JOIN [Genres] ON [Albums].[GenreId] = [Genres].[GenreId] WHERE ([AlbumId] = @AlbumId)

To je špatně. Pokusil bych se vyhnout JOINU třeba takto

SELECT [Albums].[AlbumId], [Albums].[Title] AS [Title], [Albums].[GenreId], (SELECT Title FROM Genres WHERE Albums.GenreId = Genres.GenreId) AS Genre, [Author], [Interpret], [Year], [DateBought], [Rating], FROM [Albums]

Takový tvar umožní VStudio vygenerovat příslušní INSERT a UPDATE.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

JOIN není špatně. Visual Studio sice již neumí taktovýto dotaz upravovat, nicméně Vámi uvedená varianta se na databázi bude provádět daleko pomaleji a bude velmi zatěžovat databázový server. U tabulky s 10 záznamy je to jedno, ale snažím se čtenáře naučit rovnou od začátku psát SQL dotazy efektivně.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: Pokročilá práce s daty v ASP.NET 2.0

Moc dobrý seriál,

poučný, super

I když se musím přiznat, že jsem se zapotil

když mi aplikace nechtěla načíst ConectionString z web.config

Na radu T. Hercega pomohl restart aplikace :-O

Už to šlape :-D

nahlásit spamnahlásit spam 2 / 2 odpovědětodpovědět

Diskuse: Pokročilá práce s daty v ASP.NET 2.0

Díky za seriál. Potřeboval bych radu, jak se ASP.NET co nejlépe naučit. Knížky, postupy, atd. A kdy bude další díl:)

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: Pokročilá práce s daty v ASP.NET 2.0

Fakt skvělý článek, moc pomohl, díky, Josef Hejnák

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback