O método RevertChangesSingleEntity() tem suas restrições, por exemplo não trata o caso da entity ter sido adicionada. Este cenário deve ser revertido com o detach da entity de seu context, embora a entidade continuará a existir fora dele. Importante frisar que o método ObjectContext.Detach() atua em uma única entity por vez, e desintegra o grafo que participava, pois não pode haver parte do grafo no contexto e outra parte fora. Pode pensar nisso como o gato de Schrödinger. Isto significa que o se eu tiver um Pedido e uma coleção ItemDoPedido adicionados no contexto, para reverter esta inclusão é preciso detach cada objeto individualmente. Entretanto o Pedido ficaria desconectado de seus ítens. Outra característica do detach é que a entrada (classe ObjectStateEntry) no change tracker permanece com a EntityKey e o State anterior, porém a propriedade Entity será nula pois foi desconectada.
Uma outra maneira de parar o change tracking dos objeto é executar IEntityWithChangeTracker.SetChangeTracker() passando null como o único parâmetro. Isto remove o IEntityChangeTracker que monitora e registra as alterações no objeto. A vantagem disso é que o grafo permanece intacto, e a desvantagem é que parte do grafo continua registrando as alterações e parte não mais.
Acontece que adicionar/deletar uma entidade em um relacionamento implica em manter outras entradas no ObjectStateManager referente aos relacionamentos, como podemos ver na imagem a seguir.
Segue código para tratar a reversão das associações:
public static void RevertChanges(this ObjectContext db)
{
foreach (var ose in db.ObjectStateManager.GetObjectStateEntries(EntityState.Added))
ose.Delete();
foreach (var ose in db.ObjectStateManager.GetObjectStateEntries(EntityState.Modified))
{
if (!ose.IsRelationship)
{
var org = ose.GetUpdatableOriginalValues();
var props = ose.GetModifiedProperties();
foreach (var prop in props)
{
var x = ose.Entity.GetType().GetProperty(prop);
x.SetValue(ose.Entity, org.GetValue(org.GetOrdinal(prop)), null);
}
}
}
foreach (var ose in db.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted))
if (!ose.IsRelationship)
ose.ChangeState(EntityState.Unchanged);
db.AcceptAllChanges();
}
(to be continued...)
Mostrando postagens com marcador entity framework. Mostrar todas as postagens
Mostrando postagens com marcador entity framework. Mostrar todas as postagens
sexta-feira, 9 de abril de 2010
quinta-feira, 8 de abril de 2010
Cancelar alterações na entity (ou delta ou changeset)
Você já tentou criar um cadastro mestre-detalhe com o Entity Framework? Pois bem, eu estou tentando chegar lá...
Como eu venho de um background data-centric, onde datasets são a pattern mais utilizada para a) persistir dados em memória; b) manter changeset; c) prover dados para as telas, o trabalho de controlar a edição de objetos é bastante simples. Cancelar as alterações de um registro ou de um conjunto inteiro de registros é moleza. Isto se deve a uma outra estrutura conhecida como delta (ou ainda changeset), que mantém cada inclusão/edição/exclusão de dados. Para reverter as alterações em memória, basta limpar o buffer do registro corrente ou então limpar o delta.
No Entity Framework as coisas não são simples assim. Não importa se o projeto segue uma abordagem code-first, database-first ou model-first. Se fosse fácil haveria um método RevertChanges() ou na entidade ou no ObjectContext. Um CRUD fica complicado de se implementar ou no mínimo moroso, sem auxílio da camada de apresentação. Claro que isso tem um aspecto positivo, força o desenvolvedor a planejar um modelo de apresentação independente do modelo de dados. Mas voltemos ao assunto do post.
O cenário mais básico é reverter as edições em atributos escalares ou string, ou seja, todos exceto os relacionamentos com outras entidades. O ObjectStateManager armazena uma entrada para cada entidade no contexto, juntamente com os valores originais. Podemos copiar os valores originais novamente na entidade:
public static void RevertChangesSingleEntity(this ObjectContext db, EntityObject entity)
{
// reverte alterações em propriedades escalares de uma entidade
ObjectStateEntry ose;
if (db.ObjectStateManager.TryGetObjectStateEntry(entity, out ose))
{
var org = ose.GetUpdatableOriginalValues();
var props = ose.GetModifiedProperties();
foreach (var prop in props)
{
var x = entity.GetType().GetProperty(prop);
x.SetValue(entity, org.GetValue(org.GetOrdinal(prop)), null);
}
ose.AcceptChanges();
}
}
Bom, então se bastam estas linhas de código, porque não está incluído no EF? Acho que o time responsável pelo framework não tem conhecimento preciso das necessidades de um cenário como este para um ORM...
(to be continued...)
Como eu venho de um background data-centric, onde datasets são a pattern mais utilizada para a) persistir dados em memória; b) manter changeset; c) prover dados para as telas, o trabalho de controlar a edição de objetos é bastante simples. Cancelar as alterações de um registro ou de um conjunto inteiro de registros é moleza. Isto se deve a uma outra estrutura conhecida como delta (ou ainda changeset), que mantém cada inclusão/edição/exclusão de dados. Para reverter as alterações em memória, basta limpar o buffer do registro corrente ou então limpar o delta.
No Entity Framework as coisas não são simples assim. Não importa se o projeto segue uma abordagem code-first, database-first ou model-first. Se fosse fácil haveria um método RevertChanges() ou na entidade ou no ObjectContext. Um CRUD fica complicado de se implementar ou no mínimo moroso, sem auxílio da camada de apresentação. Claro que isso tem um aspecto positivo, força o desenvolvedor a planejar um modelo de apresentação independente do modelo de dados. Mas voltemos ao assunto do post.
O cenário mais básico é reverter as edições em atributos escalares ou string, ou seja, todos exceto os relacionamentos com outras entidades. O ObjectStateManager armazena uma entrada para cada entidade no contexto, juntamente com os valores originais. Podemos copiar os valores originais novamente na entidade:
public static void RevertChangesSingleEntity(this ObjectContext db, EntityObject entity)
{
// reverte alterações em propriedades escalares de uma entidade
ObjectStateEntry ose;
if (db.ObjectStateManager.TryGetObjectStateEntry(entity, out ose))
{
var org = ose.GetUpdatableOriginalValues();
var props = ose.GetModifiedProperties();
foreach (var prop in props)
{
var x = entity.GetType().GetProperty(prop);
x.SetValue(entity, org.GetValue(org.GetOrdinal(prop)), null);
}
ose.AcceptChanges();
}
}
Bom, então se bastam estas linhas de código, porque não está incluído no EF? Acho que o time responsável pelo framework não tem conhecimento preciso das necessidades de um cenário como este para um ORM...
(to be continued...)
Assinar:
Postagens (Atom)