Aktualizowanie i usuwanie istniejących danych binarnych (C#)
We wcześniejszych samouczkach pokazano, jak kontrolka GridView ułatwia edytowanie i usuwanie danych tekstowych. W tym samouczku zobaczymy, jak kontrolka GridView umożliwia również edytowanie i usuwanie danych binarnych, niezależnie od tego, czy te dane binarne są zapisywane w bazie danych, czy przechowywane w systemie plików.
Wprowadzenie
W ciągu ostatnich trzech samouczków dodaliśmy sporo funkcji do pracy z danymi binarnymi. Zaczęliśmy od dodania BrochurePath
kolumny do Categories
tabeli i zaktualizowania architektury. Dodaliśmy również metody warstwy dostępu do danych i warstwy logiki biznesowej, aby pracować z istniejącą Picture
kolumną tabeli Categories, która zawiera zawartość binarną pliku obrazu. Utworzyliśmy strony internetowe umożliwiające prezentowanie danych binarnych w siatce Link do pobierania broszury, z obrazem kategorii pokazanym <img>
w elemecie i dodaliśmy element DetailsView, aby umożliwić użytkownikom dodawanie nowej kategorii i przekazywanie jej broszury i danych obrazów.
Wszystko, co pozostaje do zaimplementowania, to możliwość edytowania i usuwania istniejących kategorii, które wykonamy w tym samouczku przy użyciu wbudowanego edytowania i usuwania funkcji GridView. Podczas edytowania kategorii użytkownik będzie mógł opcjonalnie przekazać nowy obraz lub mieć kategorię, która będzie nadal używać istniejącej. W przypadku broszury mogą zdecydować się na użycie istniejącej broszury, przekazać nową broszurę lub wskazać, że kategoria nie ma już broszury skojarzonej z nią. Zacznijmy!
Krok 1. Aktualizowanie warstwy dostępu do danych
Funkcja DAL ma generowane automatycznie Insert
metody , Update
i Delete
, ale te metody zostały wygenerowane na CategoriesTableAdapter
podstawie głównego zapytania, które nie zawiera kolumny Picture
. Insert
W związku z tym metody i Update
nie zawierają parametrów do określania danych binarnych dla obrazu kategorii. Podobnie jak w poprzednim samouczku, musimy utworzyć nową metodę TableAdapter do aktualizowania Categories
tabeli podczas określania danych binarnych.
Otwórz typowy zestaw danych i w Projektant kliknij prawym przyciskiem myszy CategoriesTableAdapter
nagłówek s, a następnie wybierz polecenie Dodaj zapytanie z menu kontekstowego, aby uruchomić Kreatora konfiguracji zapytań TableAdapter. Ten kreator rozpoczyna się od pytania, w jaki sposób zapytanie TableAdapter powinno uzyskać dostęp do bazy danych. Wybierz pozycję Użyj instrukcji SQL i kliknij przycisk Dalej. W następnym kroku zostanie wyświetlony monit o wygenerowanie typu zapytania. Ponieważ tworzymy zapytanie w celu dodania nowego rekordu Categories
do tabeli, wybierz pozycję AKTUALIZUJ i kliknij przycisk Dalej.
Rysunek 1. Wybierz opcję AKTUALIZACJI (kliknij, aby wyświetlić obraz pełnowymiarowy)
Teraz musimy określić instrukcję UPDATE
SQL. Kreator automatycznie sugeruje instrukcję odpowiadającą UPDATE
głównemu zapytaniu TableAdapter (które aktualizuje CategoryName
wartości , Description
i BrochurePath
). Zmień instrukcję tak, aby kolumna Picture
została dołączona wraz z parametrem @Picture
, w następujący sposób:
UPDATE [Categories] SET
[CategoryName] = @CategoryName,
[Description] = @Description,
[BrochurePath] = @BrochurePath ,
[Picture] = @Picture
WHERE (([CategoryID] = @Original_CategoryID))
Na ostatnim ekranie kreatora zostanie wyświetlony monit o nadenie nazwy nowej metody TableAdapter. Wprowadź UpdateWithPicture
i kliknij przycisk Zakończ.
Rysunek 2. Nadaj nazwę nowej metodzie UpdateWithPicture
TableAdapter (kliknij, aby wyświetlić obraz pełnowymiarowy)
Krok 2. Dodawanie metod warstwy logiki biznesowej
Oprócz zaktualizowania dal musimy zaktualizować BLL, aby uwzględnić metody aktualizowania i usuwania kategorii. Są to metody wywoływane z warstwy prezentacji.
Aby usunąć kategorię, możemy użyć metody wygenerowanej CategoriesTableAdapter
Delete
automatycznie. Dodaj następującą metodę do klasy CategoriesBLL
:
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Delete, true)]
public bool DeleteCategory(int categoryID)
{
int rowsAffected = Adapter.Delete(categoryID);
// Return true if precisely one row was deleted, otherwise false
return rowsAffected == 1;
}
W tym samouczku utworzymy dwie metody aktualizowania kategorii — jednej, która oczekuje danych obrazu binarnego i wywołuje UpdateWithPicture
metodę, którą właśnie dodaliśmy do CategoriesTableAdapter
metody , i innej, która akceptuje tylko CategoryName
wartości , Description
i BrochurePath
używa CategoriesTableAdapter
instrukcji generowanej Update
automatycznie przez klasę. Uzasadnieniem użycia dwóch metod jest to, że w niektórych okolicznościach użytkownik może chcieć zaktualizować obraz kategorii wraz z innymi polami, w takim przypadku użytkownik będzie musiał przekazać nowy obraz. Przekazane dane binarne obrazu mogą być następnie używane w instrukcji UPDATE
. W innych przypadkach użytkownik może być zainteresowany tylko aktualizowaniem, powiedzmy, nazwą i opisem. UPDATE
Jeśli jednak instrukcja oczekuje również danych binarnych dla Picture
kolumny, musimy również podać te informacje. Wymagałoby to dodatkowej podróży do bazy danych, aby przywrócić dane obrazu dla edytowanego rekordu. W związku z tym potrzebujemy dwóch UPDATE
metod. Warstwa logiki biznesowej określi, która z nich ma być używana na podstawie tego, czy dane obrazu są udostępniane podczas aktualizowania kategorii.
Aby to ułatwić, dodaj dwie metody do CategoriesBLL
klasy o nazwie UpdateCategory
. Pierwsza powinna akceptować trzy string
s, tablicę byte
i int
jako parametry wejściowe; drugi, tylko trzy string
s i .int
string
Parametry wejściowe są przeznaczone dla nazwy, opisu i ścieżki pliku broszury kategorii, byte
tablica jest przeznaczona dla zawartości binarnej obrazu kategorii, a identyfikator int
CategoryID
rekordu do aktualizacji. Zwróć uwagę, że pierwsze przeciążenie wywołuje drugą, jeśli przekazana tablica byte
to null
:
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Update, false)]
public bool UpdateCategory(string categoryName, string description,
string brochurePath, byte[] picture, int categoryID)
{
// If no picture is specified, use other overload
if (picture == null)
return UpdateCategory(categoryName, description, brochurePath, categoryID);
// Update picture, as well
int rowsAffected = Adapter.UpdateWithPicture
(categoryName, description, brochurePath, picture, categoryID);
// Return true if precisely one row was updated, otherwise false
return rowsAffected == 1;
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Update, true)]
public bool UpdateCategory(string categoryName, string description,
string brochurePath, int categoryID)
{
int rowsAffected = Adapter.Update
(categoryName, description, brochurePath, categoryID);
// Return true if precisely one row was updated, otherwise false
return rowsAffected == 1;
}
Krok 3. Kopiowanie za pośrednictwem funkcji Wstawianie i Wyświetlanie
W poprzednim samouczku utworzyliśmy stronę o nazwie , UploadInDetailsView.aspx
która wymieniła wszystkie kategorie w elemecie GridView i podała element DetailsView, aby dodać nowe kategorie do systemu. W tym samouczku rozszerzymy widok GridView, aby uwzględnić edytowanie i usuwanie obsługi. Zamiast kontynuować pracę z UploadInDetailsView.aspx
programu , zamiast tego umieśćmy zmiany tego samouczka na UpdatingAndDeleting.aspx
stronie z tego samego folderu ~/BinaryData
. Skopiuj i wklej deklaratywne znaczniki i kod z UploadInDetailsView.aspx
do UpdatingAndDeleting.aspx
.
Zacznij od otwarcia UploadInDetailsView.aspx
strony. Skopiuj całą składnię deklaratywną w elemecie, jak pokazano na rysunku <asp:Content>
3. Następnie otwórz UpdatingAndDeleting.aspx
i wklej ten znacznik w swoim <asp:Content>
elemecie. Podobnie skopiuj kod z UploadInDetailsView.aspx
klasy s page-behind do UpdatingAndDeleting.aspx
.
Rysunek 3. Kopiowanie deklaratywnego znacznika z UploadInDetailsView.aspx
(kliknij, aby wyświetlić obraz pełnowymiarowy)
Po skopiowaniu deklaratywnego znaczników i kodu odwiedź stronę UpdatingAndDeleting.aspx
. Powinny zostać wyświetlone te same dane wyjściowe i mają takie same środowisko użytkownika jak UploadInDetailsView.aspx
strona z poprzedniego samouczka.
Krok 4. Dodawanie obsługi usuwania do obiektówDataSource i GridView
Jak już omówiliśmy w samouczku Omówienie wstawiania, aktualizowania i usuwania danych , funkcja GridView udostępnia wbudowane funkcje usuwania, a te możliwości można włączyć po zaznaczeniu pola wyboru, jeśli bazowe źródło danych siatki obsługuje usuwanie. Obecnie obiekt ObjectDataSource obiekt GridView jest powiązany z elementem (CategoriesDataSource
) nie obsługuje usuwania.
Aby rozwiązać ten problem, kliknij opcję Konfiguruj źródło danych z tagu inteligentnego ObjectDataSource, aby uruchomić kreatora. Pierwszy ekran pokazuje, że obiekt ObjectDataSource jest skonfigurowany do pracy z klasą CategoriesBLL
. Kliknij przycisk Dalej. Obecnie określono tylko właściwości ObjectDataSource i InsertMethod
SelectMethod
. Jednak kreator automatycznie wypełnił listy rozwijane na kartach UPDATE i DELETE odpowiednio metodami UpdateCategory
i .DeleteCategory
Wynika to z faktu, że w CategoriesBLL
klasie oznaczyliśmy te metody przy użyciu DataObjectMethodAttribute
metody domyślnej do aktualizowania i usuwania.
Na razie ustaw listę rozwijaną kart UPDATE na wartość (Brak), ale pozostaw listę rozwijaną USUŃ ustawioną na DeleteCategory
wartość . Wrócimy do tego kreatora w kroku 6, aby dodać obsługę aktualizacji.
Rysunek 4. Konfigurowanie obiektu ObjectDataSource do użycia DeleteCategory
metody (kliknij, aby wyświetlić obraz pełnowymiarowy)
Uwaga
Po ukończeniu pracy kreatora program Visual Studio może zapytać, czy chcesz odświeżyć pola i klucze, co spowoduje ponowne wygenerowanie pól kontrolek sieci Web danych. Wybierz pozycję Nie, ponieważ wybranie pozycji Tak spowoduje zastąpienie wszelkich dostosowań pól, które mogły zostać wprowadzone.
Obiekt ObjectDataSource będzie teraz zawierać wartość dla jej DeleteMethod
właściwości, a także DeleteParameter
. Pamiętaj, że w przypadku używania kreatora do określania metod program Visual Studio ustawia właściwość ObjectDataSource OldValuesParameterFormatString
na original_{0}
wartość , co powoduje problemy z wywołaniami metody aktualizacji i usuwania. W związku z tym całkowicie wyczyść tę właściwość lub zresetuj ją do domyślnej wartości {0}
. Jeśli musisz odświeżyć pamięć w tej właściwości ObjectDataSource, zobacz samouczek Omówienie wstawiania, aktualizowania i usuwania danych .
Po ukończeniu pracy kreatora i naprawieniu znaczników deklaratywnego OldValuesParameterFormatString
objectDataSource powinny wyglądać podobnie do następującego:
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
OldValuesParameterFormatString="{0}" SelectMethod="GetCategories"
TypeName="CategoriesBLL" InsertMethod="InsertWithPicture"
DeleteMethod="DeleteCategory">
<InsertParameters>
<asp:Parameter Name="categoryName" Type="String" />
<asp:Parameter Name="description" Type="String" />
<asp:Parameter Name="brochurePath" Type="String" />
<asp:Parameter Name="picture" Type="Object" />
</InsertParameters>
<DeleteParameters>
<asp:Parameter Name="categoryID" Type="Int32" />
</DeleteParameters>
</asp:ObjectDataSource>
Po skonfigurowaniu obiektu ObjectDataSource dodaj możliwości usuwania do kontrolki GridView, zaznaczając pole wyboru Włącz usuwanie z tagu inteligentnego GridView. Spowoduje to dodanie pola polecenia do kontrolki GridView, której ShowDeleteButton
właściwość jest ustawiona na true
wartość .
Rysunek 5. Włączanie obsługi usuwania w elemecie GridView (kliknij, aby wyświetlić obraz pełnowymiarowy)
Poświęć chwilę na przetestowanie funkcji usuwania. Istnieje klucz obcy między tabelą Products
s CategoryID
a Categories
tabelą s CategoryID
, więc otrzymasz wyjątek naruszenia ograniczenia klucza obcego, jeśli spróbujesz usunąć dowolną z pierwszych ośmiu kategorii. Aby przetestować tę funkcję, dodaj nową kategorię, zapewniając broszurę i obraz. Moja kategoria testowa przedstawiona na rysunku 6 zawiera plik broszury testowej o nazwie Test.pdf
i obraz testu. Rysunek 7 przedstawia element GridView po dodaniu kategorii testowej.
Rysunek 6. Dodawanie kategorii testowej z broszurą i obrazem (kliknij, aby wyświetlić obraz pełnowymiarowy)
Rysunek 7. Po wstawieniu kategorii testowej jest wyświetlany w widoku GridView (kliknij, aby wyświetlić obraz pełnowymiarowy)
W programie Visual Studio odśwież Eksplorator rozwiązań. Powinien zostać wyświetlony nowy plik w folderze ~/Brochures
Test.pdf
(zobacz Rysunek 8).
Następnie kliknij link Usuń w wierszu Kategoria testu, powodując, że strona zostanie wyświetlona po awarii, a CategoriesBLL
metoda klasy s DeleteCategory
zostanie wyzwolona. Spowoduje to wywołanie metody DAL Delete
, co spowoduje wysłanie odpowiedniej DELETE
instrukcji do bazy danych. Dane są następnie przywracane do kontrolki GridView, a znaczniki są wysyłane z powrotem do klienta z kategorią testową, która nie jest już obecna.
Przepływ pracy usuwania pomyślnie usunął rekord Kategorii testów z Categories
tabeli, ale nie usunął pliku broszury z systemu plików serwera internetowego. Odśwież Eksplorator rozwiązań i zobaczysz, że Test.pdf
nadal znajduje się w folderze~/Brochures
.
Rysunek 8. Plik Test.pdf
nie został usunięty z systemu plików serwera sieci Web
Krok 5. Usuwanie usuniętego pliku broszury kategorii
Jedną z wad przechowywania danych binarnych spoza bazy danych jest to, że należy wykonać dodatkowe kroki w celu wyczyszczenia tych plików po usunięciu skojarzonego rekordu bazy danych. Kontrolki GridView i ObjectDataSource udostępniają zdarzenia uruchamiane zarówno przed, jak i po wykonaniu polecenia usuwania. W rzeczywistości musimy utworzyć programy obsługi zdarzeń zarówno dla zdarzeń wstępnych, jak i po akcji. Przed usunięciem rekordu Categories
musimy określić jego ścieżkę do pliku PDF, ale nie chcemy usuwać pliku PDF przed usunięciem kategorii w przypadku wystąpienia wyjątku, a kategoria nie zostanie usunięta.
Zdarzenie kontrolki RowDeleting
GridView jest uruchamiane przed wywołaniem polecenia delete obiektuDataSource, podczas gdy jego RowDeleted
zdarzenie jest uruchamiane po. Utwórz programy obsługi zdarzeń dla tych dwóch zdarzeń przy użyciu następującego kodu:
// A page variable to "remember" the deleted category's BrochurePath value
string deletedCategorysPdfPath = null;
protected void Categories_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
// Determine the PDF path for the category being deleted...
int categoryID = Convert.ToInt32(e.Keys["CategoryID"]);
CategoriesBLL categoryAPI = new CategoriesBLL();
Northwind.CategoriesDataTable categories =
categoryAPI.GetCategoryByCategoryID(categoryID);
Northwind.CategoriesRow category = categories[0];
if (category.IsBrochurePathNull())
deletedCategorysPdfPath = null;
else
deletedCategorysPdfPath = category.BrochurePath;
}
protected void Categories_RowDeleted(object sender, GridViewDeletedEventArgs e)
{
// Delete the brochure file if there were no problems deleting the record
if (e.Exception == null)
{
// Is there a file to delete?
if (deletedCategorysPdfPath != null)
{
System.IO.File.Delete(Server.MapPath(deletedCategorysPdfPath));
}
}
}
W procedurze obsługi zdarzeń RowDeleting
usuwany wiersz jest pobierany z kolekcji GridViewDataKeys
, do której można uzyskać dostęp w tej procedurze obsługi zdarzeń za pośrednictwem e.Keys
kolekcji.CategoryID
Następnie klasa jest GetCategoryByCategoryID(categoryID)
wywoływana CategoriesBLL
w celu zwrócenia informacji o usuwanym rekordzie. Jeśli zwrócony CategoriesDataRow
obiekt ma wartość inną niżNULL``BrochurePath
wartość, jest przechowywany w zmiennej deletedCategorysPdfPath
strony, aby można było usunąć plik w procedurze obsługi zdarzeń RowDeleted
.
Uwaga
Zamiast pobierać BrochurePath
szczegóły rekordu usuwanego RowDeleting
Categories
w procedurze obsługi zdarzeń, można również dodać BrochurePath
element do właściwości GridView DataKeyNames
i uzyskać dostęp do wartości rekordu za pośrednictwem e.Keys
kolekcji. Spowoduje to nieznaczne zwiększenie rozmiaru stanu widoku obiektu GridView, ale zmniejszy ilość potrzebnego kodu i zapisze podróż do bazy danych.
Po wywołaniu podstawowego polecenia usuwania obiektu ObjectDataSource program obsługi zdarzeń GridView RowDeleted
zostanie wyzwolony. Jeśli nie było żadnych wyjątków podczas usuwania danych i istnieje wartość deletedCategorysPdfPath
, plik PDF zostanie usunięty z systemu plików. Należy pamiętać, że ten dodatkowy kod nie jest potrzebny do wyczyszczenia danych binarnych kategorii skojarzonych z jego obrazem. Wynika to z tego, że dane obrazu są przechowywane bezpośrednio w bazie danych, dlatego usunięcie Categories
wiersza powoduje również usunięcie danych obrazu tej kategorii.
Po dodaniu dwóch procedur obsługi zdarzeń ponownie uruchom ten przypadek testowy. Podczas usuwania kategorii skojarzony z nią plik PDF jest również usuwany.
Aktualizowanie istniejących danych binarnych skojarzonych z rekordami zapewnia kilka interesujących wyzwań. Pozostała część tego samouczka zagłębia się w dodawanie możliwości aktualizacji do broszury i obrazu. Krok 6 bada techniki aktualizowania informacji o broszurze, podczas gdy krok 7 analizuje aktualizację obrazu.
Krok 6. Aktualizowanie broszury kategorii
Zgodnie z opisem w samouczku Omówienie wstawiania, aktualizowania i usuwania danych funkcja GridView oferuje wbudowaną obsługę edycji na poziomie wiersza, którą można zaimplementować za pomocą znacznika pola wyboru, jeśli jego bazowe źródło danych jest odpowiednio skonfigurowane. Obecnie obiekt CategoriesDataSource
ObjectDataSource nie jest jeszcze skonfigurowany do dołączania obsługi aktualizacji, więc dodajmy je.
Kliknij link Konfiguruj źródło danych w kreatorze objectDataSource i przejdź do drugiego kroku. Ze względu na DataObjectMethodAttribute
używane w CategoriesBLL
systemie lista rozwijana UPDATE powinna zostać automatycznie wypełniona UpdateCategory
przeciążeniem, które akceptuje cztery parametry wejściowe (dla wszystkich kolumn, ale Picture
). Zmień to tak, aby używało przeciążenia z pięcioma parametrami.
Rysunek 9. Konfigurowanie obiektu ObjectDataSource do używania UpdateCategory
metody , która zawiera parametr dla Picture
(kliknij, aby wyświetlić obraz w pełnym rozmiarze)
Właściwość ObjectDataSource będzie teraz zawierać wartość dla jej UpdateMethod
właściwości, a także odpowiednie UpdateParameter
wartości. Jak wspomniano w kroku 4, program Visual Studio ustawia właściwość ObjectDataSource OldValuesParameterFormatString
na original_{0}
wartość podczas korzystania z kreatora Konfigurowanie źródła danych. Spowoduje to problemy z wywołaniami metody aktualizacji i usuwania. W związku z tym całkowicie wyczyść tę właściwość lub zresetuj ją do wartości domyślnej . {0}
Po zakończeniu pracy kreatora i naprawieniu OldValuesParameterFormatString
elementu znaczniki deklaratywne obiektu ObjectDataSource powinny wyglądać następująco:
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
OldValuesParameterFormatString="{0}" SelectMethod="GetCategories"
TypeName="CategoriesBLL" InsertMethod="InsertWithPicture"
DeleteMethod="DeleteCategory" UpdateMethod="UpdateCategory">
<InsertParameters>
<asp:Parameter Name="categoryName" Type="String" />
<asp:Parameter Name="description" Type="String" />
<asp:Parameter Name="brochurePath" Type="String" />
<asp:Parameter Name="picture" Type="Object" />
</InsertParameters>
<DeleteParameters>
<asp:Parameter Name="categoryID" Type="Int32" />
</DeleteParameters>
<UpdateParameters>
<asp:Parameter Name="categoryName" Type="String" />
<asp:Parameter Name="description" Type="String" />
<asp:Parameter Name="brochurePath" Type="String" />
<asp:Parameter Name="picture" Type="Object" />
<asp:Parameter Name="categoryID" Type="Int32" />
</UpdateParameters>
</asp:ObjectDataSource>
Aby włączyć wbudowane funkcje edycji kontrolki GridView, zaznacz opcję Włącz edytowanie z tagu inteligentnego GridView. Spowoduje to ustawienie właściwości CommandField ShowEditButton
na true
wartość , co spowoduje dodanie przycisku Edytuj (oraz przycisk Aktualizuj i Anuluj dla edytowanego wiersza).
Rysunek 10. Konfigurowanie widoku GridView do obsługi edycji (kliknij, aby wyświetlić obraz w pełnym rozmiarze)
Odwiedź stronę za pośrednictwem przeglądarki i kliknij jeden z przycisków Edytuj wiersz. Pola CategoryName
i Description
są renderowane jako pola tekstowe. BrochurePath
Pole TemplateField nie ma EditItemTemplate
elementu , więc nadal pokazuje link ItemTemplate
do broszury. Pole Picture
obrazu jest renderowane jako pole tekstowe, którego Text
właściwość ma przypisaną wartość wartości ImageField DataImageUrlField
, w tym przypadku CategoryID
.
Rysunek 11. Element GridView nie ma interfejsu edycji elementu BrochurePath
(kliknij, aby wyświetlić obraz w pełnym rozmiarze)
Dostosowywanie interfejsuBrochurePath
edycji
Musimy utworzyć interfejs edycji dla BrochurePath
pola TemplateField, który umożliwia użytkownikowi:
- Pozostaw broszurę kategorii, w jakim jest,
- Aktualizowanie broszury kategorii poprzez przekazanie nowej broszury lub
- Całkowicie usuń broszurę kategorii (w przypadku, gdy kategoria nie ma już powiązanej broszury).
Musimy również zaktualizować Picture
interfejs edycji pola obrazu, ale przejdziemy do tego w kroku 7.
W tagu inteligentnym GridView kliknij link Edytuj szablony i wybierz pozycję BrochurePath
TemplateField s EditItemTemplate
z listy rozwijanej. Dodaj kontrolkę Sieci Web RadioButtonList do tego szablonu, ustawiając jej ID
właściwość na BrochureOptions
i jej AutoPostBack
właściwość na true
. W okno Właściwości kliknij wielokropek we Items
właściwości , co spowoduje wyświetlenie ListItem
Redaktor Kolekcji. Dodaj następujące trzy opcje odpowiednio z wartościami Value
1, 2 i 3:
- Korzystanie z bieżącej broszury
- Usuń bieżącą broszurę
- Przekaż nową broszurę
Ustaw pierwszą ListItem
właściwość true
na Selected
wartość .
Rysunek 12. Dodawanie trzech ListItem
s do listy RadioButtonList
Pod kontrolką RadioButtonList dodaj kontrolkę FileUpload o nazwie BrochureUpload
. Ustaw jej Visible
właściwość na false
wartość .
Rysunek 13. Dodawanie kontrolki RadioButtonList i FileUpload do kontrolki EditItemTemplate
(kliknij, aby wyświetlić obraz pełnowymiarowy)
Ten radioButtonList udostępnia trzy opcje dla użytkownika. Chodzi o to, że kontrolka FileUpload będzie wyświetlana tylko wtedy, gdy wybrano ostatnią opcję Przekaż nową broszurę. Aby to osiągnąć, utwórz procedurę obsługi zdarzeń dla zdarzenia RadioButtonList SelectedIndexChanged
i dodaj następujący kod:
protected void BrochureOptions_SelectedIndexChanged(object sender, EventArgs e)
{
// Get a reference to the RadioButtonList and its Parent
RadioButtonList BrochureOptions = (RadioButtonList)sender;
Control parent = BrochureOptions.Parent;
// Now use FindControl("controlID") to get a reference of the
// FileUpload control
FileUpload BrochureUpload =
(FileUpload)parent.FindControl("BrochureUpload");
// Only show BrochureUpload if SelectedValue = "3"
BrochureUpload.Visible = (BrochureOptions.SelectedValue == "3");
}
Ponieważ kontrolki RadioButtonList i FileUpload znajdują się w szablonie, musimy napisać trochę kodu, aby programowo uzyskać dostęp do tych kontrolek. Procedura SelectedIndexChanged
obsługi zdarzeń jest przekazywana odwołanie do elementu RadioButtonList w parametrze wejściowym sender
. Aby uzyskać kontrolkę FileUpload, musimy pobrać kontrolkę nadrzędną RadioButtonList i użyć metody stamtąd FindControl("controlID")
. Gdy mamy odwołanie zarówno do kontrolek RadioButtonList, jak i FileUpload, właściwość s kontrolki Visible
FileUpload jest ustawiona true
tylko wtedy, gdy właściwość RadioButtonList jest SelectedValue
równa 3, co jest elementem Value
dla broszury ListItem
Upload new .
Po utworzeniu tego kodu poświęć chwilę na przetestowanie interfejsu edycji. Kliknij przycisk Edytuj dla wiersza. Początkowo należy wybrać opcję Użyj bieżącej broszury. Zmiana wybranego indeksu powoduje ogłaszanie zwrotne. Jeśli zostanie wybrana trzecia opcja, zostanie wyświetlona kontrolka FileUpload, w przeciwnym razie jest ukryta. Rysunek 14 przedstawia interfejs edycji po pierwszym kliknięciu przycisku Edytuj; Rysunek 15 przedstawia interfejs po wybraniu opcji Przekaż nową broszurę.
Rysunek 14. Początkowo wybrana jest opcja Użyj bieżącej broszury (kliknij, aby wyświetlić obraz pełnowymiarowy)
Rysunek 15. Wybranie opcji Przekaż nową broszurę powoduje wyświetlenie kontrolki FileUpload (kliknij, aby wyświetlić obraz w pełnym rozmiarze)
Zapisywanie pliku broszury i aktualizowanie kolumnyBrochurePath
Po kliknięciu przycisku Aktualizuj kontrolki GridView zostanie wyzwolone jego RowUpdating
zdarzenie. Wywoływane jest polecenie aktualizacji obiektu ObjectDataSource, a następnie uruchamiane jest zdarzenie GridView RowUpdated
. Podobnie jak w przypadku usuwania przepływu pracy, musimy utworzyć programy obsługi zdarzeń dla obu tych zdarzeń. W procedurze obsługi zdarzeń RowUpdating
musimy określić, jaką akcję podjąć na SelectedValue
BrochureOptions
podstawie listy RadioButtonList:
- Jeśli parametr
SelectedValue
ma wartość 1, chcemy nadal używać tego samegoBrochurePath
ustawienia. W związku z tym musimy ustawić parametr sbrochurePath
ObjectDataSource na istniejącąBrochurePath
wartość aktualizowanego rekordu. Parametr ObjectDataSourcebrochurePath
można ustawić przy użyciu poleceniae.NewValues["brochurePath"] = value
. - Jeśli parametr
SelectedValue
ma wartość 2, chcemy ustawić wartość rekorduBrochurePath
naNULL
. Można to zrobić, ustawiając parametr objectDataSource nabrochurePath
Nothing
wartość , co powoduje użycie bazy danychNULL
w instrukcjiUPDATE
. Jeśli istnieje istniejący plik broszury, który jest usuwany, musimy usunąć istniejący plik. Jednak chcemy to zrobić tylko wtedy, gdy aktualizacja zostanie ukończona bez zgłaszania wyjątku. - Jeśli parametr
SelectedValue
ma wartość 3, chcemy upewnić się, że użytkownik przesłał plik PDF, a następnie zapisać go w systemie plików i zaktualizować wartość kolumny rekorduBrochurePath
. Ponadto, jeśli istnieje istniejący plik broszury, który jest zastępowany, musimy usunąć poprzedni plik. Jednak chcemy to zrobić tylko wtedy, gdy aktualizacja zostanie ukończona bez zgłaszania wyjątku.
Kroki, które należy wykonać, gdy element RadioButtonList s SelectedValue
ma wartość 3, są praktycznie identyczne z krokami używanymi przez program obsługi zdarzeń kontrolki ItemInserting
DetailsView. Ta procedura obsługi zdarzeń jest wykonywana po dodaniu nowego rekordu kategorii z kontrolki DetailsView dodanej w poprzednim samouczku. W związku z tym zaleca się refaktoryzację tej funkcji na oddzielne metody. W szczególności przeniesiono wspólną funkcjonalność do dwóch metod:
ProcessBrochureUpload(FileUpload, out bool)
przyjmuje jako dane wejściowe wystąpienie kontrolki FileUpload i wartość logiczną danych wyjściowych, która określa, czy operacja usuwania lub edycji powinna być kontynuowana, czy też powinna zostać anulowana z powodu błędu weryfikacji. Ta metoda zwraca ścieżkę do zapisanego pliku lubnull
jeśli plik nie został zapisany.DeleteRememberedBrochurePath
Usuwa plik określony przez ścieżkę w zmiennej stronicowaniadeletedCategorysPdfPath
, jeślideletedCategorysPdfPath
nienull
jest .
Poniższy kod dla tych dwóch metod. Zwróć uwagę na podobieństwo między procedurą ProcessBrochureUpload
obsługi zdarzeń kontrolki DetailsView ItemInserting
z poprzedniego samouczka. W tym samouczku zaktualizowaliśmy procedury obsługi zdarzeń kontrolki DetailsView, aby korzystały z tych nowych metod. Pobierz kod skojarzony z tym samouczkiem, aby zobaczyć modyfikacje procedur obsługi zdarzeń kontrolki DetailsView.
private string ProcessBrochureUpload
(FileUpload BrochureUpload, out bool CancelOperation)
{
CancelOperation = false; // by default, do not cancel operation
if (BrochureUpload.HasFile)
{
// Make sure that a PDF has been uploaded
if (string.Compare(System.IO.Path.GetExtension(BrochureUpload.FileName),
".pdf", true) != 0)
{
UploadWarning.Text =
"Only PDF documents may be used for a category's brochure.";
UploadWarning.Visible = true;
CancelOperation = true;
return null;
}
const string BrochureDirectory = "~/Brochures/";
string brochurePath = BrochureDirectory + BrochureUpload.FileName;
string fileNameWithoutExtension =
System.IO.Path.GetFileNameWithoutExtension(BrochureUpload.FileName);
int iteration = 1;
while (System.IO.File.Exists(Server.MapPath(brochurePath)))
{
brochurePath = string.Concat(BrochureDirectory, fileNameWithoutExtension,
"-", iteration, ".pdf");
iteration++;
}
// Save the file to disk and set the value of the brochurePath parameter
BrochureUpload.SaveAs(Server.MapPath(brochurePath));
return brochurePath;
}
else
{
// No file uploaded
return null;
}
}
private void DeleteRememberedBrochurePath()
{
// Is there a file to delete?
if (deletedCategorysPdfPath != null)
{
System.IO.File.Delete(Server.MapPath(deletedCategorysPdfPath));
}
}
Programy obsługi zdarzeń i RowUpdated
GridView RowUpdating
używają ProcessBrochureUpload
metod iDeleteRememberedBrochurePath
, jak pokazano w poniższym kodzie:
protected void Categories_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
// Reference the RadioButtonList
RadioButtonList BrochureOptions =
(RadioButtonList)Categories.Rows[e.RowIndex].FindControl("BrochureOptions");
// Get BrochurePath information about the record being updated
int categoryID = Convert.ToInt32(e.Keys["CategoryID"]);
CategoriesBLL categoryAPI = new CategoriesBLL();
Northwind.CategoriesDataTable categories =
categoryAPI.GetCategoryByCategoryID(categoryID);
Northwind.CategoriesRow category = categories[0];
if (BrochureOptions.SelectedValue == "1")
{
// Use current value for BrochurePath
if (category.IsBrochurePathNull())
e.NewValues["brochurePath"] = null;
else
e.NewValues["brochurePath"] = category.BrochurePath;
}
else if (BrochureOptions.SelectedValue == "2")
{
// Remove the current brochure (set it to NULL in the database)
e.NewValues["brochurePath"] = null;
}
else if (BrochureOptions.SelectedValue == "3")
{
// Reference the BrochurePath FileUpload control
FileUpload BrochureUpload =
(FileUpload)Categories.Rows[e.RowIndex].FindControl("BrochureUpload");
// Process the BrochureUpload
bool cancelOperation = false;
e.NewValues["brochurePath"] =
ProcessBrochureUpload(BrochureUpload, out cancelOperation);
e.Cancel = cancelOperation;
}
else
{
// Unknown value!
throw new ApplicationException(
string.Format("Invalid BrochureOptions value, {0}",
BrochureOptions.SelectedValue));
}
if (BrochureOptions.SelectedValue == "2" ||
BrochureOptions.SelectedValue == "3")
{
// "Remember" that we need to delete the old PDF file
if (category.IsBrochurePathNull())
deletedCategorysPdfPath = null;
else
deletedCategorysPdfPath = category.BrochurePath;
}
}
protected void Categories_RowUpdated(object sender, GridViewUpdatedEventArgs e)
{
// If there were no problems and we updated the PDF file,
// then delete the existing one
if (e.Exception == null)
{
DeleteRememberedBrochurePath();
}
}
Zwróć uwagę, RowUpdating
jak program obsługi zdarzeń używa serii instrukcji warunkowych do wykonania odpowiedniej akcji na BrochureOptions
podstawie wartości właściwości RadioButtonList SelectedValue
.
Dzięki temu kodowi można edytować kategorię i korzystać z jej bieżącej broszury, nie używać broszury ani przekazywać nowej. Idź dalej i wypróbuj to. Ustaw punkty przerwania w RowUpdating
programach obsługi zdarzeń i RowUpdated
, aby poznać przepływ pracy.
Krok 7. Przekazywanie nowego obrazu
Picture
Interfejs edycji ImageField jest renderowany jako pole tekstowe wypełnione wartością z jego DataImageUrlField
właściwości. Podczas edytowania przepływu pracy kontrolka GridView przekazuje parametr do obiektu ObjectDataSource z parametrem o nazwie wartości właściwości ImageField DataImageUrlField
i wartości parametru wprowadzonej w polu tekstowym w interfejsie edycji. To zachowanie jest odpowiednie, gdy obraz jest zapisywany jako plik w systemie plików i DataImageUrlField
zawiera pełny adres URL obrazu. W takich okolicznościach interfejs edycji wyświetla adres URL obrazu w polu tekstowym, który użytkownik może zmienić i zapisać z powrotem do bazy danych. Ten interfejs domyślny nie zezwala użytkownikowi na przekazywanie nowego obrazu, ale umożliwia zmianę adresu URL obrazu z bieżącej wartości na inną. Jednak w tym samouczku domyślny interfejs edycji obiektu ImageField nie wystarczy, ponieważ Picture
dane binarne są przechowywane bezpośrednio w bazie danych, a DataImageUrlField
właściwość zawiera tylko CategoryID
wartość .
Aby lepiej zrozumieć, co się dzieje w naszym samouczku, gdy użytkownik edytuje wiersz za pomocą pola obrazu, rozważmy następujący przykład: użytkownik edytuje wiersz z CategoryID
wartością 10, co powoduje Picture
renderowanie pola obrazu jako pola tekstowego z wartością 10. Załóżmy, że użytkownik zmienia wartość w tym polu tekstowym na 50 i klika przycisk Aktualizuj. Następuje ogłaszanie zwrotne, a element GridView początkowo tworzy parametr o nazwie CategoryID
z wartością 50. Jednak przed wysłaniem tego parametru (i parametrów) CategoryName
Description
kontrolka GridView dodaje wartości z DataKeys
kolekcji. W związku z tym zastępuje CategoryID
parametr wartością podstawową CategoryID
bieżącego wiersza, 10. Krótko mówiąc, interfejs edytowania obiektu ImageField nie ma wpływu na przepływ pracy edycji dla tego samouczka, ponieważ nazwy właściwości ImageField DataImageUrlField
i wartości siatki DataKey
są takie same.
Chociaż pole obrazu ułatwia wyświetlanie obrazu na podstawie danych bazy danych, nie chcemy podawać pola tekstowego w interfejsie edycji. Zamiast tego chcemy zaoferować kontrolkę FileUpload, której użytkownik końcowy może użyć do zmiany obrazu kategorii. BrochurePath
W przeciwieństwie do wartości, w przypadku tych samouczków postanowiliśmy wymagać, aby każda kategoria miała obraz. W związku z tym nie musimy pozwolić użytkownikowi wskazać, że nie ma skojarzonego obrazu, który użytkownik może przekazać nowy obraz lub pozostawić bieżący obraz w stanie rzeczywistym.
Aby dostosować interfejs edycji imageField, musimy przekonwertować go na pole szablonu. W tagu inteligentnym GridView kliknij link Edytuj kolumny, wybierz pole obrazu, a następnie kliknij link Konwertuj to pole na pole szablonu.
Rysunek 16. Konwertowanie pola obrazu na pole szablonu
Przekonwertowanie pola obrazu na pole szablonu w ten sposób spowoduje wygenerowanie pola TemplateField z dwoma szablonami. Jak pokazano w poniższej składni deklaratywnej, element ItemTemplate
zawiera kontrolkę Sieci Web obrazów, której ImageUrl
właściwość jest przypisywana przy użyciu składni powiązania danych na podstawie właściwości ImageField.DataImageUrlField
DataImageUrlFormatString
Zawiera EditItemTemplate
właściwość TextBox, której Text
właściwość jest powiązana z wartością DataImageUrlField
określoną przez właściwość .
<asp:TemplateField>
<EditItemTemplate>
<asp:TextBox ID="TextBox1" runat="server"
Text='<%# Eval("CategoryID") %>'></asp:TextBox>
</EditItemTemplate>
<ItemTemplate>
<asp:Image ID="Image1" runat="server"
ImageUrl='<%# Eval("CategoryID",
"DisplayCategoryPicture.aspx?CategoryID={0}") %>' />
</ItemTemplate>
</asp:TemplateField>
Musimy zaktualizować element , EditItemTemplate
aby użyć kontrolki FileUpload. W tagu inteligentnym GridView kliknij link Edytuj szablony, a następnie wybierz pozycję Picture
TemplateField s EditItemTemplate
z listy rozwijanej. W szablonie powinien zostać wyświetlony element TextBox. Następnie przeciągnij kontrolkę FileUpload z przybornika do szablonu, ustawiając jej ID
wartość na PictureUpload
. Dodaj również tekst Aby zmienić obraz kategorii, określ nowy obraz. Aby zachować ten sam obraz kategorii, pozostaw pole puste również w szablonie.
Rysunek 17. Dodawanie kontrolki FileUpload do kontrolki EditItemTemplate
(Kliknij, aby wyświetlić obraz o pełnym rozmiarze)
Po dostosowaniu interfejsu edycji wyświetl postęp w przeglądarce. Podczas wyświetlania wiersza w trybie tylko do odczytu obraz kategorii jest wyświetlany tak, jak poprzednio, ale kliknięcie przycisku Edytuj renderuje kolumnę obrazu jako tekst z kontrolką FileUpload.
Rysunek 18. Interfejs edycji zawiera kontrolkę FileUpload (kliknij, aby wyświetlić obraz w pełnym rozmiarze)
Pamiętaj, że obiekt ObjectDataSource jest skonfigurowany do wywoływania CategoriesBLL
metody s UpdateCategory
klasy, która akceptuje jako dane binarne dla obrazu jako tablicę byte
. Jeśli jednak ta tablica null
ma wartość, jest wywoływane alternatywne UpdateCategory
przeciążenie, które wystawia instrukcję SQL, która nie modyfikuje Picture
UPDATE
kolumny, pozostawiając bieżący obraz kategorii nienaruszone. W związku z tym w programie obsługi zdarzeń gridView RowUpdating
musimy programowo odwołać PictureUpload
się do kontrolki FileUpload i określić, czy plik został przekazany. Jeśli go nie przekazano, nie chcemy określać wartości parametru picture
. Z drugiej strony, jeśli plik został przekazany w kontrolce PictureUpload
FileUpload, chcemy upewnić się, że jest to plik JPG. Jeśli tak jest, możemy wysłać jego zawartość binarną do obiektu ObjectDataSource za pomocą parametru picture
.
Podobnie jak w przypadku kodu używanego w kroku 6, większość potrzebnego tutaj kodu już istnieje w procedurze obsługi zdarzeń kontrolki ItemInserting
DetailsView. W związku z tym refaktoryzowałem typowe funkcje w nowej metodzie , ValidPictureUpload
i zaktualizowałem ItemInserting
procedurę obsługi zdarzeń w celu użycia tej metody.
Dodaj następujący kod na początku programu obsługi zdarzeń gridView RowUpdating
. Ważne jest, aby ten kod był wcześniejszy niż kod, który zapisuje plik broszury, ponieważ nie chcemy zapisywać broszury w systemie plików serwera internetowego, jeśli zostanie przekazany nieprawidłowy plik obrazu.
// Reference the PictureUpload FileUpload
FileUpload PictureUpload =
(FileUpload)Categories.Rows[e.RowIndex].FindControl("PictureUpload");
if (PictureUpload.HasFile)
{
// Make sure the picture upload is valid
if (ValidPictureUpload(PictureUpload))
{
e.NewValues["picture"] = PictureUpload.FileBytes;
}
else
{
// Invalid file upload, cancel update and exit event handler
e.Cancel = true;
return;
}
}
Metoda ValidPictureUpload(FileUpload)
przyjmuje kontrolkę FileUpload jako jedyny parametr wejściowy i sprawdza rozszerzenie przekazanego pliku, aby upewnić się, że przekazany plik jest plikiem JPG; jest wywoływany tylko wtedy, gdy plik obrazu zostanie przekazany. Jeśli plik nie zostanie przekazany, parametr obrazu nie zostanie ustawiony i w związku z tym używa jego wartości domyślnej .null
Jeśli obraz został przekazany i ValidPictureUpload
zwraca wartość , picture
parametr jest przypisany do danych binarnych przekazanego obrazu. Jeśli metoda zwróci false
wartość , przepływ pracy aktualizacji zostanie anulowany, a program obsługi zdarzeń true
zakończy działanie.
ValidPictureUpload(FileUpload)
Kod metody, który został refaktoryzowany z programu obsługi zdarzeń kontrolki ItemInserting
DetailsView, następuje:
private bool ValidPictureUpload(FileUpload PictureUpload)
{
// Make sure that a JPG has been uploaded
if (string.Compare(System.IO.Path.GetExtension(PictureUpload.FileName),
".jpg", true) != 0 &&
string.Compare(System.IO.Path.GetExtension(PictureUpload.FileName),
".jpeg", true) != 0)
{
UploadWarning.Text =
"Only JPG documents may be used for a category's picture.";
UploadWarning.Visible = true;
return false;
}
else
{
return true;
}
}
Krok 8. Zastępowanie oryginalnych obrazów kategorii obrazami JPG
Przypomnij sobie, że oryginalne osiem kategorii obrazów to pliki mapy bitowej opakowane w nagłówek OLE. Teraz, gdy dodaliśmy możliwość edytowania istniejącego obrazu rekordu, poświęć chwilę na zastąpienie tych map bitowych plikami JPG. Jeśli chcesz nadal używać bieżących obrazów kategorii, możesz przekonwertować je na pliki JPG, wykonując następujące kroki:
- Zapisz obrazy mapy bitowej na dysku twardym.
UpdatingAndDeleting.aspx
Odwiedź stronę w przeglądarce i dla każdej z pierwszych ośmiu kategorii kliknij prawym przyciskiem myszy obraz i wybierz opcję zapisania obrazu. - Otwórz obraz w wybranym edytorze obrazów. Możesz na przykład użyć Microsoft Paint.
- Zapisz mapę bitową jako obraz JPG.
- Zaktualizuj obraz kategorii za pomocą interfejsu edycji przy użyciu pliku JPG.
Po edytowaniu kategorii i przekazaniu obrazu JPG obraz nie będzie renderowany w przeglądarce, ponieważ DisplayCategoryPicture.aspx
strona usuwa pierwsze 78 bajtów ze zdjęć z pierwszych ośmiu kategorii. Rozwiąż ten problem, usuwając kod wykonujący usuwanie paska nagłówka OLE. Po wykonaniu DisplayCategoryPicture.aspx``Page_Load
tej czynności program obsługi zdarzeń powinien mieć tylko następujący kod:
protected void Page_Load(object sender, EventArgs e)
{
int categoryID = Convert.ToInt32(Request.QueryString["CategoryID"]);
// Get information about the specified category
CategoriesBLL categoryAPI = new CategoriesBLL();
Northwind.CategoriesDataTable categories = _
categoryAPI.GetCategoryWithBinaryDataByCategoryID(categoryID);
Northwind.CategoriesRow category = categories[0];
// For new categories, images are JPGs...
// Output HTTP headers providing information about the binary data
Response.ContentType = "image/jpeg";
// Output the binary data
Response.BinaryWrite(category.Picture);
}
Uwaga
Wstawianie UpdatingAndDeleting.aspx
i edytowanie interfejsów strony może nieco bardziej pracować. Pola CategoryName
i Description
w kontrolkach DetailsView i GridView powinny być konwertowane na pola szablonów. Ponieważ CategoryName
nie zezwala na NULL
wartości, należy dodać element RequiredFieldValidator. Description
Pole TextBox powinno być prawdopodobnie przekonwertowane na wielowierszową kontrolkę TextBox. Zostawiam te wykończenia jako ćwiczenie dla Ciebie.
Podsumowanie
Ten samouczek kończy pracę z danymi binarnymi. W tym samouczku i poprzednich trzech pokazano, jak można przechowywać dane binarne w systemie plików lub bezpośrednio w bazie danych. Użytkownik udostępnia systemowi dane binarne, wybierając plik z dysku twardego i przekazując go do serwera internetowego, gdzie może być przechowywany w systemie plików lub wstawiony do bazy danych. ASP.NET 2.0 zawiera kontrolkę FileUpload, która zapewnia taki interfejs tak łatwo, jak przeciąganie i upuszczanie. Jednak, jak wspomniano w samouczku Przekazywanie plików , kontrolka FileUpload jest odpowiednia tylko dla stosunkowo małych plików przekazywanych, najlepiej nie przekraczając megabajtów. Omówiliśmy również sposób kojarzenia przekazanych danych z bazowym modelem danych, a także jak edytować i usuwać dane binarne z istniejących rekordów.
W następnym zestawie samouczków przedstawiono różne techniki buforowania. Buforowanie zapewnia metodę poprawy ogólnej wydajności aplikacji przez pobranie wyników z kosztownych operacji i zapisanie ich w lokalizacji, do którego można szybciej uzyskać dostęp.
Szczęśliwe programowanie!
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ła Teresa Murphy. Chcesz przejrzeć nadchodzące artykuły MSDN? Jeśli tak, upuść mi wiersz pod adresemmitchell@4GuysFromRolla.com .