Atualizar e excluir dados binários existentes (C#)
por Scott Mitchell
Em tutoriais anteriores, vimos como o controle GridView simplifica a edição e a exclusão de dados de texto. Neste tutorial, vemos como o controle GridView também possibilita editar e excluir dados binários, independentemente de esses dados binários serem salvos no banco de dados ou armazenados no sistema de arquivos.
Introdução
Nos últimos três tutoriais, adicionamos um pouco de funcionalidade para trabalhar com dados binários. Começamos adicionando uma BrochurePath
coluna à Categories
tabela e atualizamos a arquitetura adequadamente. Também adicionamos métodos camada de acesso a dados e camada de lógica de negócios para trabalhar com a coluna existente Picture
da tabela Categorias, que contém o conteúdo binário de um arquivo de imagem. Criamos páginas da Web para apresentar os dados binários em um GridView um link de download para o folheto, com a imagem da categoria mostrada em um <img>
elemento e adicionamos um DetailsView para permitir que os usuários adicionem uma nova categoria e carreguem seus dados de folheto e imagem.
Tudo o que resta a ser implementado é a capacidade de editar e excluir categorias existentes, o que realizaremos neste tutorial usando os recursos internos de edição e exclusão do GridView. Ao editar uma categoria, o usuário poderá, opcionalmente, carregar uma nova imagem ou fazer com que a categoria continue a usar a existente. Para o folheto, eles podem optar por usar o folheto existente, carregar um novo folheto ou indicar que a categoria não tem mais um folheto associado a ele. Vamos começar!
Etapa 1: Atualizando a camada de acesso a dados
O DAL tem métodos , Update
e gerados Insert
automaticamente, mas esses métodos foram gerados com base na CategoriesTableAdapter
consulta s main, que não inclui a Picture
Delete
coluna. Portanto, os Insert
métodos e Update
não incluem parâmetros para especificar os dados binários para a imagem da categoria. Como fizemos no tutorial anterior, precisamos criar um novo método TableAdapter para atualizar a Categories
tabela ao especificar dados binários.
Abra o Conjunto de Dados Digitado e, no Designer, clique com o botão direito do CategoriesTableAdapter
mouse no cabeçalho s e escolha Adicionar Consulta no menu de contexto para iniciar o Assistente de Configuração de Consulta tableAdapter. Esse assistente começa perguntando como a consulta TableAdapter deve acessar o banco de dados. Escolha Usar instruções SQL e clique em Avançar. A próxima etapa solicita que o tipo de consulta seja gerado. Como estamos criando uma consulta para adicionar um novo registro à Categories
tabela, escolha ATUALIZAR e clique em Avançar.
Figura 1: selecione a opção UPDATE (Clique para exibir a imagem em tamanho real)
Agora precisamos especificar a UPDATE
instrução SQL. O assistente sugere automaticamente uma instrução UPDATE
correspondente à consulta main do TableAdapter (aquela que atualiza os CategoryName
valores , Description
e BrochurePath
). Altere a instrução para que a Picture
coluna seja incluída junto com um @Picture
parâmetro, da seguinte forma:
UPDATE [Categories] SET
[CategoryName] = @CategoryName,
[Description] = @Description,
[BrochurePath] = @BrochurePath ,
[Picture] = @Picture
WHERE (([CategoryID] = @Original_CategoryID))
A tela final do assistente nos pede para nomear o novo método TableAdapter. Insira UpdateWithPicture
e clique em Concluir.
Figura 2: nomeie o novo método UpdateWithPicture
TableAdapter (clique para exibir a imagem em tamanho real)
Etapa 2: Adicionar os métodos de camada de lógica de negócios
Além de atualizar o DAL, precisamos atualizar a BLL para incluir métodos para atualizar e excluir uma categoria. Esses são os métodos que serão invocados da Camada de Apresentação.
Para excluir uma categoria, podemos usar o CategoriesTableAdapter
método gerado automaticamente Delete
. Adicione o seguinte método à 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;
}
Para este tutorial, vamos criar dois métodos para atualizar uma categoria : um que espera os dados de imagem binária e invoca o UpdateWithPicture
método que acabamos de adicionar ao CategoriesTableAdapter
e outro que aceita apenas os CategoryName
valores , Description
e BrochurePath
e e usa CategoriesTableAdapter
a instrução gerada Update
automaticamente da classe s. A lógica por trás do uso de dois métodos é que, em algumas circunstâncias, um usuário pode querer atualizar a imagem da categoria junto com seus outros campos, nesse caso, o usuário terá que carregar a nova imagem. Os dados binários da imagem carregada podem ser usados na instrução UPDATE
. Em outros casos, o usuário pode estar interessado apenas em atualizar, digamos, o nome e a descrição. Mas se a UPDATE
instrução espera os dados binários para a Picture
coluna também, também precisamos fornecer essas informações. Isso exigiria uma viagem extra ao banco de dados para trazer de volta os dados de imagem do registro que está sendo editado. Portanto, queremos dois UPDATE
métodos. A Camada lógica de negócios determinará qual deles usar com base em se os dados de imagem são fornecidos ao atualizar a categoria.
Para facilitar isso, adicione dois métodos à CategoriesBLL
classe , ambos chamados UpdateCategory
. O primeiro deve aceitar três string
s, uma byte
matriz e um int
como seus parâmetros de entrada; o segundo, apenas três string
s e um int
. Os string
parâmetros de entrada são para o nome, a descrição e o caminho do arquivo de folheto da categoria, a byte
matriz é para o conteúdo binário da imagem da categoria e identifica o int
CategoryID
do registro a ser atualizado. Observe que a primeira sobrecarga invocará a segunda se a matriz passada byte
for 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;
}
Etapa 3: Copiar sobre a funcionalidade Inserir e Exibir
No tutorial anterior , criamos uma página chamada UploadInDetailsView.aspx
que listava todas as categorias em um GridView e fornecemos um DetailsView para adicionar novas categorias ao sistema. Neste tutorial, estenderemos o GridView para incluir a edição e a exclusão do suporte. Em vez de continuar a trabalhar com UploadInDetailsView.aspx
o , vamos colocar as alterações deste tutorial na UpdatingAndDeleting.aspx
página da mesma pasta, ~/BinaryData
. Copie e cole a marcação declarativa e o código de UploadInDetailsView.aspx
para UpdatingAndDeleting.aspx
.
Comece abrindo a UploadInDetailsView.aspx
página. Copie toda a sintaxe declarativa dentro do <asp:Content>
elemento, conforme mostrado na Figura 3. Em seguida, abra UpdatingAndDeleting.aspx
e cole essa marcação dentro de seu <asp:Content>
elemento. Da mesma forma, copie o código da UploadInDetailsView.aspx
classe code-behind da página para UpdatingAndDeleting.aspx
.
Figura 3: Copiar a Marcação Declarativa de UploadInDetailsView.aspx
(Clique para exibir a imagem em tamanho real)
Depois de copiar a marcação declarativa e o código, visite UpdatingAndDeleting.aspx
. Você deve ver a mesma saída e ter a mesma experiência do usuário com UploadInDetailsView.aspx
a página do tutorial anterior.
Etapa 4: adicionando suporte à exclusão ao ObjectDataSource e gridView
Como discutimos novamente no tutorial Uma Visão Geral de Inserção, Atualização e Exclusão de Dados , o GridView fornece recursos internos de exclusão e esses recursos podem ser habilitados no tick de uma caixa de seleção se a fonte de dados subjacente da grade der suporte à exclusão. Atualmente, o ObjectDataSource ao qual GridView está associado (CategoriesDataSource
) não dá suporte à exclusão.
Para corrigir isso, clique na opção Configurar Fonte de Dados da marca inteligente ObjectDataSource para iniciar o assistente. A primeira tela mostra que ObjectDataSource está configurado para funcionar com a CategoriesBLL
classe . Pressione Avançar. Atualmente, apenas as propriedades e SelectMethod
ObjectDataSource InsertMethod
são especificadas. No entanto, o assistente preencheu automaticamente as listas suspensas nas guias UPDATE e DELETE com os UpdateCategory
métodos e DeleteCategory
, respectivamente. Isso ocorre porque, na CategoriesBLL
classe, marcamos esses métodos usando o DataObjectMethodAttribute
como os métodos padrão para atualizar e excluir.
Por enquanto, defina a lista suspensa da guia UPDATE como (Nenhum), mas deixe a lista suspensa da guia DELETE definida como DeleteCategory
. Retornaremos a esse assistente na Etapa 6 para adicionar suporte de atualização.
Figura 4: configurar o ObjectDataSource para usar o DeleteCategory
método (clique para exibir a imagem em tamanho real)
Observação
Ao concluir o assistente, o Visual Studio pode perguntar se você deseja atualizar campos e chaves, o que regenerará os campos de controles da Web de dados. Escolha Não, pois escolher Sim substituirá todas as personalizações de campo que você possa ter feito.
O ObjectDataSource agora incluirá um valor para sua DeleteMethod
propriedade, bem como um DeleteParameter
. Lembre-se de que, ao usar o assistente para especificar os métodos, o Visual Studio define a propriedade original_{0}
ObjectDataSource como OldValuesParameterFormatString
, o que causa problemas com as invocações de método de atualização e exclusão. Portanto, desmarque completamente essa propriedade ou redefina-a para o padrão, {0}
. Se você precisar atualizar sua memória nesta propriedade ObjectDataSource, consulte o tutorial Uma Visão geral da inserção, atualização e exclusão de dados .
Depois de concluir o assistente e corrigir o OldValuesParameterFormatString
, a marcação declarativa do ObjectDataSource deve ser semelhante à seguinte:
<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>
Depois de configurar o ObjectDataSource, adicione recursos de exclusão ao GridView marcando a caixa de seleção Habilitar Exclusão da marca inteligente GridView. Isso adicionará um CommandField ao GridView cuja ShowDeleteButton
propriedade está definida como true
.
Figura 5: Habilitar o suporte para exclusão no GridView (clique para exibir a imagem em tamanho real)
Tire um momento para testar a funcionalidade de exclusão. Há uma chave estrangeira entre as Products
tabelas e CategoryID
as Categories
tabelas, CategoryID
portanto, você receberá uma exceção de violação de restrição de chave estrangeira se tentar excluir qualquer uma das oito primeiras categorias. Para testar essa funcionalidade, adicione uma nova categoria, fornecendo um folheto e uma imagem. Minha categoria de teste, mostrada na Figura 6, inclui um arquivo de folheto de teste chamado Test.pdf
e uma imagem de teste. A Figura 7 mostra o GridView após a adição da categoria de teste.
Figura 6: Adicionar uma categoria de teste com um folheto e uma imagem (clique para exibir a imagem em tamanho real)
Figura 7: depois de inserir a categoria de teste, ela é exibida no GridView (Clique para exibir a imagem em tamanho real)
No Visual Studio, atualize o Gerenciador de Soluções. Agora você deve ver um novo arquivo na ~/Brochures
pasta Test.pdf
(consulte Figura 8).
Em seguida, clique no link Excluir na linha Categoria de Teste, fazendo com que a página faça o postback e o CategoriesBLL
método da classe seja DeleteCategory
acionado. Isso invocará o método da DAL, Delete
fazendo com que a instrução apropriada DELETE
seja enviada ao banco de dados. Em seguida, os dados são recuperados para o GridView e a marcação é enviada de volta ao cliente com a Categoria de Teste não mais presente.
Embora o fluxo de trabalho de exclusão tenha removido com êxito o registro categoria de teste da Categories
tabela, ele não removeu seu arquivo de folheto do sistema de arquivos do servidor Web. Atualize o Gerenciador de Soluções e você verá que Test.pdf
ainda está sentado na ~/Brochures
pasta.
Figura 8: O Test.pdf
arquivo não foi excluído do sistema de arquivos do servidor Web
Etapa 5: Removendo o arquivo de folheto de categoria excluída
Uma das desvantagens de armazenar dados binários externos ao banco de dados é que etapas extras devem ser executadas para limpo esses arquivos quando o registro de banco de dados associado for excluído. GridView e ObjectDataSource fornecem eventos que são disparados antes e depois que o comando delete é executado. Na verdade, precisamos criar manipuladores de eventos para os eventos pré e pós-ação. Antes que o Categories
registro seja excluído, precisamos determinar o caminho do arquivo PDF, mas não queremos excluir o PDF antes que a categoria seja excluída caso haja alguma exceção e a categoria não seja excluída.
O evento GridView é RowDeleting
acionado antes que o comando de exclusão objectDataSource seja invocado, enquanto seu RowDeleted
evento é acionado depois. Crie manipuladores de eventos para esses dois eventos usando o seguinte código:
// 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
No manipulador de eventos, o CategoryID
da linha que está sendo excluída é capturado da coleção GridViewDataKeys
, que pode ser acessada nesse manipulador de eventos por meio da e.Keys
coleção. Em seguida, as CategoriesBLL
classes s GetCategoryByCategoryID(categoryID)
são invocadas para retornar informações sobre o registro que está sendo excluído. Se o objeto retornado CategoriesDataRow
tiver um valor diferente,NULL``BrochurePath
ele será armazenado na variável deletedCategorysPdfPath
de página para que o arquivo possa ser excluído no RowDeleted
manipulador de eventos.
Observação
Em vez de recuperar os BrochurePath
detalhes do Categories
registro que está sendo excluído no RowDeleting
manipulador de eventos, poderíamos ter adicionado alternativamente o BrochurePath
à propriedade gridView e DataKeyNames
acessado o valor do registro por meio da e.Keys
coleção. Isso aumentaria ligeiramente o tamanho do estado de exibição do GridView, mas reduziria a quantidade de código necessária e salvaria uma viagem ao banco de dados.
Depois que o comando de exclusão subjacente objectDataSource tiver sido invocado, o manipulador de eventos gridView RowDeleted
será acionado. Se não houver exceções na exclusão dos dados e houver um valor para deletedCategorysPdfPath
, o PDF será excluído do sistema de arquivos. Observe que esse código extra não é necessário para limpo os dados binários da categoria associados à sua imagem. Isso ocorre porque os dados da imagem são armazenados diretamente no banco de dados, portanto, excluir a Categories
linha também exclui os dados de imagem dessa categoria.
Depois de adicionar os dois manipuladores de eventos, execute esse caso de teste novamente. Ao excluir a categoria, seu PDF associado também é excluído.
A atualização de dados binários associados a um registro existente fornece alguns desafios interessantes. O restante deste tutorial se aprofunda na adição de recursos de atualização ao folheto e à imagem. A etapa 6 explora técnicas para atualizar as informações do folheto enquanto a Etapa 7 analisa a atualização da imagem.
Etapa 6: Atualizando um folheto de categoria
Conforme discutido no tutorial Uma Visão Geral da Inserção, Atualização e Exclusão de Dados , o GridView oferece suporte interno à edição em nível de linha que pode ser implementado pelo tique de uma caixa de seleção se sua fonte de dados subjacente estiver configurada adequadamente. Atualmente, o CategoriesDataSource
ObjectDataSource ainda não está configurado para incluir o suporte de atualização, portanto, vamos adicioná-lo.
Clique no link Configurar Fonte de Dados do assistente ObjectDataSource e prossiga para a segunda etapa. Devido ao DataObjectMethodAttribute
usado em CategoriesBLL
, a lista suspensa UPDATE deve ser preenchida automaticamente com a UpdateCategory
sobrecarga que aceita quatro parâmetros de entrada (para todas as colunas, menos Picture
). Altere isso para que ele use a sobrecarga com cinco parâmetros.
Figura 9: configurar o ObjectDataSource para usar o método para o UpdateCategory
qual inclui um parâmetro Picture
(clique para exibir a imagem em tamanho real)
O ObjectDataSource agora incluirá um valor para sua UpdateMethod
propriedade, bem como s correspondentes UpdateParameter
. Conforme observado na Etapa 4, o Visual Studio define a propriedade ObjectDataSource como OldValuesParameterFormatString
original_{0}
ao usar o assistente Configurar Fonte de Dados. Isso causará problemas com as invocações de método de atualização e exclusão. Portanto, desmarque completamente essa propriedade ou redefina-a para o padrão, {0}
.
Depois de concluir o assistente e corrigir o OldValuesParameterFormatString
, a marcação declarativa do ObjectDataSource deve ser semelhante à seguinte:
<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>
Para ativar os recursos de edição internos do GridView, marcar a opção Habilitar Edição da marca inteligente gridView. Isso definirá a propriedade true
CommandField como ShowEditButton
, resultando na adição de um botão Editar (e botões Atualizar e Cancelar para a linha que está sendo editada).
Figura 10: configurar o GridView para dar suporte à edição (clique para exibir a imagem em tamanho real)
Visite a página por meio de um navegador e clique em um dos botões Editar da linha. O CategoryName
e Description
BoundFields são renderizados como caixas de texto. O BrochurePath
TemplateField não tem um EditItemTemplate
, portanto, ele continua a mostrar ItemTemplate
um link para o folheto. O Picture
ImageField é renderizado como uma TextBox cuja Text
propriedade recebe o valor do valor de DataImageUrlField
ImageField, nesse caso CategoryID
.
Figura 11: o GridView não tem uma interface de edição para BrochurePath
(clique para exibir imagem em tamanho real)
Personalizando aBrochurePath
interface de edição s
Precisamos criar uma interface de edição para o BrochurePath
TemplateField, uma que permita que o usuário:
- Deixe o folheto da categoria no estado em que se encontra,
- Atualizar o folheto da categoria carregando um novo folheto ou
- Remova completamente o folheto da categoria (caso a categoria não tenha mais um folheto associado).
Também precisamos atualizar a Picture
interface de edição do ImageField, mas chegaremos a isso na Etapa 7.
Na marca inteligente GridView, clique no link Editar Modelos e selecione TemplateField BrochurePath
s EditItemTemplate
na lista suspensa. Adicione um controle Web RadioButtonList a esse modelo, definindo sua ID
propriedade como BrochureOptions
e sua AutoPostBack
propriedade como true
. No janela Propriedades, clique nas reticências na Items
propriedade , que abrirá o ListItem
Editor Coleção. Adicione as três opções a seguir com Value
s 1, 2 e 3, respectivamente:
- Usar o folheto atual
- Remover o folheto atual
- Carregar novo folheto
Defina a primeira ListItem
propriedade s Selected
como true
.
Figura 12: Adicionar três ListItem
s ao RadioButtonList
Abaixo do RadioButtonList, adicione um controle FileUpload chamado BrochureUpload
. Defina sua Visible
propriedade como false
.
Figura 13: Adicionar um controle RadioButtonList e FileUpload ao EditItemTemplate
(Clique para exibir a imagem em tamanho real)
Este RadioButtonList fornece as três opções para o usuário. A ideia é que o controle FileUpload será exibido somente se a última opção, Carregar novo folheto, estiver selecionada. Para fazer isso, crie um manipulador de eventos para o evento RadioButtonList SelectedIndexChanged
e adicione o seguinte código:
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");
}
Como os controles RadioButtonList e FileUpload estão dentro de um modelo, precisamos escrever um pouco de código para acessar programaticamente esses controles. O SelectedIndexChanged
manipulador de eventos é passado uma referência do RadioButtonList no sender
parâmetro de entrada. Para obter o controle FileUpload, precisamos obter o controle pai do RadioButtonList e usar o FindControl("controlID")
método de lá. Depois que tivermos uma referência aos controles RadioButtonList e FileUpload, a propriedade do Visible
controle FileUpload será definida true
como somente se o RadioButtonList for SelectedValue
igual a 3, que é o Value
para o folheto ListItem
Carregar novo .
Com esse código em vigor, reserve um momento para testar a interface de edição. Clique no botão Editar para uma linha. Inicialmente, a opção Usar folheto atual deve ser selecionada. Alterar o índice selecionado causa um postback. Se a terceira opção estiver selecionada, o controle FileUpload será exibido, caso contrário, ficará oculto. A Figura 14 mostra a interface de edição quando o botão Editar é clicado pela primeira vez; A Figura 15 mostra a interface depois que a opção Carregar novo folheto é selecionada.
Figura 14: Inicialmente, a opção Usar folheto atual é Selecionada (Clique para exibir a imagem em tamanho real)
Figura 15: escolher a opção Carregar novo folheto exibe o controle FileUpload (Clique para exibir a imagem em tamanho real)
Salvando o arquivo de folheto e atualizando aBrochurePath
coluna
Quando o botão Atualização do GridView é clicado, seu RowUpdating
evento é acionado. O comando de atualização objectDataSource é invocado e, em seguida, o evento GridView é RowUpdated
acionado. Assim como acontece com o fluxo de trabalho de exclusão, precisamos criar manipuladores de eventos para ambos os eventos. RowUpdating
No manipulador de eventos, precisamos determinar qual ação tomar com base no SelectedValue
de BrochureOptions
RadioButtonList:
- Se for
SelectedValue
1, queremos continuar usando a mesmaBrochurePath
configuração. Portanto, precisamos definir o parâmetro ObjectDataSource parabrochurePath
o valor existenteBrochurePath
do registro que está sendo atualizado. O parâmetro ObjectDataSource podebrochurePath
ser definido usandoe.NewValues["brochurePath"] = value
. - Se for
SelectedValue
2, queremos definir o valor doBrochurePath
registro comoNULL
. Isso pode ser feito definindo o parâmetro ObjectDataSource comobrochurePath
, o que resulta em um banco de dadosNULL
sendo usado naUPDATE
instrução .Nothing
Se houver um arquivo de folheto existente que está sendo removido, precisamos excluir o arquivo existente. No entanto, só queremos fazer isso se a atualização for concluída sem gerar uma exceção. - Se for
SelectedValue
3, queremos garantir que o usuário tenha carregado um arquivo PDF e, em seguida, salve-o no sistema de arquivos e atualize o valor da coluna doBrochurePath
registro. Além disso, se houver um arquivo de folheto existente que está sendo substituído, precisaremos excluir o arquivo anterior. No entanto, só queremos fazer isso se a atualização for concluída sem gerar uma exceção.
As etapas necessárias para serem concluídas quando o RadioButtonList s SelectedValue
for 3 são praticamente idênticas às usadas pelo manipulador de eventos DetailsView ItemInserting
. Esse manipulador de eventos é executado quando um novo registro de categoria é adicionado do controle DetailsView que adicionamos no tutorial anterior. Portanto, cabe a nós refatorar essa funcionalidade em métodos separados. Especificamente, mudei a funcionalidade comum para dois métodos:
ProcessBrochureUpload(FileUpload, out bool)
aceita como entrada uma instância de controle FileUpload e um valor booliano de saída que especifica se a operação de exclusão ou edição deve continuar ou se deve ser cancelada devido a algum erro de validação. Esse método retorna o caminho para o arquivo salvo ounull
se nenhum arquivo foi salvo.DeleteRememberedBrochurePath
exclui o arquivo especificado pelo caminho na variáveldeletedCategorysPdfPath
de página sedeletedCategorysPdfPath
nãonull
for .
O código para esses dois métodos segue. Observe a similaridade entre ProcessBrochureUpload
e o manipulador de ItemInserting
eventos DetailsView do tutorial anterior. Neste tutorial, atualizei os manipuladores de eventos do DetailsView para usar esses novos métodos. Baixe o código associado a este tutorial para ver as modificações nos manipuladores de eventos 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));
}
}
Os manipuladores de RowUpdating
eventos e RowUpdated
GridView usam os ProcessBrochureUpload
métodos e DeleteRememberedBrochurePath
, como mostra o seguinte código:
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();
}
}
Observe como o RowUpdating
manipulador de eventos usa uma série de instruções condicionais para executar a ação apropriada com base no BrochureOptions
valor da SelectedValue
propriedade RadioButtonList.
Com esse código em vigor, você pode editar uma categoria e fazer com que ela use seu folheto atual, não use nenhum folheto ou carregue um novo. Vá em frente e experimente. Defina pontos RowUpdating
de interrupção nos manipuladores de eventos e RowUpdated
para ter uma noção do fluxo de trabalho.
Etapa 7: Carregando uma nova imagem
A Picture
interface de edição imagefield é renderizada como uma caixa de texto preenchida com o valor de sua DataImageUrlField
propriedade. Durante o fluxo de trabalho de edição, o GridView passa um parâmetro para ObjectDataSource com o nome do parâmetro o valor da propriedade ImageField s DataImageUrlField
e o valor do parâmetro que o valor inseriu na caixa de texto na interface de edição. Esse comportamento é adequado quando a imagem é salva como um arquivo no sistema de arquivos e DataImageUrlField
contém a URL completa da imagem. Com essas circunstâncias, a interface de edição exibe a URL da imagem na caixa de texto, que o usuário pode alterar e salvo de volta no banco de dados. Concedida, essa interface padrão não permite que o usuário carregue uma nova imagem, mas permite que ele altere a URL da imagem do valor atual para outra. Para este tutorial, no entanto, a interface de edição padrão do ImageField não é suficiente porque os Picture
dados binários estão sendo armazenados diretamente no banco de dados e a DataImageUrlField
propriedade contém apenas o CategoryID
.
Para entender melhor o que acontece em nosso tutorial quando um usuário edita uma linha com um ImageField, considere o seguinte exemplo: um usuário edita uma linha com 10, fazendo com CategoryID
que o Picture
ImageField seja renderizado como uma caixa de texto com o valor 10. Imagine que o usuário altere o valor nesta caixa de texto para 50 e clique no botão Atualizar. Ocorre um postback e o GridView cria inicialmente um parâmetro chamado CategoryID
com o valor 50. No entanto, antes que o GridView envie esse parâmetro (e os CategoryName
parâmetros e Description
), ele adiciona os valores da DataKeys
coleção. Portanto, ele substitui o CategoryID
parâmetro pelo valor subjacente CategoryID
da linha atual, 10. Em suma, a interface de edição do ImageField não afeta o fluxo de trabalho de edição deste tutorial porque os nomes da propriedade ImageField e DataImageUrlField
do valor da DataKey
grade são um no mesmo.
Embora o ImageField facilite a exibição de uma imagem com base em dados de banco de dados, não queremos fornecer uma caixa de texto na interface de edição. Em vez disso, queremos oferecer um controle FileUpload que o usuário final pode usar para alterar a imagem da categoria. Ao contrário do BrochurePath
valor, para esses tutoriais, decidimos exigir que cada categoria tenha uma imagem. Portanto, não precisamos permitir que o usuário indique que não há nenhuma imagem associada que o usuário possa carregar uma nova imagem ou deixar a imagem atual como está.
Para personalizar a interface de edição do ImageField, precisamos convertê-la em um TemplateField. Na marca inteligente GridView, clique no link Editar Colunas, selecione ImageField e clique no link Converter este campo em um TemplateField.
Figura 16: Converter o ImageField em um TemplateField
Converter o ImageField em um TemplateField dessa maneira gera um TemplateField com dois modelos. Como mostra a sintaxe declarativa a seguir, o ItemTemplate
contém um controle Web image cuja ImageUrl
propriedade é atribuída usando a sintaxe de vinculação de dados com base nas propriedades e DataImageUrlFormatString
do DataImageUrlField
ImageField. O EditItemTemplate
contém um TextBox cuja Text
propriedade está associada ao valor especificado pela DataImageUrlField
propriedade .
<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>
Precisamos atualizar o EditItemTemplate
para usar um controle FileUpload. Na marca inteligente GridView, clique no link Editar Modelos e selecione TemplateField Picture
s EditItemTemplate
na lista suspensa. No modelo, você deve ver um TextBox remover isso. Em seguida, arraste um controle FileUpload da Caixa de Ferramentas para o modelo, definindo-o ID
como PictureUpload
. Adicione também o texto Para alterar a imagem da categoria, especifique uma nova imagem. Para manter a imagem da categoria igual, deixe o campo vazio para o modelo também.
Figura 17: Adicionar um controle FileUpload ao EditItemTemplate
(Clique para exibir a imagem em tamanho real)
Depois de personalizar a interface de edição, exiba seu progresso em um navegador. Ao exibir uma linha no modo somente leitura, a imagem da categoria é mostrada como era antes, mas clicar no botão Editar renderiza a coluna de imagem como texto com um controle FileUpload.
Figura 18: a interface de edição inclui um controle FileUpload (clique para exibir a imagem em tamanho real)
Lembre-se de que ObjectDataSource está configurado para chamar o CategoriesBLL
método de classe s UpdateCategory
que aceita como entrada os dados binários para a imagem como uma byte
matriz. No entanto, se essa matriz tiver um null
valor, a sobrecarga alternativa UpdateCategory
será chamada, o que emitirá a UPDATE
instrução SQL que não modifica a Picture
coluna, deixando a imagem atual da categoria intacta. Portanto, no manipulador de eventos do RowUpdating
GridView, precisamos referenciar programaticamente o PictureUpload
controle FileUpload e determinar se um arquivo foi carregado. Se um não tiver sido carregado, não queremos especificar um valor para o picture
parâmetro . Por outro lado, se um arquivo foi carregado no PictureUpload
controle FileUpload, queremos garantir que ele seja um arquivo JPG. Se for, poderemos enviar seu conteúdo binário para ObjectDataSource por meio do picture
parâmetro .
Assim como com o código usado na Etapa 6, grande parte do código necessário aqui já existe no manipulador de ItemInserting
eventos DetailsView. Portanto, refatorei a funcionalidade comum em um novo método e ValidPictureUpload
atualizei o ItemInserting
manipulador de eventos para usar esse método.
Adicione o código a seguir ao início do manipulador de eventos GridView RowUpdating
. É importante que esse código venha antes do código que salva o arquivo de folheto, pois não queremos salvar o folheto no sistema de arquivos do servidor Web se um arquivo de imagem inválido for carregado.
// 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;
}
}
O ValidPictureUpload(FileUpload)
método usa um controle FileUpload como seu único parâmetro de entrada e verifica a extensão do arquivo carregado para garantir que o arquivo carregado seja um JPG; ele só será chamado se um arquivo de imagem for carregado. Se nenhum arquivo for carregado, o parâmetro de imagem não será definido e, portanto, usará seu valor padrão de null
. Se uma imagem foi carregada e ValidPictureUpload
retorna , o picture
parâmetro recebe os dados binários da imagem carregada; se o método retorna false
, o fluxo de trabalho de atualização é cancelado e o manipulador de true
eventos é encerrado.
O ValidPictureUpload(FileUpload)
código do método, que foi refatorado do manipulador de eventos 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;
}
}
Etapa 8: Substituindo as imagens de categorias originais por JPGs
Lembre-se de que as imagens originais de oito categorias são arquivos bitmap encapsulados em um cabeçalho OLE. Agora que adicionamos a capacidade de editar uma imagem de registro existente, tire um momento para substituir esses bitmaps por JPGs. Se você quiser continuar a usar as imagens de categoria atuais, poderá convertê-las em JPGs executando as seguintes etapas:
- Salve as imagens de bitmap no disco rígido. Visite a
UpdatingAndDeleting.aspx
página no navegador e, para cada uma das oito primeiras categorias, clique com o botão direito do mouse na imagem e escolha salvar a imagem. - Abra a imagem no editor de imagens de sua escolha. Você pode usar Microsoft Paint, por exemplo.
- Salve o bitmap como uma imagem JPG.
- Atualize a imagem da categoria por meio da interface de edição, usando o arquivo JPG.
Depois de editar uma categoria e carregar a imagem JPG, a imagem não será renderizada no navegador porque a DisplayCategoryPicture.aspx
página está retirando os primeiros 78 bytes das imagens das oito primeiras categorias. Corrija isso removendo o código que executa a remoção do cabeçalho OLE. Depois de fazer isso, o DisplayCategoryPicture.aspx``Page_Load
manipulador de eventos deve ter apenas o seguinte código:
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);
}
Observação
As UpdatingAndDeleting.aspx
interfaces de inserção e edição da página podem usar um pouco mais de trabalho. O CategoryName
e Description
BoundFields em DetailsView e GridView devem ser convertidos em TemplateFields. Como CategoryName
não permite NULL
valores, um RequiredFieldValidator deve ser adicionado. E o Description
TextBox provavelmente deve ser convertido em um TextBox de várias linhas. Eu deixo estes retoques finais como um exercício para você.
Resumo
Este tutorial conclui nossa análise de como trabalhar com dados binários. Neste tutorial e nos três anteriores, vimos como os dados binários podem ser armazenados no sistema de arquivos ou diretamente no banco de dados. Um usuário fornece dados binários para o sistema selecionando um arquivo em seu disco rígido e carregando-os no servidor Web, onde eles podem ser armazenados no sistema de arquivos ou inseridos no banco de dados. ASP.NET 2.0 inclui um controle FileUpload que torna o fornecimento de uma interface tão fácil quanto arrastar e soltar. No entanto, conforme observado no tutorial Carregando Arquivos , o controle FileUpload só é adequado para uploads de arquivos relativamente pequenos, o ideal é não exceder um megabyte. Também exploramos como associar dados carregados ao modelo de dados subjacente, bem como editar e excluir os dados binários de registros existentes.
Nosso próximo conjunto de tutoriais explora várias técnicas de cache. O cache fornece um meio de melhorar o desempenho geral de um aplicativo, tirando os resultados de operações caras e armazenando-os em um local que pode ser acessado mais rapidamente.
Programação feliz!
Sobre o autor
Scott Mitchell, autor de sete livros do ASP/ASP.NET e fundador da 4GuysFromRolla.com, trabalha com tecnologias da Microsoft Web desde 1998. Scott trabalha como consultor independente, treinador e escritor. Seu último livro é Sams Teach Yourself ASP.NET 2.0 em 24 Horas. Ele pode ser contatado em mitchell@4GuysFromRolla.com. ou através de seu blog, que pode ser encontrado em http://ScottOnWriting.NET.
Agradecimentos Especiais
Esta série de tutoriais foi revisada por muitos revisores úteis. A revisora principal deste tutorial foi Teresa Murphy. Interessado em revisar meus próximos artigos do MSDN? Nesse caso, deixe-me uma linha em mitchell@4GuysFromRolla.com.