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

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

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

CD..
источник
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 по началу работы, объясняются состояния сущностей и способы их выполнения:

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

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

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

Simon_Weaver
источник
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 () десериализует объект.
Джертер