Recupero dados do filme de uma API externa. Numa primeira fase, raspar cada filme e inseri-lo no meu próprio banco de dados. Em uma segunda fase, atualizarei periodicamente meu banco de dados usando a API "Alterações" da API, que posso consultar para ver quais filmes tiveram suas informações alteradas.
Minha camada ORM é Entity-Framework. A classe Movie é assim:
class Movie
{
public virtual ICollection<Language> SpokenLanguages { get; set; }
public virtual ICollection<Genre> Genres { get; set; }
public virtual ICollection<Keyword> Keywords { get; set; }
}
O problema surge quando eu tenho um filme que precisa ser atualizado: meu banco de dados pensa no objeto que está sendo rastreado e o novo que eu recebo da API de atualização chama como objetos diferentes, desconsiderando .Equals()
.
Isso causa um problema porque, quando agora tento atualizar o banco de dados com o filme atualizado, ele será inserido em vez de atualizar o filme existente.
Eu já tive esse problema com os idiomas e minha solução foi procurar os objetos de idioma anexados, desanexá-los do contexto, mover seu PK para o objeto atualizado e anexá-lo ao contexto. Quando SaveChanges()
agora é executado, o substituirá essencialmente.
Essa é uma abordagem bastante fedorenta, porque, se eu continuar com essa abordagem Movie
, significa que terei que desanexar o filme, os idiomas, os gêneros e as palavras-chave, procurar cada um no banco de dados, transferir seus IDs e inserir o código. novos objetos.
Existe uma maneira de fazer isso com mais elegância? O ideal é apenas passar o filme atualizado para o contexto e selecionar o filme correto a ser atualizado com base no Equals()
método, atualizar todos os seus campos e para cada objeto complexo: use o registro existente novamente com base em seu próprio Equals()
método e insira se ainda não existe.
Posso pular a desanexação / anexação fornecendo .Update()
métodos para cada objeto complexo que posso usar em combinação para recuperar todos os objetos anexados, mas isso ainda exigirá que eu recupere cada objeto existente para atualizá-lo.
fonte
id
e os filmes da API externa são compatíveis com os locais usando o campotmdbid
. Não consigo recuperar todas as entidades que precisam ser atualizadas em uma chamada, porque trata-se de filmes, gêneros, idiomas, palavras-chave etc. Cada uma delas possui uma PK e pode já existir no banco de dados.Respostas:
Não encontrei o que esperava, mas encontrei uma melhoria na sequência existente de seleção-desanexação-atualização-conexão.
O método de extensão
AddOrUpdate(this DbSet)
permite que você faça exatamente o que eu quero: insira se não estiver lá e atualize se encontrar um valor existente. Eu não percebi o uso mais cedo, pois realmente só vi ele ser usado noseed()
método em combinação com Migrações. Se houver algum motivo para eu não usar isso, me avise.Algo útil a ser observado: Há uma sobrecarga disponível que permite selecionar especificamente como a igualdade deve ser determinada. Aqui eu poderia ter usado o meu,
TMDbId
mas optei por simplesmente desconsiderar completamente meu próprio ID e, em vez disso, usar um PK no TMDbId combinado comDatabaseGeneratedOption.None
. Também uso essa abordagem em cada subcoleção, quando apropriado.Parte interessante da fonte :
é assim que os dados são realmente atualizados.
Tudo o que resta é chamar
AddOrUpdate
cada objeto que eu quero ser afetado por isso:Não é tão limpo quanto eu esperava, pois tenho que especificar manualmente cada parte do meu objeto que precisa ser atualizada, mas é o mais próximo possível.
Leitura relacionada: /programming/15336248/entity-framework-5-updating-a-record
Atualizar:
Acontece que meus testes não foram rigorosos o suficiente. Depois de usar essa técnica, notei que, embora o novo idioma tenha sido adicionado, ele não estava conectado ao filme. na tabela muitos para muitos. Esse é um problema conhecido, mas aparentemente de baixa prioridade e não foi corrigido até onde eu sei.
No final, decidi seguir a abordagem em que tenho
Update(T)
métodos para cada tipo e seguir esta sequência de eventos:Update()
método para atualizá-lo com os novos valoresÉ muito trabalho manual e é feio, portanto passará por mais algumas refatorações, mas agora meus testes indicam que ele deve funcionar para cenários mais rigorosos.
Depois de limpá-lo ainda mais, agora uso este método:
Isso me permite chamá-lo assim e inserir / atualizar as coleções subjacentes:
Observe como eu reatribui o valor recuperado ao objeto raiz original: agora ele está conectado a cada objeto anexado. A atualização do objeto raiz (o filme) é feita da mesma maneira:
fonte
Como você está lidando com diferentes campos
id
etmbid
, sugiro que atualizar a API para fazer um índice único e separada de todas as informações, como gêneros, línguas, palavras-chave etc ... E, em seguida, fazer uma chamada para indexar e buscar por informações em vez de encontro toda a informação sobre um objeto específico na sua classe de filme.fonte