Обновить запись без предварительного запроса?


104

Допустим, я запрашиваю базу данных и загружаю список элементов. Затем я открываю один из элементов в форме подробного просмотра и вместо повторного запроса элемента из базы данных создаю экземпляр элемента из источника данных в списке.

Есть ли способ обновить запись базы данных, не извлекая запись отдельного элемента?

Вот пример того, как я это делаю сейчас:

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

Затем после извлечения записи я обновляю некоторые значения в элементе и возвращаю запись:

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

Я думаю, есть лучший способ сделать это, есть идеи?


2
Это не так уж и плохо. У вас есть одновременный доступ к этой таблице?
— Хенк Холтерман

Я бы подумал, что это использование ORM, такого как EF, именно для этого. Чтобы разрешить операции в контексте приложения, которые будут выполняться с объектами, которые вы хотите создать / изменить / удалить, не заботясь о базовой реализации БД?
— Перо П.

41
Я думаю, что для разработчиков с опытом работы в TSQL, которые пытаются принять и использовать ORM, немного неэффективно искать запись только для ее обновления и никогда не использовать полученные данные. Идея о том, что разработчику не нужно беспокоиться о базовой реализации БД, является чушью. Чем больше разработчик знает обо всей системе, тем лучше может быть решение. Опции никогда не плохи.
— barrypicker

1
Подход ORM подходит для реальных объектов, но если вы также храните в своей базе данных другие вещи (например, большие двоичные капли), может быть очень полезно иметь возможность обновлять их без предварительной загрузки исходного содержимого.
— BrainSlugs83

Ответы:


68

Вам следует использовать метод Attach () .

Присоединение и отсоединение объектов


17
можешь привести пример?
— Барт Каликсто

17
context.Products.Attach (продукт); context.Entry (product) .State = EntityState.Modified;
— Габриэль

7
@Gabriel Но разве это не обновит все свойства? Что, если я хочу изменить только один?
— Дэвид Пфеффер

22
Да, это обновит все свойства. Если вы хотите обновить отдельное свойство, вы можете сделать это: context.Entry (user) .Property (x => x.Property) .IsModified = true; (посмотрите здесь stackoverflow.com/a/5567616/57369 )
— Габриэль

6
Я просто хотел бы добавить, что context.Entry () доступен только в .net 4.1, если вы все еще используете 4.0 (как я), проверьте это для альтернативы: stackoverflow.com/questions/7113434/where-is- контекстная запись, которая по сути: context.ObjectStateManager.ChangeObjectState (yourObject, EntityState.Modified);
— dyslexicanaboko

39

Вы также можете использовать прямой SQL для базы данных, используя контекст хранилища данных. Пример:

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

По соображениям производительности вы можете захотеть передать переменные вместо одной жестко закодированной строки SQL. Это позволит SQL Server кэшировать запрос и повторно использовать его с параметрами. Пример:

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

ОБНОВЛЕНИЕ - для EF 6.0

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

9
почему бы вам понизить этот ответ, не оставляя комментария. Это предложение решает вопрос авторов оригинала.
— barrypicker

18
ExecuteStoreCommandна самом деле это не способ EF, он просто использует DbConnectionсодержимое внутри DbContextдля выполнения команды. Это не агностик базы данных, не говоря уже о агностике персистентности (например, в этом примере произойдет сбой, если OP переключится на XML).
— just.another.programmer 07

9
@ just.another.programmer - с большой силой приходит большая ответственность.
— barrypicker

13
Должен ли он быть агностиком настойчивости? Вы же не собираетесь менять систему хранения через день.
— Дэвид

5
@ BrainSlugs83 - попробуйте использовать EF на серверах ссылок, которые поддерживают только OpenQuery - очень весело. Иногда для выполнения работы вам абсолютно необходим необработанный SQL. Не всегда можно выделить код для тестирования. Это не идеальный мир.
— barrypicker 03

20

Код:

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();

Результат TSQL:

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

Примечание:

Строка «IsModified = true» необходима, потому что при создании нового объекта ExampleEntity (только с заполненным свойством Id) все остальные свойства имеют значения по умолчанию (0, null и т. Д.). Если вы хотите обновить БД «значением по умолчанию», изменение не будет обнаружено структурой сущности, и тогда БД не будет обновлена.

Например:

exampleEntity.ExampleProperty = null;

не будет работать без строки «IsModified = true», поскольку свойство ExampleProperty уже имеет значение null, когда вы создавали пустой объект ExampleEntity, вам нужно сказать EF, что этот столбец должен быть обновлен, и это цель этой строки.


Это потрясающе. Я только что это проверил, и это именно то, что я хотел. Я хочу, чтобы изменения проходили через инфраструктуру EF (включая использование проекта EntityFramework.Triggers), но я хотел иметь возможность изменить 1 столбец, имея только первичный ключ.
— MikeJansen

11

Если поле DataItemhas EF будет предварительно проверять (например, поля, не допускающие значения NULL), нам придется отключить эту проверку для этого контекста:

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;

В противном случае мы можем попытаться выполнить предварительную проверку и по-прежнему обновить только один столбец:

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();

Предполагая, dataEntityчто этоSystem.Data.Entity.DbContext

Вы можете проверить созданный запрос, добавив его в DbContext:

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

2

В этой статье, являющейся частью руководства Microsoft по началу работы, объясняются состояния сущностей и способы их выполнения:

Состояния добавления / присоединения и сущности

Посмотрите раздел «Присоединение существующей, но измененной сущности к контексту»

Теперь я собираюсь прочитать оставшуюся часть этих руководств.


0

В EF Core это работает несколько иначе:

В EF Core может быть более быстрый способ сделать это, но следующее обеспечивает ОБНОВЛЕНИЕ без необходимости выполнять SELECT (проверено с EF Core 2 и JET в .NET Framework 4.6.2):

Убедитесь, что ваша модель не имеет свойств IsRequired

Затем используйте следующий шаблон (в 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

-1

Вообще говоря, если вы использовали Entity Framework для запроса всех элементов и сохранили объект сущности, вы можете обновить отдельные элементы в объекте сущности и вызвать его, SaveChanges()когда закончите. Например:

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();

Получение одного элемента, который вы хотите, не должно приводить к созданию нового запроса.


Интересный ответ, еще кто-нибудь это подтверждал?
— Ян

5
Это делает то же самое, что и проблема OP: получает всю запись, а затем обновляет ее. .First () десериализует объект.
— Джертер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.