Aggiornamento ed eliminazione di dati binari esistenti (C#)

di Scott Mitchell

Scarica il PDF

Nelle esercitazioni precedenti è stato illustrato come il controllo GridView semplifica la modifica ed eliminazione dei dati di testo. In questa esercitazione viene illustrato come il controllo GridView consente anche di modificare ed eliminare i dati binari, se i dati binari vengono salvati nel database o archiviati nel file system.

Introduzione

Negli ultimi tre esercitazioni abbiamo aggiunto un po' di funzionalità per l'uso dei dati binari. È stata avviata l'aggiunta di una BrochurePath colonna alla Categories tabella e l'architettura è stata aggiornata di conseguenza. Sono stati aggiunti anche metodi livello di accesso ai dati e livello di logica di business per usare la colonna esistente Picture della tabella Categorie, che contiene il contenuto binario di un file di immagine. Sono state compilate pagine Web per presentare i dati binari in un collegamento di download di GridView per la brochure, con l'immagine della categoria illustrata in un <img> elemento e sono stati aggiunti dettagliView per consentire agli utenti di aggiungere una nuova categoria e caricare i dati della brochure e dell'immagine.

Tutto ciò che rimane da implementare è la possibilità di modificare ed eliminare le categorie esistenti, che verranno eseguite in questa esercitazione usando la modifica predefinita di GridView e l'eliminazione delle funzionalità. Quando si modifica una categoria, l'utente sarà in grado di caricare facoltativamente una nuova immagine o di continuare a usare quello esistente. Per la brochure, possono scegliere di usare la brochure esistente, per caricare una nuova brochure o per indicare che la categoria non ha più una brochure associata. Iniziamo!

Passaggio 1: Aggiornamento del livello di accesso ai dati

Dal ha generato automaticamente , Updatee metodi, ma questi metodi sono stati generati Insertin base alla query principale della classe, che non include la PictureCategoriesTableAdapterDelete colonna. Pertanto, i Insert metodi e Update non includono parametri per specificare i dati binari per l'immagine della categoria. Come illustrato nell'esercitazione precedente, è necessario creare un nuovo metodo TableAdapter per aggiornare la Categories tabella quando si specificano i dati binari.

Aprire Typed DataSet e, dal Designer, fare clic con il pulsante destro del mouse CategoriesTableAdapter sull'intestazione s e scegliere Aggiungi query dal menu di scelta rapida per avviare la Configurazione guidata query TableAdapter. Questa procedura guidata inizia chiedendoci come deve accedere al database la query TableAdapter. Scegliere Usa istruzioni SQL e fare clic su Avanti. Il passaggio successivo richiede la generazione del tipo di query. Poiché si sta creando una query per aggiungere un nuovo record alla Categories tabella, scegliere UPDATE e fare clic su Avanti.

Selezionare l'opzione UPDATE

Figura 1: Selezionare l'opzione UPDATE (Fare clic per visualizzare l'immagine full-size)

È ora necessario specificare l'istruzione UPDATE SQL. La procedura guidata suggerisce automaticamente un'istruzione UPDATE corrispondente alla query principale di TableAdapter (una che aggiorna i CategoryNamevalori , Descriptione BrochurePath ). Modificare l'istruzione in modo che la Picture colonna sia inclusa insieme a un @Picture parametro, ad esempio:

UPDATE [Categories] SET 
    [CategoryName] = @CategoryName, 
    [Description] = @Description, 
    [BrochurePath] = @BrochurePath ,
    [Picture] = @Picture
WHERE (([CategoryID] = @Original_CategoryID))

La schermata finale della procedura guidata chiede di assegnare un nome al nuovo metodo TableAdapter. Immettere UpdateWithPicture e fare clic su Fine.

Assegnare un nome al nuovo metodo TableAdapter UpdateWithPicture

Figura 2: Assegnare un nome al nuovo metodo UpdateWithPicture TableAdapter (Fare clic per visualizzare l'immagine full-size)

Passaggio 2: Aggiunta dei metodi del livello di logica di business

Oltre all'aggiornamento di DAL, è necessario aggiornare il BLL per includere metodi per l'aggiornamento e l'eliminazione di una categoria. Questi sono i metodi che verranno richiamati dal livello presentazione.

Per eliminare una categoria, è possibile usare il CategoriesTableAdapter metodo generato Delete automaticamente. Aggiungere il metodo seguente alla classe 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;
}

Per questa esercitazione, consente di creare due metodi per aggiornare una categoria, una che prevede i dati dell'immagine binaria e richiama il metodo appena aggiunto all'oggetto CategoriesTableAdapter e un altro che accetta solo l'istruzione , Descriptione BrochurePath e usa CategoriesTableAdapter l'istruzione UpdateWithPicture generata Update automaticamente dalla CategoryNameclasse. La logica dietro l'uso di due metodi è che in alcune circostanze, un utente potrebbe voler aggiornare l'immagine della categoria insieme ai relativi altri campi, in cui l'utente dovrà caricare la nuova immagine. I dati binari dell'immagine caricati possono quindi essere usati nell'istruzione UPDATE . In altri casi, l'utente potrebbe essere interessato solo all'aggiornamento, ad esempio il nome e la descrizione. Tuttavia, se l'istruzione UPDATE prevede anche i dati binari per la Picture colonna, è necessario fornire tali informazioni. Ciò richiederebbe un viaggio aggiuntivo nel database per ripristinare i dati dell'immagine per il record da modificare. Pertanto, vogliamo due UPDATE metodi. Il livello di logica di business determina quale usare in base al fatto che i dati dell'immagine vengano forniti durante l'aggiornamento della categoria.

Per facilitare questa operazione, aggiungere due metodi alla CategoriesBLL classe, entrambi denominati UpdateCategory. Il primo deve accettare tre s, una byte matrice e un come int parametri di input; il secondo, solo tre stringstring s e un int. I string parametri di input sono per il nome, la descrizione e il percorso del file della brochure della categoria, la byte matrice è per il contenuto binario dell'immagine della categoria e l'identifica int l'oggetto CategoryID del record da aggiornare. Si noti che il primo overload richiama il secondo se la matrice passata byte è 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;
}

Passaggio 3: Copia sulla funzionalità Inserimento e visualizzazione

Nell'esercitazione precedente è stata creata una pagina denominata UploadInDetailsView.aspx che elenca tutte le categorie in GridView e ha fornito un oggetto DetailsView per aggiungere nuove categorie al sistema. In questa esercitazione verrà esteso GridView per includere la modifica e l'eliminazione del supporto. Anziché continuare a funzionare da UploadInDetailsView.aspx, consentire invece di posizionare le modifiche di questa esercitazione nella UpdatingAndDeleting.aspx pagina dalla stessa cartella, ~/BinaryData. Copiare e incollare il markup dichiarativo e il codice da UploadInDetailsView.aspx a UpdatingAndDeleting.aspx.

Iniziare aprendo la UploadInDetailsView.aspx pagina. Copiare tutte le sintassi dichiarative all'interno dell'elemento <asp:Content> , come illustrato nella figura 3. Aprire UpdatingAndDeleting.aspx e incollare quindi questo markup all'interno del relativo <asp:Content> elemento. Analogamente, copiare il codice dalla classe code-behind della UploadInDetailsView.aspx pagina a UpdatingAndDeleting.aspx.

Copiare il markup dichiarativo da UploadInDetailsView.aspx

Figura 3: Copiare il markup dichiarativo da UploadInDetailsView.aspx (Fare clic per visualizzare l'immagine full-size)

Dopo aver copiato il markup dichiarativo e il codice, visitare UpdatingAndDeleting.aspx. Verrà visualizzato lo stesso output e si avrà la stessa esperienza utente con UploadInDetailsView.aspx la pagina dell'esercitazione precedente.

Passaggio 4: Aggiunta del supporto per ObjectDataSource e GridView

Come illustrato di nuovo nell'esercitazione Panoramica dell'inserimento, dell'aggiornamento e dell'eliminazione dei dati , GridView offre funzionalità di eliminazione predefinite e queste funzionalità possono essere abilitate al segno di spunta di una casella di controllo se l'origine dati sottostante della griglia supporta l'eliminazione. Attualmente ObjectDataSource, GridView è associato a (CategoriesDataSource) non supporta l'eliminazione.

Per risolvere questo problema, fare clic sull'opzione Configura origine dati dallo smart tag di ObjectDataSource per avviare la procedura guidata. La prima schermata mostra che ObjectDataSource è configurato per l'uso della CategoriesBLL classe. Fare clic su Avanti. Attualmente vengono specificate solo le proprietà e SelectMethod s InsertMethod ObjectDataSource. Tuttavia, la procedura guidata ha popolato automaticamente gli elenchi a discesa nelle schede UPDATE e DELETE rispettivamente con i UpdateCategory metodi e DeleteCategory . Questo è dovuto al fatto che nella classe sono stati contrassegnati questi metodi usando come metodi predefiniti per l'aggiornamento e l'eliminazione CategoriesBLLDataObjectMethodAttribute .

Per il momento, impostare l'elenco a discesa UPDATE tabulazioni su (Nessuno), ma lasciare l'elenco a discesa DELETE tabulazioni impostato su DeleteCategory. Verrà restituita questa procedura guidata nel passaggio 6 per aggiungere il supporto per l'aggiornamento.

Configurare ObjectDataSource per usare il metodo DeleteCategory

Figura 4: Configurare ObjectDataSource per usare il metodo (fare clic per visualizzare l'immagineDeleteCategory a dimensioni complete)

Nota

Al termine della procedura guidata, Visual Studio può chiedere se si vogliono aggiornare campi e chiavi, che rigenerano i campi dei controlli Web dei dati. Scegliere No, perché scegliere Sì sovrascriverà le personalizzazioni dei campi apportate.

ObjectDataSource includerà ora un valore per la relativa DeleteMethod proprietà e un DeleteParameteroggetto . Tenere presente che quando si usa la procedura guidata per specificare i metodi, Visual Studio imposta la proprietà ObjectDataSource s OldValuesParameterFormatString su original_{0}, che causa problemi con le chiamate al metodo update ed delete. Pertanto, deselezionare completamente questa proprietà o reimpostarla nel valore predefinito, {0}. Se è necessario aggiornare la memoria in questa proprietà ObjectDataSource, vedere l'esercitazione Panoramica dell'inserimento, dell'aggiornamento e dell'eliminazione dei dati .

Dopo aver completato la procedura guidata e corretto OldValuesParameterFormatString, il markup dichiarativo di ObjectDataSource deve essere simile al seguente:

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

Dopo aver configurato ObjectDataSource, aggiungere funzionalità di eliminazione a GridView selezionando la casella di controllo Abilita eliminazione dallo smart tag di GridView. Verrà aggiunto un CommandField a GridView la cui ShowDeleteButton proprietà è impostata su true.

Abilitare il supporto per l'eliminazione in GridView

Figura 5: Abilitare il supporto per l'eliminazione in GridView (Fare clic per visualizzare l'immagine full-size)

Per testare la funzionalità di eliminazione, prendere un momento. È presente una chiave esterna tra la tabella s e la ProductsCategories tabella s CategoryIDCategoryID , quindi si otterrà un'eccezione di violazione del vincolo di chiave esterna se si tenta di eliminare una delle prime otto categorie. Per testare questa funzionalità, aggiungere una nuova categoria, fornendo una brochure e un'immagine. La categoria di test, illustrata nella figura 6, include un file di brochure di test denominato Test.pdf e un'immagine di test. La figura 7 mostra GridView dopo l'aggiunta della categoria di test.

Aggiungere una categoria di test con una brochure e un'immagine

Figura 6: Aggiungere una categoria di test con una brochure e un'immagine (fare clic per visualizzare un'immagine full-size)

Dopo aver inserito la categoria di test, viene visualizzata in GridView

Figura 7: Dopo aver inserito la categoria di test, viene visualizzata in GridView (Fare clic per visualizzare l'immagine a dimensioni complete)

In Visual Studio aggiornare il Esplora soluzioni. Verrà ora visualizzato un nuovo file nella ~/Brochures cartella ( Test.pdf vedere la figura 8).

Fare quindi clic sul collegamento Elimina nella riga Categoria di test, causando il postback della pagina e il CategoriesBLL metodo della DeleteCategory classe. Verrà richiamato il metodo DAL Delete , causando l'invio dell'istruzione appropriata DELETE al database. I dati vengono quindi rimbalzato in GridView e il markup viene inviato nuovamente al client con la categoria di test non più presente.

Anche se il flusso di lavoro di eliminazione ha rimosso il record categoria di test dalla Categories tabella, non ha rimosso il file brochure dal file system del server Web. Aggiornare il Esplora soluzioni e si noterà che Test.pdf è ancora seduto nella ~/Brochures cartella.

Il file Test.pdf non è stato eliminato dal file system del server Web

Figura 8: Il Test.pdf file non è stato eliminato dal file system del server Web

Passaggio 5: Rimozione del file brochure della categoria eliminata

Uno degli aspetti negativi dell'archiviazione di dati binari esterni al database è che è necessario eseguire ulteriori passaggi per pulire questi file quando il record di database associato viene eliminato. GridView e ObjectDataSource forniscono eventi che vengono attivati sia prima che dopo l'esecuzione del comando delete. In realtà è necessario creare gestori eventi per gli eventi di pre-azione e post-azione. Prima che il Categories record venga eliminato, è necessario determinare il percorso del file PDF, ma non si vuole eliminare il PDF prima che la categoria venga eliminata in caso di eccezione e la categoria non viene eliminata.

L'evento gridView viene RowDeleting generato prima che sia stato richiamato il comando di eliminazione di ObjectDataSource, mentre il relativo RowDeleted evento viene generato dopo. Creare gestori eventi per questi due eventi usando il codice seguente:

// 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));
        }
    }
}

RowDeleting Nel gestore eventi, l'oggetto CategoryID della riga da eliminare viene afferrato dalla raccolta di DataKeys GridView, a cui è possibile accedere in questo gestore eventi tramite la e.Keys raccolta. Successivamente, la CategoriesBLL classe s GetCategoryByCategoryID(categoryID) viene richiamata per restituire informazioni sul record da eliminare. Se l'oggetto restituito ha un valore diversoNULL``BrochurePath da , viene archiviato CategoriesDataRow nella variabile deletedCategorysPdfPath di pagina in modo che il file possa essere eliminato nel RowDeleted gestore eventi.

Nota

Invece di recuperare i BrochurePath dettagli per il Categories record da eliminare nel RowDeleting gestore eventi, è possibile aggiungere in alternativa alla BrochurePath proprietà gridView s DataKeyNames e accedere al valore del record tramite la e.Keys raccolta. In questo modo si aumentano leggermente le dimensioni dello stato di visualizzazione di GridView, ma si riduce la quantità di codice necessaria e si salva un viaggio nel database.

Dopo aver richiamato il comando di eliminazione sottostante di ObjectDataSource, viene generato il gestore eventi di RowDeleted GridView. Se non sono state rilevate eccezioni nell'eliminazione dei dati e esiste un valore per deletedCategorysPdfPath, il PDF viene eliminato dal file system. Si noti che questo codice aggiuntivo non è necessario per pulire i dati binari della categoria associati alla relativa immagine. Ciò è dovuto al fatto che i dati immagine vengono archiviati direttamente nel database, pertanto l'eliminazione della Categories riga comporta anche l'eliminazione dei dati immagine della categoria.

Dopo aver aggiunto i due gestori eventi, eseguire di nuovo questo test case. Quando si elimina la categoria, viene eliminato anche il pdf associato.

L'aggiornamento di dati binari associati a un record esistente offre alcune problematiche interessanti. La parte restante di questa esercitazione illustra come aggiungere funzionalità di aggiornamento alla brochure e all'immagine. Il passaggio 6 esplora le tecniche per aggiornare le informazioni sulla brochure mentre il passaggio 7 esamina l'aggiornamento dell'immagine.

Passaggio 6: Aggiornamento della brochure di una categoria

Come illustrato nell'esercitazione Panoramica dell'inserimento, dell'aggiornamento e dell'eliminazione dei dati , GridView offre il supporto predefinito per la modifica a livello di riga che può essere implementato dal segno di spunta di una casella di controllo se l'origine dati sottostante è configurata in modo appropriato. Attualmente ObjectDataSource CategoriesDataSource non è ancora configurato per includere il supporto per l'aggiornamento, quindi consentire di aggiungerlo.

Fare clic sul collegamento Configura origine dati dalla procedura guidata ObjectDataSource e procedere con il secondo passaggio. A causa dell'oggetto DataObjectMethodAttribute usato in CategoriesBLL, l'elenco a discesa UPDATE deve essere popolato automaticamente con l'overload UpdateCategory che accetta quattro parametri di input (per tutte le colonne ma Picture). Modificare questa impostazione in modo che usi l'overload con cinque parametri.

Configurare ObjectDataSource per l'utilizzo del metodo UpdateCategory che include un parametro per Picture

Figura 9: Configurare ObjectDataSource per l'uso del UpdateCategory metodo che include un parametro per Picture (fare clic per visualizzare l'immagine a dimensione intera)

ObjectDataSource includerà ora un valore per la relativa UpdateMethod proprietà, nonché gli oggetti corrispondenti UpdateParameter . Come indicato nel passaggio 4, Visual Studio imposta la proprietà original_{0} ObjectDataSource su OldValuesParameterFormatString quando si usa la procedura guidata Configura origine dati. Ciò causerà problemi con le chiamate al metodo update ed delete. Di conseguenza, cancellare completamente questa proprietà o reimpostarla sul valore predefinito, {0}.

Dopo aver completato la procedura guidata e corretto , OldValuesParameterFormatStringil markup dichiarativo di ObjectDataSource dovrebbe essere simile al seguente:

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

Per attivare le funzionalità di modifica predefinite di GridView, selezionare l'opzione Abilita modifica dallo smart tag di GridView. Verrà impostata la proprietà trueCommandField su ShowEditButton , con conseguente aggiunta di un pulsante Modifica (e pulsanti Aggiorna e Annulla per la riga da modificare).

Configurare GridView per supportare la modifica

Figura 10: Configurare GridView per supportare la modifica (fare clic per visualizzare l'immagine a dimensione intera)

Visitare la pagina tramite un browser e fare clic su uno dei pulsanti Modifica della riga. Il rendering di CategoryName e Description BoundFields viene eseguito come caselle di testo. TemplateField BrochurePath non dispone di un oggetto EditItemTemplate, quindi continua a mostrare ItemTemplate il relativo collegamento alla brochure. Il Picture rendering di ImageField viene eseguito come textBox la cui Text proprietà è assegnata al valore di ImageField DataImageUrlField , in questo caso CategoryID.

GridView non dispone di un'interfaccia di modifica per BrochurePath

Figura 11: GridView non dispone di un'interfaccia di modifica per BrochurePath (fare clic per visualizzare l'immagine a dimensione intera)

Personalizzazione dell'interfacciaBrochurePathdi modifica

È necessario creare un'interfaccia di modifica per BrochurePath TemplateField, una che consente all'utente di:

  • Lasciare invariata la brochure della categoria,
  • Aggiornare la brochure della categoria caricando una nuova brochure o
  • Rimuovere completamente la brochure della categoria (nel caso in cui la categoria non abbia più una brochure associata).

È anche necessario aggiornare l'interfaccia Picture di modifica di ImageField, ma verrà visualizzata nel passaggio 7.

Dallo smart tag gridView fare clic sul collegamento Modifica modelli e selezionare TemplateField BrochurePath dall'elenco EditItemTemplate a discesa. Aggiungere un controllo Web RadioButtonList a questo modello, impostandone la ID proprietà su BrochureOptions e la relativa AutoPostBack proprietà su true. Dalla Finestra Proprietà fare clic sui puntini di sospensione nella Items proprietà , che visualizzerà il ListItem Editor Collection. Aggiungere le tre opzioni seguenti rispettivamente con Value s 1, 2 e 3:

  • Utilizzare la brochure corrente
  • Rimuovere la brochure corrente
  • Caricare una nuova brochure

Impostare la prima ListItem proprietà s Selected su true.

Aggiungere Tre ListItems a RadioButtonList

Figura 12: Aggiungere tre ListItem s a RadioButtonList

Sotto RadioButtonList aggiungere un controllo FileUpload denominato BrochureUpload. Impostare la relativa Visible proprietà su false.

Aggiungere un controllo RadioButtonList e FileUpload a EditItemTemplate

Figura 13: Aggiungere un controllo RadioButtonList e FileUpload a (fare clic per visualizzare l'immagineEditItemTemplate a dimensione intera)

Questo Oggetto RadioButtonList offre le tre opzioni per l'utente. L'idea è che il controllo FileUpload verrà visualizzato solo se è selezionata l'ultima opzione Carica nuova brochure. A tale scopo, creare un gestore eventi per l'evento RadioButtonList SelectedIndexChanged e aggiungere il codice seguente:

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");
}

Poiché i controlli RadioButtonList e FileUpload si trovano all'interno di un modello, è necessario scrivere un po' di codice per accedere a questi controlli a livello di codice. Al SelectedIndexChanged gestore eventi viene passato un riferimento di RadioButtonList nel sender parametro di input. Per ottenere il controllo FileUpload, è necessario ottenere il controllo padre di RadioButtonList e usare il FindControl("controlID") metodo da questa posizione. Dopo aver ottenuto un riferimento ai controlli RadioButtonList e FileUpload, la proprietà del Visible controllo FileUpload è impostata su true solo se RadioButtonList è SelectedValue uguale a 3, ovvero per Value la nuova brochure ListItemUpload .

Con questo codice sul posto, testare l'interfaccia di modifica. Fare clic sul pulsante Modifica per una riga. Inizialmente, è consigliabile selezionare l'opzione Usa brochure corrente. La modifica dell'indice selezionato causa un postback. Se è selezionata la terza opzione, viene visualizzato il controllo FileUpload, altrimenti è nascosto. La figura 14 mostra l'interfaccia di modifica quando si fa clic per la prima volta il pulsante Modifica; La figura 15 mostra l'interfaccia dopo l'opzione Carica nuova brochure.

Inizialmente, l'opzione Usa brochure corrente è selezionata

Figura 14: Inizialmente, l'opzione Usa brochure corrente è selezionata (fare clic per visualizzare l'immagine a dimensione intera)

Scegliendo l'opzione Carica nuova brochure viene visualizzato il controllo FileUpload

Figura 15: Scelta dell'opzione Carica nuova brochure Visualizza il controllo FileUpload (fare clic per visualizzare l'immagine a dimensione intera)

Salvataggio del file della brochure e aggiornamento dellaBrochurePathcolonna

Quando si fa clic sul pulsante Update di GridView, viene generato il relativo RowUpdating evento. Il comando di aggiornamento di ObjectDataSource viene richiamato e quindi viene generato l'evento gridView.RowUpdated Analogamente al flusso di lavoro di eliminazione, è necessario creare gestori eventi per entrambi questi eventi. RowUpdating Nel gestore eventi è necessario determinare l'azione da eseguire in base all'oggetto SelectedValueBrochureOptions di RadioButtonList:

  • Se è SelectedValue 1, si vuole continuare a usare la stessa BrochurePath impostazione. È quindi necessario impostare il parametro ObjectDataSource sul brochurePath valore esistente BrochurePath del record da aggiornare. Il parametro objectDataSource può brochurePath essere impostato usando e.NewValues["brochurePath"] = value.
  • Se è SelectedValue 2, si vuole impostare il valore NULLdel record su BrochurePath . Questa operazione può essere eseguita impostando il parametro ObjectDataSource su brochurePath , che comporta l'uso di un database NULL nell'istruzione UPDATE .Nothing Se è presente un file di brochure esistente che viene rimosso, è necessario eliminare il file esistente. Tuttavia, si vuole eseguire questa operazione solo se l'aggiornamento viene completato senza generare un'eccezione.
  • Se è SelectedValue 3, si vuole assicurarsi che l'utente abbia caricato un file PDF e quindi salvarlo nel file system e aggiornare il valore della colonna del BrochurePath record. Inoltre, se è presente un file di brochure esistente che viene sostituito, è necessario eliminare il file precedente. Tuttavia, si vuole eseguire questa operazione solo se l'aggiornamento viene completato senza generare un'eccezione.

I passaggi necessari per essere completati quando radioButtonList s SelectedValue è 3 sono virtualmente identici a quelli usati dal gestore eventi DetailsView.ItemInserting Questo gestore eventi viene eseguito quando viene aggiunto un nuovo record di categoria dal controllo DetailsView aggiunto nell'esercitazione precedente. Pertanto, viene eseguito il refactoring di questa funzionalità in metodi separati. In particolare, ho spostato la funzionalità comune in due metodi:

  • ProcessBrochureUpload(FileUpload, out bool) accetta come input un'istanza del controllo FileUpload e un valore booleano di output che specifica se l'operazione di eliminazione o modifica deve procedere o se deve essere annullata a causa di un errore di convalida. Questo metodo restituisce il percorso del file salvato o null se non è stato salvato alcun file.
  • DeleteRememberedBrochurePath elimina il file specificato dal percorso nella variabile deletedCategorysPdfPath di pagina se deletedCategorysPdfPath non nullè .

Il codice per questi due metodi segue. Si noti la somiglianza tra ProcessBrochureUpload e il gestore eventi DetailsView dell'esercitazione ItemInserting precedente. In questa esercitazione sono stati aggiornati i gestori eventi detailsView per usare questi nuovi metodi. Scaricare il codice associato a questa esercitazione per visualizzare le modifiche ai gestori eventi 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));
    }
}

I gestori eventi e s RowUpdating e RowUpdated GridView usano i ProcessBrochureUpload metodi e DeleteRememberedBrochurePath , come illustrato nel codice seguente:

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

Si noti come il RowUpdating gestore eventi usa una serie di istruzioni condizionali per eseguire l'azione appropriata in base al BrochureOptions valore della proprietà RadioButtonList SelectedValue .

Con questo codice sul posto, è possibile modificare una categoria e usarla con la sua brochure corrente, non usare alcuna brochure o caricarne una nuova. Vai avanti e provalo. Impostare i punti di interruzione nei RowUpdating gestori di eventi e RowUpdated per ottenere un'idea del flusso di lavoro.

Passaggio 7: Caricamento di una nuova immagine

L'interfaccia Picture di modifica di ImageField esegue il rendering come casella di testo popolata con il valore della relativa DataImageUrlField proprietà. Durante il flusso di lavoro di modifica, GridView passa un parametro a ObjectDataSource con il nome del parametro il valore della proprietà ImageField DataImageUrlField e il valore del parametro il valore immesso nella casella di testo nell'interfaccia di modifica. Questo comportamento è adatto quando l'immagine viene salvata come file nel file system e contiene DataImageUrlField l'URL completo dell'immagine. Con tali circostanze, l'interfaccia di modifica visualizza l'URL dell'immagine nella casella di testo, che l'utente può modificare e aver salvato nuovamente nel database. Concesso, questa interfaccia predefinita non consente all'utente di caricare una nuova immagine, ma consente loro di modificare l'URL dell'immagine dal valore corrente a un altro. Per questa esercitazione, tuttavia, l'interfaccia di modifica predefinita di ImageField non è sufficiente perché i Picture dati binari vengono archiviati direttamente nel database e la DataImageUrlField proprietà contiene solo .CategoryID

Per comprendere meglio cosa accade nell'esercitazione quando un utente modifica una riga con imageField, prendere in considerazione l'esempio seguente: un utente modifica una riga con CategoryID 10, causando il Picture rendering di ImageField come casella di testo con il valore 10. Si supponga che l'utente modifica il valore in questa casella di testo su 50 e fa clic sul pulsante Aggiorna. Si verifica un postback e GridView crea inizialmente un parametro denominato CategoryID con il valore 50. Tuttavia, prima che GridView invii questo parametro (e i CategoryName parametri e Description ), aggiunge i valori della DataKeys raccolta. Pertanto, sovrascrive il parametro con il CategoryID valore sottostante CategoryID della riga corrente, 10. In breve, l'interfaccia di modifica di ImageField non ha alcun effetto sul flusso di lavoro di modifica per questa esercitazione perché i nomi della proprietà ImageField e DataImageUrlField il valore della DataKey griglia sono uno nello stesso.

Anche se ImageField semplifica la visualizzazione di un'immagine in base ai dati del database, non si vuole fornire una casella di testo nell'interfaccia di modifica. Invece, si vuole offrire un controllo FileUpload che l'utente finale può usare per modificare l'immagine della categoria. A differenza del BrochurePath valore, per queste esercitazioni abbiamo deciso di richiedere che ogni categoria debba avere un'immagine. Pertanto, non è necessario consentire all'utente di indicare che non esiste un'immagine associata che l'utente può caricare una nuova immagine o lasciare l'immagine corrente così come è.

Per personalizzare l'interfaccia di modifica di ImageField, è necessario convertirla in un oggetto TemplateField. Nella smart tag di GridView fare clic sul collegamento Modifica colonne, selezionare ImageField e fare clic sul collegamento Convert this field in a TemplateField .From the GridView s smart tag (Modifica colonne), selezionare ImageField e fare clic sul collegamento Convert this field in a TemplateField (Converti questo campo in un campo Modello).

Convertire ImageField in un modelloField

Figura 16: Convertire ImageField in un modelloField

La conversione di ImageField in un modelloField in questo modo genera un modelloField con due modelli. Come illustrato nella sintassi dichiarativa seguente, contiene ItemTemplate un controllo Web image la cui ImageUrl proprietà viene assegnata usando la sintassi di associazione dati in base alle proprietà e DataImageUrlFormatString a ImageFieldDataImageUrlField. Contiene EditItemTemplate una Casella di testo la cui Text proprietà è associata al valore specificato dalla DataImageUrlField proprietà .

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

È necessario aggiornare l'oggetto EditItemTemplate per usare un controllo FileUpload. Dal smart tag gridView fare clic sul collegamento Modifica modelli e quindi selezionare Picture TemplateField nell'elenco EditItemTemplate a discesa. Nel modello dovrebbe essere visualizzato un controllo TextBox. Trascinare quindi un controllo FileUpload dalla casella degli strumenti nel modello, impostandone su IDPictureUpload. Aggiungere anche il testo Per modificare l'immagine della categoria, specificare una nuova immagine. Per mantenere la stessa immagine della categoria, lasciare vuoto anche il campo al modello.

Aggiungere un controllo FileUpload all'oggetto EditItemTemplate

Figura 17: Aggiungere un controllo FileUpload all'oggetto EditItemTemplate (fare clic per visualizzare l'immagine full-size)

Dopo aver personalizzato l'interfaccia di modifica, visualizzare lo stato di avanzamento in un browser. Quando si visualizza una riga in modalità di sola lettura, l'immagine della categoria viene visualizzata come era prima, ma facendo clic sul pulsante Modifica viene eseguita il rendering della colonna immagine come testo con un controllo FileUpload.

L'interfaccia di modifica include un controllo FileUpload

Figura 18: l'interfaccia di modifica include un controllo FileUpload (fare clic per visualizzare l'immagine full-size)

Si ricordi che ObjectDataSource è configurato per chiamare il CategoriesBLL metodo della UpdateCategory classe che accetta come input i dati binari per l'immagine come byte matrice. Se questa matrice ha un null valore, tuttavia, viene chiamato l'overload alternativo UpdateCategory , che genera l'istruzione UPDATE SQL che non modifica la colonna, lasciando quindi intatta l'immagine Picture corrente della categoria. Pertanto, nel gestore eventi di RowUpdating GridView è necessario fare riferimento a livello di codice al PictureUpload controllo FileUpload e determinare se è stato caricato un file. Se non è stato caricato, non si vuole specificare un valore per il picture parametro. D'altra parte, se un file è stato caricato nel PictureUpload controllo FileUpload, si vuole assicurarsi che sia un file JPG. In caso affermativo, è possibile inviare il contenuto binario all'oggetto ObjectDataSource tramite il picture parametro .

Come nel codice usato nel passaggio 6, gran parte del codice necessario esiste già nel gestore eventi detailsView.ItemInserting Di conseguenza, ho refactorato la funzionalità comune in un nuovo metodo, ValidPictureUploade aggiornato il ItemInserting gestore eventi per usare questo metodo.

Aggiungere il codice seguente all'inizio del gestore eventi gridView s RowUpdating . È importante che questo codice venga eseguito prima del codice che salva il file della brochure perché non si vuole salvare la brochure nel file system del server Web se viene caricato un file immagine non valido.

// 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;
    }
}

Il ValidPictureUpload(FileUpload) metodo accetta un controllo FileUpload come parametro di input unico e controlla l'estensione del file caricato per assicurarsi che il file caricato sia un file JPG. Viene chiamato solo se viene caricato un file immagine. Se non viene caricato alcun file, il parametro immagine non è impostato e quindi usa il valore predefinito di null. Se un'immagine è stata caricata e ValidPictureUpload restituisce , il picture parametro viene assegnato ai dati binari dell'immagine caricata. Se il metodo restituisce truefalse, il flusso di lavoro di aggiornamento viene annullato e il gestore eventi è uscito.

Il ValidPictureUpload(FileUpload) codice del metodo, che è stato refactorato dal gestore eventi DetailsView, ItemInserting segue:

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

Passaggio 8: Sostituzione delle immagini delle categorie originali con JPG

Si ricordi che le otto categorie originali sono file bitmap avvolti in un'intestazione OLE. Dopo aver aggiunto la funzionalità per modificare un'immagine del record esistente, prendere un momento per sostituire queste bitmap con I GRUPPI di sicurezza. Se si vuole continuare a usare le immagini di categoria correnti, è possibile convertirle in JPGs eseguendo la procedura seguente:

  1. Salvare le immagini bitmap nel disco rigido. Visitare la UpdatingAndDeleting.aspx pagina nel browser e per ognuna delle prime otto categorie, fare clic con il pulsante destro del mouse sull'immagine e scegliere di salvare l'immagine.
  2. Aprire l'immagine nell'editor di immagini a scelta. È possibile usare Microsoft Paint, ad esempio.
  3. Salvare la bitmap come immagine JPG.
  4. Aggiornare l'immagine della categoria tramite l'interfaccia di modifica usando il file JPG.

Dopo aver modificato una categoria e caricato l'immagine JPG, l'immagine non verrà eseguita nel browser perché la DisplayCategoryPicture.aspx pagina rimuove i primi 78 byte dalle immagini delle prime otto categorie. Risolvere questo problema rimuovendo il codice che esegue la rimozione dell'intestazione OLE. Dopo aver eseguito questa operazione, il DisplayCategoryPicture.aspx``Page_Load gestore eventi deve avere solo il codice seguente:

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);
}

Nota

L'inserimento UpdatingAndDeleting.aspx e la modifica delle interfacce della pagina potrebbero usare un po'più di lavoro. Gli CategoryName elementi e BoundFields in DetailsView e Description GridView devono essere convertiti in TemplateFields. Poiché CategoryName non consente NULL valori, deve essere aggiunto un oggetto RequiredFieldValidator. E la Description casella di testo dovrebbe essere probabilmente convertita in una casella di testo a più righe. Lascio questi toccato di fine come esercizio per te.

Riepilogo

Questa esercitazione completa l'analisi dell'uso dei dati binari. In questa esercitazione e nei tre precedenti è stato illustrato come i dati binari possono essere archiviati nel file system o direttamente all'interno del database. Un utente fornisce dati binari al sistema selezionando un file dal disco rigido e caricandolo nel server Web, dove può essere archiviato nel file system o inserito nel database. ASP.NET 2.0 include un controllo FileUpload che consente di fornire un'interfaccia così semplice come trascinare e rilasciare. Tuttavia, come indicato nell'esercitazione Caricamento file , il controllo FileUpload è adatto solo per caricamenti di file relativamente piccoli, idealmente non superare un megabyte. È stato anche illustrato come associare i dati caricati al modello di dati sottostante, nonché come modificare ed eliminare i dati binari dai record esistenti.

Il set successivo di esercitazioni illustra varie tecniche di memorizzazione nella cache. La memorizzazione nella cache offre un mezzo per migliorare le prestazioni complessive di un'applicazione prendendo i risultati dalle operazioni costose e archiviandole in una posizione a cui è possibile accedere più rapidamente.

Programmazione felice!

Informazioni sull'autore

Scott Mitchell, autore di sette libri ASP/ASP.NET e fondatore di 4GuysFromRolla.com, lavora con le tecnologie Web Microsoft dal 1998. Scott lavora come consulente indipendente, formatore e scrittore. Il suo ultimo libro è Sams Teach Yourself ASP.NET 2.0 in 24 ore. Può essere raggiunto all'indirizzo mitchell@4GuysFromRolla.com. o tramite il suo blog, disponibile all'indirizzo http://ScottOnWriting.NET.

Grazie speciale a

Questa serie di esercitazioni è stata esaminata da molti revisori utili. Il revisore principale di questa esercitazione era Teresa Murphy. Si è interessati a esaminare i prossimi articoli MSDN? In tal caso, rilasciami una riga in mitchell@4GuysFromRolla.com.