Atualizar um registro sem primeiro consultar?

104

Digamos que eu consulte o banco de dados e carregue uma lista de itens. Em seguida, abro um dos itens em um formulário de exibição de detalhes e, em vez de consultar novamente o item do banco de dados, crio uma instância do item a partir da fonte de dados na lista.

Existe uma maneira de atualizar o registro do banco de dados sem buscar o registro do item individual?

Aqui está um exemplo de como estou fazendo isso agora:

dataItem itemToUpdate = (from t in dataEntity.items
                                 where t.id == id
                                 select t).FirstOrDefault();

Então, depois de obter o registro, atualizo alguns valores no item e empurro o registro de volta:

itemToUpdate.itemstatus = newStatus;
dataEntity.SaveChanges();

Eu acho que haveria uma maneira melhor de fazer isso, alguma ideia?

Shane Grant
fonte
2
Não é uma maneira terrivelmente ruim de fazer as coisas. Você tem acesso simultâneo a essa mesa?
Henk Holterman
Eu acho que este é o uso que um ORM como EF está exatamente lá para servir. Para permitir que as operações dentro do contexto do aplicativo sejam realizadas nos objetos que você deseja criar / modificar / excluir, sem se preocupar com a implementação do banco de dados subjacente?
Pero P.
40
Acho que para desenvolvedores com experiência em TSQL tentando aceitar e abraçar ORMs, é um pouco ineficiente pesquisar um registro apenas para atualizá-lo e nunca utilizar os dados buscados. Esse conceito de que um desenvolvedor não precisa se preocupar com a implementação do banco de dados subjacente é um crock. Quanto mais um desenvolvedor conhece todo o sistema, melhor pode ser a solução. Opções nunca são ruins.
barrypicker
1
A abordagem ORM é boa para objetos reais, mas se você também armazenar outras coisas em seu banco de dados (como grandes blobs binários), pode ser muito útil poder atualizá-los sem carregar o conteúdo original primeiro.
BrainSlugs83

Respostas:

68

Você deve usar o Attach () método .

Anexando e Desanexando Objetos

CD..
fonte
16
você pode dar um exemplo?
Bart Calixto
16
context.Products.Attach (produto); context.Entry (produto) .State = EntityState.Modified;
Gabriel
6
@Gabriel Isso não irá atualizar todas as propriedades? E se eu só quiser modificar um?
David Pfeffer
22
Sim, isso atualizará todas as propriedades. Se você quiser atualizar uma única propriedade, pode fazer isso: context.Entry (user) .Property (x => x.Property) .IsModified = true; (dê uma olhada aqui stackoverflow.com/a/5567616/57369 )
Gabriel
6
Eu gostaria apenas de adicionar esse context.Entry () está disponível apenas em .net 4.1, se você ainda estiver usando 4.0 (como eu), verifique esta alternativa: stackoverflow.com/questions/7113434/where-is- entrada de contexto que é essencialmente: context.ObjectStateManager.ChangeObjectState (yourObject, EntityState.Modified);
dyslexicanaboko
39

Você também pode usar SQL direto no banco de dados usando o contexto do armazenamento de dados. Exemplo:

dataEntity.ExecuteStoreCommand
   ("UPDATE items SET itemstatus = 'some status' WHERE id = 123 ");

Por motivos de desempenho, você pode querer passar variáveis ​​em vez de uma única string SQL codificada. Isso permitirá que o SQL Server armazene a consulta em cache e a reutilize com parâmetros. Exemplo:

dataEntity.ExecuteStoreCommand
   ("UPDATE items SET itemstatus = 'some status' WHERE id = {0}", new object[] { 123 });

ATUALIZAÇÃO - para EF 6.0

dataEntity.Database.ExecuteSqlCommand
       ("UPDATE items SET itemstatus = 'some status' WHERE id = {0}", new object[] { 123 });
barrypicker
fonte
9
por que você faria o downgrade desta resposta sem deixar um comentário. Esta sugestão aborda a questão do autor original na hora.
barrypicker
18
ExecuteStoreCommandnão é realmente uma maneira EF de fazer isso, é apenas usar o DbConnectioncontido dentro do DbContextpara executar um comando. Não é agnóstico de banco de dados, muito menos agnóstico de persistência (por exemplo, este exemplo travaria se o OP mudasse para XML).
just.another.programmer
9
@ just.another.programmer - com grande poder vêm grandes responsabilidades.
barrypicker
13
Tem que ser agnóstico de persistência? Não é como se você fosse mudar seu sistema de armazenamento a cada dois dias.
David,
5
@ BrainSlugs83 - tente usar EF em servidores de link que suportam apenas OpenQuery - muito divertido. Às vezes, você precisa absolutamente de SQL bruto para fazer o trabalho. Nem sempre você pode isolar o código para teste. Não é um mundo perfeito lá fora.
barrypicker
20

O código:

ExampleEntity exampleEntity = dbcontext.ExampleEntities.Attach(new ExampleEntity { Id = 1 });
exampleEntity.ExampleProperty = "abc";
dbcontext.Entry<ExampleEntity>(exampleEntity).Property(ee => ee.ExampleProperty).IsModified = true;
dbcontext.Configuration.ValidateOnSaveEnabled = false;
dbcontext.SaveChanges();

O resultado TSQL:

exec sp_executesql N'UPDATE [dbo].[ExampleEntities]
SET [ExampleProperty ] = @0
WHERE ([Id] = @1)
',N'@0 nvarchar(32),@1 bigint',@0='abc',@1=1

Nota:

A linha "IsModified = true" é necessária porque quando você cria o novo objeto ExampleEntity (somente com a propriedade Id preenchida) todas as outras propriedades têm seus valores padrão (0, nulo, etc). Se você deseja atualizar o banco de dados com um "valor padrão", a alteração não será detectada pela estrutura da entidade e, em seguida, o banco de dados não será atualizado.

Por exemplo:

exampleEntity.ExampleProperty = null;

não funcionará sem a linha "IsModified = true", pois a propriedade ExampleProperty, já é nula quando você criou o objeto ExampleEntity vazio, você precisa dizer ao EF que esta coluna deve ser atualizada, e esse é o propósito desta linha.

tecla
fonte
Isto é perfeito. Acabei de testar isso e é exatamente o que eu queria. Eu quero que as alterações passem pela infraestrutura EF (incluindo o uso do projeto EntityFramework.Triggers), mas queria poder alterar 1 coluna tendo apenas a chave primária.
MikeJansen
11

Se os DataItemcampos EF forem pré-validados (como campos não anuláveis), teremos que desabilitar essa validação para este contexto:

DataItem itemToUpdate = new DataItem { Id = id, Itemstatus = newStatus };
dataEntity.Entry(itemToUpdate).Property(x => x.Itemstatus).IsModified = true;
dataEntity.Configuration.ValidateOnSaveEnabled = false;
dataEntity.SaveChanges();
//dataEntity.Configuration.ValidateOnSaveEnabled = true;

Caso contrário, podemos tentar satisfazer a pré-validação e ainda atualizar apenas uma única coluna:

DataItem itemToUpdate = new DataItem
{
    Id = id,
    Itemstatus = newStatus,
    NonNullableColumn = "this value is disregarded - the db original will remain"
};
dataEntity.Entry(itemToUpdate).Property(x => x.Itemstatus).IsModified = true;
dataEntity.SaveChanges();

Supondo que dataEntityseja umSystem.Data.Entity.DbContext

Você pode verificar a consulta gerada adicionando-o a DbContext:

/*dataEntity.*/Database.Log = m => System.Diagnostics.Debug.Write(m);
Aske B.
fonte
0

Funciona um pouco diferente no EF Core:

Pode haver uma maneira mais rápida de fazer isso no EF Core, mas o seguinte garante um UPDATE sem ter que fazer um SELECT (testado com EF Core 2 e JET no .NET Framework 4.6.2):

Certifique-se de que seu modelo não tenha propriedades IsRequired

Em seguida, use o seguinte modelo (em VB.NET):

    Using dbContext = new MyContext()
        Dim bewegung = dbContext.MyTable.Attach(New MyTable())
        bewegung.Entity.myKey = someKey
        bewegung.Entity.myOtherField = "1"

        dbContext.Entry(bewegung.Entity).State = EntityState.Modified
        dbContext.Update(bewegung.Entity)

        Dim BewegungenDescription = (From tp In dbContext.Model.GetEntityTypes() Where tp.ClrType.Name = "MyTable" Select tp).First()
        For Each p In (From prop In BewegungenDescription.GetProperties() Select prop)
            Dim pp = dbContext.Entry(bewegung.Entity).Property(p.Name)
            pp.IsModified = False
        Next
        dbContext.Entry(bewegung.Entity).Property(Function(row) row.myOtherField).IsModified = True
        dbContext.SaveChanges()
    End Using
Wolfgang Grinfeld
fonte
-1

De modo geral, se você usou o Entity Framework para consultar todos os itens e salvou o objeto de entidade, poderá atualizar os itens individuais no objeto de entidade e chamar SaveChanges()quando terminar. Por exemplo:

var items = dataEntity.Include("items").items;
// For each one you want to change:
items.First(item => item.id == theIdYouWant).itemstatus = newStatus;
// After all changes:
dataEntity.SaveChanges();

A recuperação de um item que você deseja não deve gerar uma nova consulta.

Andrew
fonte
Resposta interessante, mais alguém confirmou isso?
Ian
5
Isso faz a mesma coisa que o problema do OP: buscar o registro inteiro e atualizá-lo. .First () desserializa o objeto.
Jerther