Cập nhật bản ghi mà không cần truy vấn đầu tiên?


103

Giả sử tôi truy vấn cơ sở dữ liệu và tải một danh sách các mục. Sau đó, tôi mở một trong các mục trong biểu mẫu xem chi tiết và thay vì truy vấn lại mục đó từ cơ sở dữ liệu, tôi tạo một phiên bản của mục từ nguồn dữ liệu trong danh sách.

Có cách nào tôi có thể cập nhật bản ghi cơ sở dữ liệu mà không cần tìm nạp bản ghi của từng mục không?

Đây là một ví dụ về cách tôi đang làm điều đó bây giờ:

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

Sau đó, sau khi kéo bản ghi, tôi cập nhật một số giá trị trong mục và đẩy bản ghi trở lại:

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

Tôi sẽ nghĩ rằng sẽ có một cách tốt hơn để làm điều này, bất kỳ ý tưởng?


2
Đó không phải là một cách tồi tệ để làm mọi việc. Bạn có quyền truy cập đồng thời vào bảng đó không?
Henk Holterman

Tôi nghĩ rằng đây là cách sử dụng mà một ORM như EF chính xác ở đó để phục vụ. Để cho phép các hoạt động trong ngữ cảnh của ứng dụng được thực hiện trên các đối tượng bạn muốn tạo / sửa đổi / xóa mà không cần quan tâm đến việc triển khai DB bên dưới?
Pero P.

40
Tôi nghĩ rằng đối với các nhà phát triển có nền tảng về TSQL đang cố gắng chấp nhận và nắm lấy ORM, việc tra cứu bản ghi chỉ để cập nhật nó và không bao giờ sử dụng dữ liệu đã tìm nạp sẽ hơi kém hiệu quả. Khái niệm mà một nhà phát triển không cần phải quan tâm đến việc triển khai DB cơ bản là một sai lầm. Nhà phát triển càng biết nhiều về toàn bộ hệ thống, thì giải pháp càng tốt. Tùy chọn không bao giờ là một điều xấu.
barrypicker

1
Cách tiếp cận ORM phù hợp với các đối tượng thực tế, nhưng nếu bạn cũng lưu trữ những thứ khác trong cơ sở dữ liệu của mình (như các đốm màu nhị phân lớn) thì có thể cực kỳ hữu ích khi có thể cập nhật chúng mà không cần tải nội dung gốc trước.
BrainSlugs83

Câu trả lời:


68

Bạn nên sử dụng Attach () phương thức .

Đính kèm và tách đối tượng


16
bạn có thể cung cấp một ví dụ?
Bart Calixto

16
context.Products.Attach (sản phẩm); context.Entry (product) .State = EntityState.Modified;
Gabriel

6
@Gabriel Tuy nhiên, điều này sẽ không cập nhật tất cả các thuộc tính phải không? Điều gì sẽ xảy ra nếu tôi chỉ muốn sửa đổi một cái duy nhất?
David Pfeffer

22
Có, điều này sẽ cập nhật tất cả các thuộc tính. Nếu bạn muốn cập nhật một thuộc tính duy nhất, bạn có thể thực hiện điều này: context.Entry (user) .Property (x => x.Property) .IsModified = true; (xem ở đây stackoverflow.com/a/5567616/57369 )
Gabriel

6
Tôi chỉ muốn thêm ngữ cảnh đó .Entry () chỉ khả dụng trong .net 4.1, nếu bạn vẫn đang sử dụng 4.0 (giống như tôi) thì hãy kiểm tra điều này để có lựa chọn thay thế: stackoverflow.com/questions/7113434/where-is- context-entry về cơ bản là: context.ObjectStateManager.ChangeObjectState (yourObject, EntityState.Modified);
dyslexicanaboko

39

Bạn cũng có thể sử dụng SQL trực tiếp đối với cơ sở dữ liệu bằng cách sử dụng ngữ cảnh của kho dữ liệu. Thí dụ:

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

Vì lý do hiệu suất, bạn có thể muốn chuyển vào các biến thay vì một chuỗi SQL được mã hóa cứng. Điều này sẽ cho phép SQL Server lưu vào bộ nhớ cache truy vấn và sử dụng lại với các tham số. Thí dụ:

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

CẬP NHẬT - cho EF 6.0

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

9
Tại sao bạn lại hạ cấp câu trả lời này mà không để lại bình luận. Đề xuất này giải quyết ngay câu hỏi của các tác giả ban đầu.
barrypicker

18
ExecuteStoreCommandEF thực sự không phải là một cách thực hiện điều này, nó chỉ sử dụng DbConnectionhàm chứa bên trong DbContextđể thực hiện một lệnh. Nó không phải là bất khả tri về cơ sở dữ liệu, chứ đừng nói là bất khả tri về tính liên tục (ví dụ: ví dụ này sẽ bị lỗi nếu OP chuyển sang XML).
just.a another.programmer,

9
@ just.a another.programmer - với sức mạnh to lớn đi kèm với trách nhiệm lớn.
barrypicker

13
Nó có phải là bất khả tri bền bỉ không? Nó không giống như bạn sẽ thay đổi hệ thống lưu trữ của mình mỗi ngày.
David

5
@ BrainSlugs83 - hãy thử sử dụng EF trên các máy chủ liên kết chỉ hỗ trợ OpenQuery - rất thú vị. Đôi khi bạn hoàn toàn cần SQL thô để hoàn thành công việc. Không phải lúc nào bạn cũng có thể rút mã thành cô lập để thử nghiệm. Nó không phải là một thế giới hoàn hảo ngoài kia.
barrypicker

20

Mật mã:

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

Kết quả TSQL:

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

Ghi chú:

Dòng "IsModified = true" là cần thiết vì khi bạn tạo đối tượng ExampleEntity mới (chỉ với thuộc tính Id được điền) tất cả các thuộc tính khác đều có giá trị mặc định (0, null, v.v.). Nếu bạn muốn cập nhật DB với một "giá trị mặc định", thay đổi sẽ không được khung thực thể phát hiện và sau đó DB sẽ không được cập nhật.

Trong ví dụ:

exampleEntity.ExampleProperty = null;

sẽ không hoạt động nếu không có dòng "IsModified = true", bởi vì thuộc tính ExampleProperty, đã rỗng khi bạn tạo đối tượng ExampleEntity trống, bạn cần nói với EF rằng cột này phải được cập nhật và đây là mục đích của dòng này.


Đây là hoàn hảo. Tôi vừa thử nghiệm điều này và nó chính xác là những gì tôi muốn. Tôi muốn các thay đổi đi qua cơ sở hạ tầng EF (bao gồm cả việc sử dụng dự án EntityFramework.Triggers) nhưng muốn có thể thay đổi 1 cột mà chỉ có khóa chính.
MikeJansen

11

Nếu các DataItemtrường có EF sẽ xác thực trước (như các trường không thể null), chúng tôi sẽ phải tắt xác thực đó cho ngữ cảnh này:

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;

Nếu không, chúng tôi có thể thử đáp ứng xác thực trước và vẫn chỉ cập nhật cột đơn:

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

Giả sử dataEntitylà mộtSystem.Data.Entity.DbContext

Bạn có thể xác minh truy vấn được tạo bằng cách thêm truy vấn này vào DbContext:

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


0

Nó hoạt động hơi khác trong EF Core:

Có thể có một cách nhanh hơn để thực hiện việc này trong EF Core, nhưng cách sau đảm bảo CẬP NHẬT mà không cần phải thực hiện CHỌN (đã thử nghiệm với EF Core 2 và JET trên .NET Framework 4.6.2):

Đảm bảo mô hình của bạn không có thuộc tính IsRequired

Sau đó, sử dụng mẫu sau (trong 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

Nói chung, nếu bạn đã sử dụng Entity Framework để truy vấn tất cả các mục và bạn đã lưu đối tượng entity, bạn có thể cập nhật các mục riêng lẻ trong đối tượng entity và gọi SaveChanges()khi bạn hoàn tất. Ví dụ:

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

Việc truy xuất một mục bạn muốn không nên tạo ra một truy vấn mới.


Câu trả lời thú vị, đã có ai khác xác nhận điều này?
Ian

5
Điều này thực hiện tương tự như vấn đề của OP: tìm nạp toàn bộ bản ghi, sau đó cập nhật nó. .First () deserializing đối tượng.
Jerther
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.