Entity Framework 5 Cập nhật một bản ghi


870

Tôi đã khám phá các phương pháp chỉnh sửa / cập nhật bản ghi khác nhau trong Entity Framework 5 trong môi trường ASP.NET MVC3, nhưng cho đến nay không có cái nào trong số chúng đánh dấu vào tất cả các hộp tôi cần. Tôi sẽ giải thích tại sao.

Tôi đã tìm thấy ba phương pháp mà tôi sẽ đề cập đến những ưu và nhược điểm:

Phương pháp 1 - Tải bản ghi gốc, cập nhật từng thuộc tính

var original = db.Users.Find(updatedUser.UserId);

if (original != null)
{
    original.BusinessEntityId = updatedUser.BusinessEntityId;
    original.Email = updatedUser.Email;
    original.EmployeeId = updatedUser.EmployeeId;
    original.Forename = updatedUser.Forename;
    original.Surname = updatedUser.Surname;
    original.Telephone = updatedUser.Telephone;
    original.Title = updatedUser.Title;
    original.Fax = updatedUser.Fax;
    original.ASPNetUserId = updatedUser.ASPNetUserId;
    db.SaveChanges();
}    

Ưu

  • Có thể chỉ định các thuộc tính thay đổi
  • Lượt xem không cần chứa mọi tài sản

Nhược điểm

  • 2 x truy vấn trên cơ sở dữ liệu để tải bản gốc, sau đó cập nhật nó

Phương pháp 2 - Tải bản ghi gốc, đặt giá trị thay đổi

var original = db.Users.Find(updatedUser.UserId);

if (original != null)
{
    db.Entry(original).CurrentValues.SetValues(updatedUser);
    db.SaveChanges();
}

Ưu

  • Chỉ các thuộc tính được sửa đổi được gửi đến cơ sở dữ liệu

Nhược điểm

  • Lượt xem cần chứa mọi tài sản
  • 2 x truy vấn trên cơ sở dữ liệu để tải bản gốc, sau đó cập nhật nó

Phương pháp 3 - Đính kèm bản ghi được cập nhật và đặt trạng thái thành EntityState.Modified

db.Users.Attach(updatedUser);
db.Entry(updatedUser).State = EntityState.Modified;
db.SaveChanges();

Ưu

  • 1 x truy vấn trên cơ sở dữ liệu để cập nhật

Nhược điểm

  • Không thể chỉ định thuộc tính nào thay đổi
  • Lượt xem phải chứa mọi tài sản

Câu hỏi

Câu hỏi của tôi cho các bạn; Có cách nào trong sạch để tôi có thể đạt được mục tiêu này không?

  • Có thể chỉ định các thuộc tính thay đổi
  • Lượt xem không cần chứa mọi thuộc tính (như mật khẩu!)
  • 1 x truy vấn trên cơ sở dữ liệu để cập nhật

Tôi hiểu đây là một điều khá nhỏ để chỉ ra nhưng tôi có thể đang thiếu một giải pháp đơn giản cho việc này. Nếu không phương pháp người ta sẽ thắng thế ;-)


13
Sử dụng ViewModels và một công cụ lập bản đồ tốt? Bạn chỉ nhận được "thuộc tính để cập nhật" để đưa vào chế độ xem của mình (và sau đó cập nhật). Vẫn sẽ có 2 truy vấn để cập nhật (lấy bản gốc + cập nhật nó), nhưng tôi sẽ không gọi đây là "Con". Nếu đó là vấn đề hiệu suất duy nhất của bạn, bạn là một người đàn ông hạnh phúc;)
Raphaël Althaus 11/03/13

Cảm ơn @ RaphaëlAlthaus, điểm rất hợp lệ. Tôi có thể làm điều này, nhưng tôi phải tạo hoạt động CRUD cho một số bảng vì vậy tôi đang tìm kiếm một phương thức có thể làm việc trực tiếp với mô hình để tiết kiệm cho tôi tạo n-1 ViewModel cho mỗi Mô hình.
Stokingout

3
Chà, trong dự án hiện tại của tôi (nhiều thực thể nữa), chúng tôi đã bắt đầu làm việc với các Mô hình, nghĩ rằng chúng tôi sẽ mất thời gian làm việc với ViewModels. Bây giờ chúng ta sẽ đến ViewModels và với cơ sở hạ tầng (không đáng kể) bắt đầu, giờ đây, rất xa, rõ ràng và dễ bảo trì hơn bây giờ. Và an toàn hơn (không cần phải lo sợ về "các trường ẩn" độc hại hoặc những thứ tương tự)
Raphaël Althaus

1
Và không còn nữa (khủng khiếp) ViewBags để điền vào DropDownLists của bạn (chúng tôi có ít nhất một DropDownList trên hầu hết tất cả các lượt xem CRU (D) của chúng tôi ...)
Raphaël Althaus 11/03/13

Tôi nghĩ bạn đúng, xấu của tôi vì đã cố gắng bỏ qua ViewModels. Có, đôi khi ViewBag có vẻ hơi bẩn. Tôi thường đi xa hơn một bước theo blog của Dino Esposito và cũng tạo InputModels, một vành đai và niềng răng nhưng nó hoạt động khá tốt. Chỉ có nghĩa là 2 mô hình bổ sung cho mỗi mô hình - doh ;-)
Stokingout

Câu trả lời:


681

Bạn đang tìm kiếm:

db.Users.Attach(updatedUser);
var entry = db.Entry(updatedUser);
entry.Property(e => e.Email).IsModified = true;
// other changed properties
db.SaveChanges();

59
xin chào @Ladislav Mrnka, nếu tôi muốn cập nhật tất cả các thuộc tính cùng một lúc, tôi có thể sử dụng mã dưới đây không? db.Depemony.Attach (bộ phận); db.Entry (bộ phận) .State = EntityState.Modified; db.SaveChanges ();
Foyzul Karim

23
@Foysal: Có bạn có thể.
Ladislav Mrnka

5
Một trong những vấn đề với cách tiếp cận này là bạn không thể chế nhạo db.Entry (), đây là một PITA nghiêm trọng. EF có một câu chuyện chế giễu khá hợp lý ở nơi khác - thật khó chịu khi (theo như tôi có thể nói) họ không có ở đây.
Ken Smith

23
@Foysal Làm bối cảnh.Entry (thực thể) .State = EntityState.Modified một mình là đủ không cần phải thực hiện đính kèm. Nó sẽ được tự động đính kèm khi được sửa đổi ...
HelloWorld

4
@ Sandman4, điều đó có nghĩa là mọi tài sản khác cần phải ở đó và được đặt thành giá trị hiện tại. Trong một số thiết kế ứng dụng, điều này không khả thi.
Dan Esparza

176

Tôi thực sự thích câu trả lời được chấp nhận. Tôi tin rằng có một cách khác để tiếp cận điều này là tốt. Giả sử bạn có một danh sách ngắn các thuộc tính mà bạn không muốn đưa vào Chế độ xem, vì vậy khi cập nhật thực thể, chúng sẽ bị bỏ qua. Hãy nói rằng hai trường đó là Mật khẩu và SSN.

db.Users.Attach(updatedUser);

var entry = db.Entry(updatedUser);
entry.State = EntityState.Modified;

entry.Property(e => e.Password).IsModified = false;
entry.Property(e => e.SSN).IsModified = false;   

db.SaveChanges();   

Ví dụ này cho phép bạn về cơ bản để logic kinh doanh của bạn một mình sau khi thêm một trường mới vào bảng Người dùng và vào Chế độ xem của bạn.


Tuy nhiên, tôi sẽ nhận được lỗi nếu tôi không chỉ định giá trị cho thuộc tính SSN, mặc dù tôi đã đặt IsModified thành false, nó vẫn xác thực thuộc tính theo quy tắc mô hình. Vì vậy, nếu thuộc tính được đánh dấu là KHÔNG NULL, nó sẽ thất bại nếu tôi không đặt bất kỳ giá trị nào khác với null.
RolandoCC

Bạn sẽ không nhận được lỗi vì các trường đó sẽ không ở dạng của bạn. Bạn bỏ qua các trường mà bạn chắc chắn sẽ không cập nhật, lấy mục nhập từ cơ sở dữ liệu bằng cách sử dụng biểu mẫu được gửi lại bằng cách đính kèm và nói với mục đó rằng các trường đó không bị sửa đổi. Xác thực mô hình được kiểm soát trong ModelState, không phải trong ngữ cảnh. Ví dụ này đang tham chiếu một người dùng hiện có, do đó "updateUser". Nếu SSN của bạn là trường bắt buộc, nó sẽ ở đó khi nó được tạo lần đầu tiên.
smd

4
Nếu tôi hiểu chính xác, "updateUser" là một phiên bản của một đối tượng đã được điền bằng FirstOrDefault () hoặc tương tự, vì vậy tôi chỉ cập nhật các thuộc tính tôi đã thay đổi và đặt các thuộc tính khác thành ISModified = false. Điều này hoạt động tốt. Nhưng, những gì tôi đang cố gắng làm là cập nhật một đối tượng mà không điền vào nó trước, mà không thực hiện bất kỳ FirstOrDefault () nào để cập nhật. Đây là khi tôi nhận được lỗi nếu tôi không chỉ định giá trị cho tất cả các trường được yêu cầu, ngay cả khi tôi đặt ISModified = false trên các thuộc tính đó. mục nhập.Property (e => e.columnA) .IsModified = false; Nếu không có dòng này, Cột sẽ thất bại.
RolandoCC

Những gì bạn đang mô tả là tạo ra một thực thể mới. Điều này chỉ áp dụng cho việc cập nhật.
smd

1
RolandoCC, đặt db.Configuration.ValidateOnSaveEnables = false; trước db.SaveChanges ();
Wilky

28
foreach(PropertyInfo propertyInfo in original.GetType().GetProperties()) {
    if (propertyInfo.GetValue(updatedUser, null) == null)
        propertyInfo.SetValue(updatedUser, propertyInfo.GetValue(original, null), null);
}
db.Entry(original).CurrentValues.SetValues(updatedUser);
db.SaveChanges();

Đây có vẻ như là một giải pháp thực sự tốt đẹp - không ồn ào hay phiền phức; bạn không phải chỉ định thủ công các thuộc tính và nó sẽ tính đến tất cả các viên đạn OP - có lý do nào khiến nó không có nhiều phiếu bầu hơn không?
nocarrier

Nó không mặc dù. Nó có một trong những "khuyết điểm" lớn nhất, nhiều hơn một lần nhấn vào cơ sở dữ liệu. Bạn vẫn phải tải bản gốc với câu trả lời này.
smd

1
@smd tại sao bạn nói nó truy cập cơ sở dữ liệu nhiều lần? Tôi không thấy điều đó xảy ra trừ khi sử dụng SetValues ​​() có tác dụng đó nhưng dường như điều đó không đúng.
Quốc hội

@parferences Tôi nghĩ rằng tôi đã ngủ khi tôi viết điều đó. Lời xin lỗi. Vấn đề thực tế là ghi đè một giá trị null dự định. Nếu người dùng cập nhật không còn tham chiếu đến một cái gì đó, thì sẽ không đúng nếu thay thế nó bằng giá trị ban đầu nếu bạn muốn xóa nó.
smd

22

Tôi đã thêm một phương thức cập nhật bổ sung vào lớp cơ sở kho lưu trữ của mình tương tự như phương thức cập nhật được tạo bởi Giàn giáo. Thay vì đặt toàn bộ đối tượng thành "sửa đổi", nó đặt một tập các thuộc tính riêng lẻ. (T là một tham số chung của lớp.)

public void Update(T obj, params Expression<Func<T, object>>[] propertiesToUpdate)
{
    Context.Set<T>().Attach(obj);

    foreach (var p in propertiesToUpdate)
    {
        Context.Entry(obj).Property(p).IsModified = true;
    }
}

Và sau đó gọi, ví dụ:

public void UpdatePasswordAndEmail(long userId, string password, string email)
{
    var user = new User {UserId = userId, Password = password, Email = email};

    Update(user, u => u.Password, u => u.Email);

    Save();
}

Tôi thích một chuyến đi đến cơ sở dữ liệu. Tuy nhiên, có lẽ tốt hơn để làm điều này với các mô hình xem, để tránh lặp lại các bộ thuộc tính. Tôi chưa làm điều đó bởi vì tôi không biết làm thế nào để tránh đưa các thông báo xác thực trên trình xác thực mô hình xem của tôi vào dự án miền của tôi.


Aha ... dự án riêng cho các mô hình xem và dự án riêng cho các kho lưu trữ hoạt động với các mô hình xem.
Ian Warburton

11
public interface IRepository
{
    void Update<T>(T obj, params Expression<Func<T, object>>[] propertiesToUpdate) where T : class;
}

public class Repository : DbContext, IRepository
{
    public void Update<T>(T obj, params Expression<Func<T, object>>[] propertiesToUpdate) where T : class
    {
        Set<T>().Attach(obj);
        propertiesToUpdate.ToList().ForEach(p => Entry(obj).Property(p).IsModified = true);
        SaveChanges();
    }
}

Tại sao không chỉ DbContext.Attach (obj); DbContext.Entry (obj) .State = EntityState.Modified;
nelsontruran

Điều này kiểm soát setmột phần của tuyên bố cập nhật.
Tanveer Badar

4

Chỉ cần thêm vào danh sách các tùy chọn. Bạn cũng có thể lấy đối tượng từ cơ sở dữ liệu và sử dụng công cụ lập bản đồ tự động như Auto Mapper để cập nhật các phần của bản ghi bạn muốn thay đổi ..


3

Tùy thuộc vào trường hợp sử dụng của bạn, tất cả các giải pháp trên được áp dụng. Đây là cách tôi thường làm nó tuy nhiên:

Đối với mã phía máy chủ (ví dụ: quy trình hàng loạt) tôi thường tải các thực thể và làm việc với các proxy động. Thông thường trong các quy trình hàng loạt, bạn cần tải dữ liệu bằng mọi cách tại thời điểm dịch vụ chạy. Tôi cố gắng tải dữ liệu hàng loạt thay vì sử dụng phương thức find để tiết kiệm thời gian. Tùy thuộc vào quá trình tôi sử dụng điều khiển đồng thời lạc quan hoặc bi quan (tôi luôn sử dụng lạc quan ngoại trừ các tình huống thực thi song song khi tôi cần khóa một số bản ghi bằng các câu lệnh sql đơn giản, mặc dù điều này rất hiếm). Tùy thuộc vào mã và kịch bản, tác động có thể giảm xuống gần như bằng không.

Đối với các kịch bản phía máy khách, bạn có một vài tùy chọn

  1. Sử dụng mô hình xem. Các mô hình nên có một thuộc tính UpdateStatus (chưa sửa đổi-chèn-cập nhật-xóa). Khách hàng có trách nhiệm đặt giá trị chính xác cho cột này tùy thuộc vào hành động của người dùng (insert-update-xóa). Máy chủ có thể truy vấn db cho các giá trị ban đầu hoặc máy khách sẽ gửi các giá trị gốc đến máy chủ cùng với các hàng đã thay đổi. Máy chủ nên đính kèm các giá trị ban đầu và sử dụng cột UpdateStatus cho mỗi hàng để quyết định cách xử lý các giá trị mới. Trong kịch bản này, tôi luôn luôn sử dụng đồng thời lạc quan. Điều này sẽ chỉ thực hiện các câu lệnh chèn - cập nhật - xóa chứ không phải bất kỳ lựa chọn nào, nhưng nó có thể cần một số mã thông minh để đi qua biểu đồ và cập nhật các thực thể (tùy thuộc vào kịch bản - ứng dụng của bạn). Một người lập bản đồ có thể giúp nhưng không xử lý logic CRUD

  2. Sử dụng một thư viện như breeze.js che giấu phần lớn sự phức tạp này (như được mô tả trong 1) và cố gắng điều chỉnh nó phù hợp với trường hợp sử dụng của bạn.

Hy vọng nó giúp

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.