none
Delete com Entity Framework 4 & UpdateException RRS feed

  • Pergunta

  • Boa tarde a todos,
     
    Estou implementando uma classe genéricas de acesso ao banco de dados com entity framework, contendo os comandos Novo, Cancelar, Salvar, Excluir, etc.
    Até então tudo ia bem, mas no método Excluir em especial. Quando ocorre a excessão "UpdateException" (devido à restrições de chaves estrangeiras), ou seja, o objeto não foi de fato excluído do banco de dados, eu percebi que meu CollectionViewSource (projeto em WPF) que contém a lista de objetos vinda da query SELECT remove o item que até então seria excluído, mas na realidade não foi devido à excessão. Não estou sabendo resolver isto (sem ter que fazer um novo SELECT após a excessão, que daria um "overhead" a mais na performance).
    A seguir o código:
            Public Function ExcluiItem(ByVal entity As Object, Optional ByVal AutoSave As Boolean = True) As String
    
                Dim modified As Boolean = False
    
                Try
    
                    If Me.context.ObjectStateManager.GetObjectStateEntry(entity).State = EntityState.Modified Then
                        modified = True
                    End If
    
                    Me.context.DeleteObject(entity)
                    If AutoSave = True Then
                        Me.context.SaveChanges()
                    End If
                    Return String.Empty
    
                Catch sqlEx As System.Data.UpdateException
                    If modified = True Then
                        Me.context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified)
                    Else
                        Me.context.ObjectStateManager.ChangeObjectState(entity, EntityState.Unchanged)
                    End If
                    Me.context.AcceptAllChanges()
    
                    Return "Não foi possível excluir o item selecionado porque existem outros cadastros e movimentações vinculados ao mesmo."
    
                End Try
    
            End Function
    
    Pelo método acima eu tento voltar o Estado da entity de "Deleted" de volta para "Modified" ou "Unchanged" em caso de exceção, pois a excessão significaria que em fato a entity nao foi excluída.

    Nem usando Me.context.Refresh(System.Data.Objects.RefreshMode.StoreWins, entity) meu problema foi resolvido.
    Ex: Eu tenho os registros 1,2,5,7 e 8, sendo que o item 5 possui vinculos com outras tabelas 5.1, 5.2
    Como não defini a regra de exclusão em Cascata no relacionamento entre as tabelas, eu recebo o UpdateException no momento em que dou um Me.context.SaveChanges , se eu tentar excluir o item 5. E o mais engraçado que na janela o item some!!
    Alguém poderia me ajudar, por favor?
    Grato a todos.

    Henrique Clausing eficazcsblog.spaces.live.com
    sexta-feira, 30 de setembro de 2011 17:23

Respostas

  • Olá Henrique, eu nunca mexi muito com WPF mas acho que eu entendi o seu problema.

    Pelo o que eu entendi está acontecendo o seguinte... você carrega um CollectionViewSource, e usa ele como Source de algum WPF Control (DataGrid) por exemplo... ao você excluir o dado do control, você chama seu método que exclui do banco... se a exclusão falha, não muda nada no banco, mas o collectionViewSource perde o dado....

    Isso acontece porque o CollectionViewSource não está diretamente ligado ao banco, ele foi apenas consumido pelos dados retornados do banco...

    Para você resolver esse problema, você tem que rodar esse método em alguma função do seu WPF Control que é desperada ANTES que algo seja deletado do seu Source...

    Exemplo: Antes de algo ser deletado, rode o metodo que exclui do banco. Deu certo ? então deleta o dado do CollectionViewSource, deu errado... então cancela tudo e não deleta nada do CollectionViewSource.

    Eu não sei qual WPF Control você está usando, mas não importa qual seja, só ache o método que é disparado antes que algo do seu Source seja alterado.

    -----------

    Outra solução também seria você mudar a forma que você está Bindando seu Control. Procure por WPF Data Binding, eu creio que existem formas de bindar um control que não acontece esse tipo de problema ai...

     

    Espero ter ajudado =)


    Ao infinito e além!
    twitter @elzacky
    sábado, 1 de outubro de 2011 15:57
  • Henrique só para completar as respostas dos colegas...

    Não é uma boa prática deixar que sua aplicação gere Exceptions isso pode lhe causar problemas futuros e problemas de performance, ao meu ver o mais correto seria você verificar se o registro a ser excluído possui alguma referência em outra tabela e se o mesmo pode ser excluído (claro depois de analisar as regras de negócio de sua aplicação) e caso o mesmo possa ser excluído você exclui senão mostre a mensagem ao usuário informando ele o motivo.

    Pode resolver isso fazendo um select simples antes da exclusão por exemplo...

     

     

    Abraços e espero ter ajudado de alguma maneira!


    Estudar, Estudar e Estudar! Não existe caminho curto.
    sábado, 1 de outubro de 2011 17:03

Todas as Respostas

  • Chave estrangeira você sabe o que é né? Tem que remover as referencias nas outras tabelas para de pois remover o registro na tabela "pai".

    Existe a opção para deletar em cascata. Não recomendo isto de forma alguma.


    --
    Marque as respostas e ajude a melhorar a busca do fórum. pcfviana@gmail.com
    sábado, 1 de outubro de 2011 02:10
  • É como o Paulo César disse, você não pode deletar uma Tabela caso exista Outra tabela vinculada à ela. (e que contenha registros). Exemplo:

    -----------------------

    Tabela Categoria

    CategoriaID int

    Nome varchar

    ---------------------

    Tabela Produto

    ProdutoID int

    CategoriaID int

    Nome varchar

    ----------------------


    Você não pode deletar a tabela Categoria caso exista algum produto relacionado com alguma Categoria. Pois isso quebraria a integridade do banco. Entretanto é possivel que haja deleção mesmo elas estando relacionadas. Para isso você precisa ativar a opção de Deletar em Cascata, coisa que não é recomendada, pois ao ativar essa opção e você deletar a tabela Categoria, todos os Produtos pertencentes aquela Categoria também serão excluídos.


    Ao infinito e além!
    twitter @elzacky
    sábado, 1 de outubro de 2011 12:41
  • Bom dia,

    Primeiramente, gostaria de agradecer a todos pela atenção. Mas creio que minha pergunta não ficou bem elaborada.

    Compreendo que tenho que excluir as referências antes.

    No caso não é a geração da exception que está me intrigando. Pelo contrário, utilizo ela para mostrar para o usuário que o registro não pode ser excluído.

    Caso prático: Tenho uma tabela de "Cadastro de Pessoas" e uma Tabela de "Pedidos", na qual faz referência a de pessoas. O fato estranho diz respeito à tentativa do usuário excluir uma pessoa que possui algum pedido. Logicamente a exceção ocorre e a MessageBox é exibida na ela. O ítem não é excluído do banco, claro. Mesmo assim, na janela o ítem simplesmente some. Notei que no Context (EntityFramework) a entity fica marcada como "Deleted". Tentei modificar ela para "Unchanged" ou "Modified" para ver se ela voltava a ser exibida na janela, mas não tive sucesso. Imagine só, algum de vocês utilizando o sitema, clica em excluir, recebe uma mensagem de erro, mas o item (que não foi excluído) some da lista de items. Estranho não?

    Este fato está me forçando a dar um novo Select após a exception para que o ítem volte a aparecer na janela novamente. Em termos de performance, isto não é bom, estou certo? Um abraço.


    Henrique Clausing
    eficazcs.wordpress.com 

    sábado, 1 de outubro de 2011 13:55
  • Olá Henrique, eu nunca mexi muito com WPF mas acho que eu entendi o seu problema.

    Pelo o que eu entendi está acontecendo o seguinte... você carrega um CollectionViewSource, e usa ele como Source de algum WPF Control (DataGrid) por exemplo... ao você excluir o dado do control, você chama seu método que exclui do banco... se a exclusão falha, não muda nada no banco, mas o collectionViewSource perde o dado....

    Isso acontece porque o CollectionViewSource não está diretamente ligado ao banco, ele foi apenas consumido pelos dados retornados do banco...

    Para você resolver esse problema, você tem que rodar esse método em alguma função do seu WPF Control que é desperada ANTES que algo seja deletado do seu Source...

    Exemplo: Antes de algo ser deletado, rode o metodo que exclui do banco. Deu certo ? então deleta o dado do CollectionViewSource, deu errado... então cancela tudo e não deleta nada do CollectionViewSource.

    Eu não sei qual WPF Control você está usando, mas não importa qual seja, só ache o método que é disparado antes que algo do seu Source seja alterado.

    -----------

    Outra solução também seria você mudar a forma que você está Bindando seu Control. Procure por WPF Data Binding, eu creio que existem formas de bindar um control que não acontece esse tipo de problema ai...

     

    Espero ter ajudado =)


    Ao infinito e além!
    twitter @elzacky
    sábado, 1 de outubro de 2011 15:57
  • Henrique só para completar as respostas dos colegas...

    Não é uma boa prática deixar que sua aplicação gere Exceptions isso pode lhe causar problemas futuros e problemas de performance, ao meu ver o mais correto seria você verificar se o registro a ser excluído possui alguma referência em outra tabela e se o mesmo pode ser excluído (claro depois de analisar as regras de negócio de sua aplicação) e caso o mesmo possa ser excluído você exclui senão mostre a mensagem ao usuário informando ele o motivo.

    Pode resolver isso fazendo um select simples antes da exclusão por exemplo...

     

     

    Abraços e espero ter ajudado de alguma maneira!


    Estudar, Estudar e Estudar! Não existe caminho curto.
    sábado, 1 de outubro de 2011 17:03
  • Olá Henrique, eu nunca mexi muito com WPF mas acho que eu entendi o seu problema.

    Pelo o que eu entendi está acontecendo o seguinte... você carrega um CollectionViewSource, e usa ele como Source de algum WPF Control (DataGrid) por exemplo... ao você excluir o dado do control, você chama seu método que exclui do banco... se a exclusão falha, não muda nada no banco, mas o collectionViewSource perde o dado....

    Isso acontece porque o CollectionViewSource não está diretamente ligado ao banco, ele foi apenas consumido pelos dados retornados do banco...

    Para você resolver esse problema, você tem que rodar esse método em alguma função do seu WPF Control que é desperada ANTES que algo seja deletado do seu Source...

    Exemplo: Antes de algo ser deletado, rode o metodo que exclui do banco. Deu certo ? então deleta o dado do CollectionViewSource, deu errado... então cancela tudo e não deleta nada do CollectionViewSource.

    Eu não sei qual WPF Control você está usando, mas não importa qual seja, só ache o método que é disparado antes que algo do seu Source seja alterado.

    -----------

    Outra solução também seria você mudar a forma que você está Bindando seu Control. Procure por WPF Data Binding, eu creio que existem formas de bindar um control que não acontece esse tipo de problema ai...

     

    Espero ter ajudado =)


    Ao infinito e além!
    twitter @elzacky

    Correto Fábio, é este o cenário. O CollectionViewSource está com seu Sorce definido no DataContext de meu ViewModel, que por sua vez requisita os dados no DAL, que é uma classe contendo métodos que se relacionam com o EntityFramework.

    Realmente o CollectionViewSource não está diretamente ligado ao banco, muito menos ao ObjectContext do EntityFramework. Porém ele reflete bem as operações de Insert, Update e eventos PropertyChanging e PropertyChanged das entities retornadas pelos Select. Por isso achei que ele refletiria bem também o Delete. Como diz um amigo: "Mistérios da Programação"    :)

     

    Caro Pablo,

    Me chamou atenção também seu ponto de vista, porém não sei fazer citação de 2 posts numa mensagem só aqui...

    Analisando sua visão de forma detalhada, realmente, esperar que o SQL tente fazer a exclusão e falhe, pra depois informar o usuário que não é possível gera uma "punição" na performance. Eu não tinha pensado nisto, me deixei levar pelo comodismo, visto que é a opção mais fácil.

    Agora analisando que não estou "tipando" meu ObjectContext, deixando as regras trabalhando com objetos no nível mais genérico possível, preciso estudar uma forma, talvez por reflection, para tentar obter todos os relacionamentos que o objecto Entity possui e fazer o select para cada um encontrado, estou certo?

    Caso positivo, se souber como fazer e puder me ajudar com um pouco mais detalhes, ficarei grato.

    No mais, muito obrigado a todos, no fim, resumo que o meu problema não era o fato ocorrido que descrevi, mas sim os meios que utilizava para chegar até ele.

    Grande abraço a todos.


    Henrique Clausing
    eficazcs.wordpress.com 

    • Editado Henrique Clausing sábado, 1 de outubro de 2011 17:50 erro de "catilografia"
    sábado, 1 de outubro de 2011 17:49