Entity Framework 4, objetos POCO e ASP.Net MVC2. Eu tenho um relacionamento muitos para muitos, digamos, entre entidades BlogPost e Tag. Isso significa que em minha classe POCO BlogPost gerada por T4 eu tenho:
public virtual ICollection<Tag> Tags {
// getter and setter with the magic FixupCollection
}
private ICollection<Tag> _tags;
Peço um BlogPost e as tags relacionadas de uma instância do ObjectContext e envio para outra camada (Exibir no aplicativo MVC). Mais tarde, recebo de volta o BlogPost atualizado com propriedades alteradas e relacionamentos alterados. Por exemplo, ele tinha tags "A" "B" e "C", e as novas tags são "C" e "D". Em meu exemplo particular, não há novos tags e as propriedades dos tags nunca mudam, então a única coisa que deve ser salva são os relacionamentos alterados. Agora preciso salvar isso em outro ObjectContext. (Atualização: agora tentei fazer na mesma instância de contexto e também falhei.)
O problema: não consigo salvar os relacionamentos corretamente. Eu tentei tudo que encontrei:
- Controller.UpdateModel e Controller.ExperimenteUpdateModel não funcionam.
- Obter o antigo BlogPost do contexto e depois modificar a coleção não funciona. (com métodos diferentes a partir do próximo ponto)
- Isso provavelmente iria funcionar, mas espero que esta é apenas uma solução alternativa, não a solução :(.
- Experimentei as funções Attach / Add / ChangeObjectState para BlogPost e / ou Tags em todas as combinações possíveis. Falhou.
- Este parece com o que eu preciso, mas ele não funciona (Tentei corrigi-lo, mas não pode para o meu problema).
- Tentei ChangeState / Add / Attach / ... os objetos de relacionamento do contexto. Falhou.
"Não funciona" significa, na maioria dos casos, que trabalhei na "solução" fornecida até que ela não produzisse erros e salve pelo menos as propriedades do BlogPost. O que acontece com os relacionamentos varia: geralmente as tags são adicionadas novamente à tabela de tags com novos PKs e o BlogPost salvo faz referência a esses e não aos originais. É claro que os Tags retornados têm PKs, e antes dos métodos salvar / atualizar eu verifico os PKs e eles são iguais aos do banco de dados, então provavelmente EF pensa que eles são objetos novos e aqueles PKs são temporários.
Um problema que conheço e que pode tornar impossível encontrar uma solução automatizada simples: Quando a coleção de um objeto POCO é alterada, isso deve acontecer pela propriedade de coleção virtual mencionada acima, porque então o truque FixupCollection atualizará as referências reversas na outra extremidade do relacionamento muitos para muitos. No entanto, quando uma Visualização "retorna" um objeto BlogPost atualizado, isso não aconteceu. Isso significa que talvez não haja uma solução simples para o meu problema, mas isso me deixaria muito triste e eu odiaria o triunfo EF4-POCO-MVC :(. Isso também significaria que EF não pode fazer isso no ambiente MVC, seja qual for Tipos de objeto EF4 são usados :(. Acho que o rastreamento de alterações baseado em instantâneo deve descobrir que o BlogPost alterado tem relacionamentos com Tags com PKs existentes.
Aliás: acho que o mesmo problema acontece com as relações um-para-muitos (o Google e meu colega dizem isso). Vou tentar em casa, mas mesmo que funcione, isso não me ajuda em meus seis relacionamentos muitos-para-muitos em meu aplicativo :(.
fonte
Respostas:
Vamos tentar desta forma:
Editar:
Acho que um de meus comentários deu a você uma falsa esperança de que a EF fará a fusão para você. Brinquei muito com esse problema e minha conclusão diz que a EF não fará isso por você. Acho que você também encontrou minha pergunta no MSDN . Na realidade, há muitas perguntas desse tipo na Internet. O problema é que não está claro como lidar com esse cenário. Então, vamos dar uma olhada no problema:
Histórico do problema
EF precisa rastrear mudanças em entidades para que a persistência saiba quais registros devem ser atualizados, inseridos ou deletados. O problema é que é responsabilidade do ObjectContext rastrear as mudanças. ObjectContext é capaz de rastrear alterações apenas para entidades anexadas. As entidades criadas fora do ObjectContext não são rastreadas de forma alguma.
Descrição do Problema
Com base na descrição acima, podemos afirmar claramente que o EF é mais adequado para cenários conectados onde a entidade está sempre anexada ao contexto - típico para aplicativos WinForm. Os aplicativos da Web requerem um cenário desconectado em que o contexto é fechado após o processamento da solicitação e o conteúdo da entidade é passado como resposta HTTP ao cliente. A próxima solicitação HTTP fornece conteúdo modificado da entidade que deve ser recriado, anexado a um novo contexto e persistido. A recreação geralmente acontece fora do escopo do contexto (arquitetura em camadas com ignorância de persistência).
Solução
Então, como lidar com esse cenário desconectado? Ao usar classes POCO, temos 3 maneiras de lidar com o controle de alterações:
A sincronização manual em uma única entidade é uma tarefa fácil. Você só precisa anexar a entidade e chamar AddObject para inserir, DeleteObject para excluir ou definir o estado em ObjectStateManager como Modificado para atualização. A verdadeira dor surge quando você tem que lidar com o gráfico do objeto em vez de uma entidade única. Essa dor é ainda pior quando você tem que lidar com associações independentes (aquelas que não usam propriedade de chave estrangeira) e muitos para muitos relacionamentos. Nesse caso, você deve sincronizar manualmente cada entidade no gráfico de objeto, mas também cada relação no gráfico de objeto.
A sincronização manual é proposta como solução pela documentação do MSDN: Attaching and Detaching objects diz:
Os métodos mencionados são ChangeObjectState e ChangeRelationshipState de ObjectStateManager = controle de alteração manual. Proposta semelhante está em outro artigo de documentação do MSDN: Definindo e gerenciando relacionamentos diz:
Além disso, há um post no blog relacionado ao EF v1 que critica exatamente esse comportamento do EF.
Razão da solução
EF tem muitas operações e configurações "úteis", como Refresh , Load , ApplyCurrentValues , ApplyOriginalValues , MergeOption etc. Mas, pela minha investigação, todos esses recursos funcionam apenas para uma entidade e afetam apenas propriedades escalares (= não propriedades e relações de navegação). Prefiro não testar esses métodos com tipos complexos aninhados em entidade.
Outra solução proposta
Em vez da funcionalidade de fusão real, a equipe EF fornece algo chamado Self Tracking Entities (STE) que não resolve o problema. Em primeiro lugar, o STE funciona apenas se a mesma instância for usada para todo o processamento. No aplicativo da web, não é o caso, a menos que você armazene a instância no estado de exibição ou sessão. Por isso estou muito insatisfeito com o uso do EF e vou verificar as funcionalidades do NHibernate. A primeira observação diz que o NHibernate talvez tenha essa funcionalidade .
Conclusão
Vou terminar essas suposições com um único link para outra questão relacionada no fórum do MSDN. Verifique a resposta de Zeeshan Hirani. Ele é autor de Entity Framework 4.0 Recipes . Se ele disser que a mesclagem automática de gráficos de objetos não é compatível, eu acredito nele.
Mas ainda existe a possibilidade de que eu esteja completamente errado e alguma funcionalidade de mesclagem automática exista no EF.
Editar 2:
Como você pode ver, isso já foi adicionado ao MS Connect como sugestão em 2007. A MS fechou como algo a ser feito na próxima versão, mas na verdade nada foi feito para melhorar essa lacuna, exceto STE.
fonte
Tenho uma solução para o problema descrito acima por Ladislav. Eu criei um método de extensão para o DbContext que executará automaticamente a adição / atualização / exclusão com base em uma comparação do gráfico fornecido e gráfico persistente.
Dê uma olhada e veja se isso pode ajudar http://refactorthis.wordpress.com/2012/12/11/introducing-graphdiff-for-entity-framework-code-first-allowing-automated-updates-of-a- gráfico de entidades desanexadas /
Você pode ir direto para o código aqui https://github.com/refactorthis/GraphDiff
fonte
Eu sei que é tarde para o OP, mas como esse é um problema muito comum, postei isso caso seja para outra pessoa. Tenho brincado com esse problema e acho que encontrei uma solução bastante simples, o que faço é:
No exemplo a seguir, "dataobj" e "_categories" são os parâmetros recebidos por meu controlador "dataobj" é meu objeto principal e "_categories" é um IEnumerable contendo os IDs das categorias que o usuário selecionou na visualização.
Até funciona para relações múltiplas
fonte
A equipe do Entity Framework está ciente de que este é um problema de usabilidade e planeja resolvê-lo pós-EF6.
Da equipe do Entity Framework:
Se isso afeta você, vote no recurso em
http://entityframework.codeplex.com/workitem/864
fonte
Todas as respostas foram ótimas para explicar o problema, mas nenhuma delas realmente resolveu o problema para mim.
Descobri que, se não usar o relacionamento na entidade pai, mas apenas adicionar e remover as entidades filho, tudo funcionará bem.
Desculpe pelo VB, mas é nele que o projeto em que estou trabalhando está escrito.
A entidade pai "Report" tem um relacionamento um para muitos com "ReportRole" e tem a propriedade "ReportRoles". As novas funções são passadas por uma string separada por vírgulas de uma chamada Ajax.
A primeira linha removerá todas as entidades filhas e, se eu usar "report.ReportRoles.Remove (f)" em vez de "db.ReportRoles.Remove (f)", obteria o erro.
fonte