Perbarui catatan tanpa membuat kueri terlebih dahulu?

104

Katakanlah saya menanyakan database dan memuat daftar item. Kemudian saya membuka salah satu item dalam bentuk tampilan detail, dan alih-alih membuat kueri ulang item tersebut dari database, saya membuat instance item dari sumber data dalam daftar.

Adakah cara agar saya dapat memperbarui catatan basis data tanpa mengambil catatan dari masing-masing item?

Berikut adalah contoh bagaimana saya melakukannya sekarang:

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

Kemudian setelah menarik catatan saya memperbarui beberapa nilai dalam item dan mendorong catatan kembali:

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

Saya akan berpikir akan ada cara yang lebih baik untuk melakukan ini, ada ide?

Shane Grant
sumber
2
Ini bukan cara yang buruk untuk melakukan sesuatu. Apakah Anda memiliki akses bersamaan ke tabel itu?
Henk Holterman
Saya akan berpikir ini adalah penggunaan yang ORM seperti EF tepat untuk dilayani. Untuk mengizinkan operasi dalam konteks aplikasi yang akan dilakukan pada objek yang ingin Anda buat / modifikasi / hapus, tanpa memperhatikan implementasi DB yang mendasarinya?
Pero P.
40
Saya pikir untuk pengembang dengan latar belakang TSQL mencoba menerima dan menerima ORM, agak tidak efisien untuk mencari catatan hanya untuk memperbaruinya, dan tidak pernah memanfaatkan data yang diambil. Konsep bahwa pengembang tidak perlu peduli dengan implementasi DB yang mendasarinya adalah tempayan. Semakin banyak pengembang mengetahui tentang keseluruhan sistem, semakin baik solusinya. Pilihan bukanlah hal yang buruk.
barrypicker
1
Pendekatan ORM baik-baik saja untuk objek sebenarnya, tetapi jika Anda juga menyimpan hal-hal lain dalam database Anda (seperti gumpalan biner besar), akan sangat berguna untuk dapat memperbaruinya tanpa memuat konten asli terlebih dahulu.
BrainSlugs83

Jawaban:

68

Anda harus menggunakan metode Attach () .

Memasang dan Memisahkan Objek

CD..
sumber
16
dapatkah anda memberikan contoh?
Bart Calixto
17
context.Products.Attach (produk); context.Entry (product) .State = EntityState.Modified;
Gabriel
6
@Gabriel Akankah ini memperbarui semua properti? Bagaimana jika saya hanya ingin memodifikasi satu saja?
David Pfeffer
22
Ya ini akan memperbarui semua properti. Jika Anda ingin memperbarui satu properti, Anda dapat melakukan ini: context.Entry (user) .Property (x => x.Property) .IsModified = true; (lihat di sini stackoverflow.com/a/5567616/57369 )
Gabriel
6
Saya hanya ingin menambahkan konteks itu.Entry () hanya tersedia di .net 4.1, jika Anda masih menggunakan 4.0 (seperti saya) maka periksa ini untuk alternatif: stackoverflow.com/questions/7113434/where-is- konteks-entri yang pada dasarnya adalah: context.ObjectStateManager.ChangeObjectState (yourObject, EntityState.Modified);
dyslexicanaboko
39

Anda juga dapat menggunakan SQL langsung terhadap database menggunakan konteks datastore. Contoh:

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

Untuk alasan kinerja, Anda mungkin ingin meneruskan variabel daripada satu string SQL kode keras. Ini akan memungkinkan SQL Server untuk menyimpan kueri dan menggunakan kembali dengan parameter. Contoh:

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

UPDATE - untuk EF 6.0

dataEntity.Database.ExecuteSqlCommand
       ("UPDATE items SET itemstatus = 'some status' WHERE id = {0}", new object[] { 123 });
barrypicker
sumber
9
mengapa Anda menurunkan jawaban ini tanpa meninggalkan komentar. Saran ini menjawab pertanyaan penulis asli tepat.
barrypicker
18
ExecuteStoreCommandsebenarnya bukan cara EF untuk melakukan ini, ini hanya menggunakan yang DbConnectionterkandung di dalam DbContextuntuk menjalankan perintah. Ini bukan database agnostik, apalagi persistence agnostic (contoh ini akan crash jika OP dialihkan ke XML).
just.another.programmer
9
@ just.another.programmer - dengan kekuatan besar datanglah tanggung jawab yang besar.
barrypicker
13
Apakah itu harus agnostik ketekunan? Ini tidak seperti Anda akan mengubah sistem penyimpanan Anda setiap hari.
David
5
@ BrainSlugs83 - coba gunakan EF di seluruh server tautan yang hanya mendukung OpenQuery - sangat menyenangkan. Terkadang Anda benar-benar membutuhkan SQL mentah untuk menyelesaikan pekerjaan. Tidak selalu Anda dapat menarik kode ke dalam isolasi untuk pengujian. Ini bukan dunia yang sempurna di luar sana.
barrypicker
20

Kode:

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

Hasil TSQL:

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

catatan:

Baris "IsModified = true", diperlukan karena saat Anda membuat objek ExampleEntity baru (hanya dengan properti Id terisi) semua properti lainnya memiliki nilai default (0, null, dll). Jika Anda ingin memperbarui DB dengan "nilai default", perubahan tidak akan terdeteksi oleh kerangka entitas, dan kemudian DB tidak akan diperbarui.

Contoh:

exampleEntity.ExampleProperty = null;

tidak akan bekerja tanpa baris "IsModified = true", karena properti ExampleProperty sudah null saat Anda membuat objek ExampleEntity kosong, Anda perlu mengatakan kepada EF bahwa kolom ini harus diperbarui, dan inilah tujuan baris ini.

tecla
sumber
Ini sempurna. Saya baru saja menguji ini dan itulah yang saya inginkan. Saya ingin perubahan melalui infrastruktur EF (termasuk menggunakan proyek EntityFramework.Triggers) tetapi ingin dapat mengubah 1 kolom hanya dengan memiliki kunci utama.
MikeJansen
11

Jika kolom DataItemhas EF akan melakukan pra-validasi (seperti kolom non-nullable), kita harus menonaktifkan validasi tersebut untuk konteks ini:

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;

Jika tidak, kami dapat mencoba memenuhi pra-validasi dan tetap hanya memperbarui kolom tunggal:

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

Dengan asumsi dataEntityadalah aSystem.Data.Entity.DbContext

Anda dapat memverifikasi kueri yang dihasilkan dengan menambahkan ini ke DbContext:

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

Ini bekerja agak berbeda di EF Core:

Mungkin ada cara yang lebih cepat untuk melakukan ini di EF Core, tetapi berikut ini memastikan UPDATE tanpa harus melakukan PILIHAN (diuji dengan EF Core 2 dan JET di .NET Framework 4.6.2):

Pastikan model Anda tidak memiliki properti IsRequired

Kemudian gunakan template berikut (di 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
sumber
-1

Secara umum, jika Anda menggunakan Entity Framework untuk meminta semua item, dan Anda menyimpan objek entitas, Anda dapat memperbarui item individual di objek entitas dan menelepon SaveChanges()saat Anda selesai. Sebagai contoh:

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

Pengambilan satu item yang Anda inginkan seharusnya tidak menghasilkan kueri baru.

Andrew
sumber
Jawaban yang menarik, apakah ada orang lain yang mengkonfirmasi ini?
Ian
5
Ini melakukan hal yang sama dengan masalah OP: ambil seluruh catatan, lalu perbarui. .First () deserialisasi objek.
Jerther