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


103

Скажімо, я запитую базу даних і завантажую список елементів. Потім я відкриваю один із елементів у формі перегляду деталей, і замість того, щоб повторно запитувати елемент із бази даних, я створюю екземпляр елемента з джерела даних у списку.

Чи є спосіб я оновити запис бази даних, не виймаючи запис окремого елемента?

Ось зразок того, як я це роблю зараз:

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

Потім після витягування запису я оновлюю деякі значення в елементі і відсуваю запис назад:

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

Я думаю, що є кращий спосіб зробити це, будь-які ідеї?


2
Це не дуже страшний спосіб робити речі. Чи є у вас паралельний доступ до цієї таблиці?
Хенк Холтерман

Я б подумав, що це саме використання такого ORM, як EF. Щоб дозволити операції в контексті програми виконуватись на об'єктах, які ви хочете створити / змінити / видалити, не турбуючись про базову реалізацію БД?
Перо П.

40
Я думаю, що для розробників, які мають досвід роботи в TSQL, які намагаються прийняти і прийняти ORM, це трохи неефективно шукати запис лише для оновлення, і ніколи не використовувати отримані дані. Ця концепція, що розробнику не потрібно стосуватися базової реалізації БД, є сукупністю. Чим більше розробник знає про всю систему, тим кращим може бути рішення. Варіанти ніколи не є поганою справою.
barrypicker

1
Підхід ORM чудово підходить для фактичних об'єктів, але якщо ви також зберігаєте інші речі у вашій базі даних (наприклад, великі бінарні краплі), це може бути дуже корисним, щоб можна було оновити їх, не завантажуючи спочатку оригінальний вміст.
BrainSlugs83

Відповіді:


68

Вам слід скористатися методом Attach () .

Прикріплення та відокремлення об'єктів


16
ви можете навести приклад?
Барт Калікто

16
контекст.Продукти. Приєднання (продукт); контекст.вступ (продукт) .State = EntityState.Modified;
Габріель

6
@Gabriel Чи не буде оновлено всі властивості? Що робити, якщо я хочу змінити лише одну?
Девід Пфеффер

22
Так, це оновить усі властивості. Якщо ви хочете оновити одну властивість, ви можете зробити це: context.Entry (user) .Property (x => x.Property) .IsModified = true; (подивіться тут stackoverflow.com/a/5567616/57369 )
Габріель

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

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для виконання команди. Це не агностик бази даних, не кажучи вже про стійкість агностика (наприклад, цей приклад може вийти з ладу, якщо ОП перейде на XML).
just.another.programmer

9
@ just.another.programmer - з великою силою приходить велика відповідальність.
barrypicker

13
Чи повинен це бути наполегливим агностиком? Це не так, як ви будете змінювати систему зберігання через день.
Девід

5
@ BrainSlugs83 - спробуйте використовувати EF на серверах посилань, які підтримують лише OpenQuery - дуже весело. Іноді вам абсолютно потрібний сирий SQL, щоб виконати роботу. Не завжди можна скласти код ізольовано для тестування. Це не ідеальний світ там.
barrypicker

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 вже є нульовою, коли ви створили порожній об'єкт ExampleEntity, вам потрібно сказати EF, що цей стовпець повинен бути оновлений, і це мета цього рядка.


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

11

Якщо DataItemполя EF мають попередню валідацію (наприклад, поля, що не обнуляються), нам доведеться вимкнути цю перевірку для цього контексту:

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
Це робить те ж саме, що і проблема ОП: отримати весь запис, а потім оновити його. .Перший () десеріалізує об'єкт.
Джертер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.