Entity Framework 4 - AddObject vs Đính kèm


132

Tôi đã làm việc với Entity Framework 4 gần đây và tôi hơi bối rối khi sử dụng ObjectSet.AttachObjectSet.AddObject .

Từ sự hiểu biết của tôi:

  • Sử dụng "Đính kèm" khi một Thực thể đã tồn tại trong hệ thống
  • Sử dụng "AddObject" khi tạo một thực thể hoàn toàn mới

Vì vậy, nếu tôi đang tạo một Người mới , tôi sẽ làm điều này.

var ctx = new MyEntities();
var newPerson = new Person { Name = "Joe Bloggs" };
ctx.Persons.AddObject(newPerson);
ctx.SaveChanges();

Nếu tôi đang sửa đổi một Người hiện có , tôi sẽ làm điều này:

var ctx = new MyEntities();
var existingPerson = ctx.Persons.SingleOrDefault(p => p.Name = "Joe Bloggs" };
existingPerson.Name = "Joe Briggs";
ctx.SaveChanges();

Hãy ghi nhớ, đây là một ví dụ rất đơn giản . Trong thực tế, tôi đang sử dụng Pure POCO (không tạo mã), mẫu Kho lưu trữ (không giao dịch với ctx.Persons) và Đơn vị công việc (không giao dịch với ctx.SaveChanges). Nhưng "dưới vỏ bọc", trên đây là những gì xảy ra trong quá trình thực hiện của tôi.

Bây giờ, câu hỏi của tôi - tôi vẫn chưa tìm thấy một kịch bản mà tôi đã phải sử dụng Đính kèm .

Tôi đang thiếu gì ở đây? Khi nào chúng ta cần sử dụng Đính kèm?

BIÊN TẬP

Để làm rõ, tôi đang tìm ví dụ về thời điểm sử dụng Đính kèm qua AddObject (hoặc ngược lại).

CHỈNH SỬA 2

Câu trả lời dưới đây là chính xác (mà tôi đã chấp nhận), nhưng nghĩ rằng tôi sẽ thêm một ví dụ khác trong đó Đính kèm sẽ hữu ích.

Trong ví dụ trên của tôi để sửa đổi một Người hiện có , hai truy vấn đang thực sự được thực thi.

Một để truy xuất Person (.SingleOrDefault) và một để thực hiện CẬP NHẬT (.SaveChanges).

Nếu (vì một số lý do), tôi đã biết rằng "Joe Bloggs" tồn tại trong hệ thống, tại sao phải truy vấn thêm để có được anh ta trước? Tôi có thể làm điều này:

var ctx = new MyEntities();
var existingPerson = new Person { Name = "Joe Bloggs" };
ctx.Persons.Attach(existingPerson);
ctx.SaveChanges();

Điều này sẽ dẫn đến chỉ một câu lệnh CẬP NHẬT được thực thi.


Đính kèm cũng được sử dụng trong MVC ngay bây giờ một ngày khi đưa các mô hình trở lại trực tiếp với EF. Hoạt động khá tốt và tiết kiệm được một tấn dòng mã.
Piotr Kula

Câu trả lời:


162

ObjectContext.AddObject ObjectSet.AddObject :
Các AddObject phương pháp là để thêm đối tượng mới được tạo ra mà không tồn tại trong cơ sở dữ liệu. Thực thể sẽ nhận được một EntityKey tạm thời được tạo tự độngvà EntityState của nó sẽ được đặt thành Đã thêm . Khi SaveChanges được gọi, sẽ rõ ràng với EF rằng thực thể này cần được chèn vào cơ sở dữ liệu.

ObjectContext.Attach ObjectSet.Attach :
Mặt khác, Đính kèm được sử dụng cho các thực thể đã tồn tại trong cơ sở dữ liệu. Thay vì đặt EntityState thành Đã thêm, Đính kèm kết quả vàoEntityState không thay đổi , có nghĩa là nó không thay đổi kể từ khi được gắn vào ngữ cảnh. Các đối tượng mà bạn đang đính kèm được giả sử tồn tại trong cơ sở dữ liệu. Nếu bạn sửa đổi các đối tượng sau khi chúng được đính kèm, khi bạn gọi SaveChanges, giá trị của EntityKey được sử dụng để cập nhật (hoặc xóa) hàng thích hợp bằng cách tìm ID phù hợp của nó trong bảng db.

Hơn nữa, bằng cách sử dụng phương thức Đính kèm, bạn có thể xác định mối quan hệ giữa các thực thể đã tồn tại trong ObjectContext nhưng cókhông được kết nối tự động. Về cơ bản, mục đích chính của Đính kèm là để kết nối các thực thể đã được đính kèm với ObjectContext và không phải làmới nên bạn không thể sử dụng Đính kèm để đính kèm các thực thể có EntityState được thêm vào. Bạn phải sử dụng Add () trong trường hợp này.

Ví dụ: giả sử thực thể Person của bạn có thuộc tính điều hướng có tên Địa chỉ là tập hợpthực thể Địa chỉ . Giả sử bạn đã đọc cả hai Đối tượng từ ngữ cảnh, nhưng chúng không liên quan đến nhau và bạn muốn làm như vậy:

var existingPerson = ctx.Persons.SingleOrDefault(p => p.Name = "Joe Bloggs" };
var myAddress = ctx.Addresses.First(a => a.PersonID != existingPerson.PersonID);
existingPerson.Addresses.Attach(myAddress);
// OR:
myAddress.PersonReference.Attach(existingPerson)
ctx.SaveChanges();

Cảm ơn câu trả lời, tôi hiểu định nghĩa của hai (hay hai đoạn đầu tiên). Nhưng tôi không hiểu một kịch bản mà tôi CẦN sử dụng Đính kèm. Đoạn cuối của bạn không thực sự có ý nghĩa với tôi (về cơ bản giống như sự kết hợp của hai đoạn đầu tiên), bạn có thể cho tôi một ví dụ về nơi tôi sẽ sử dụng "Đính kèm" trong kịch bản trên không? Đó thực sự là những gì tôi đang tìm kiếm - ví dụ, không phải định nghĩa. Thực sự đánh giá cao thời gian của bạn mặc dù. :)
RPM1984

1
Không có vấn đề gì, tôi đã thêm một đoạn mã để làm rõ đoạn cuối, vì bạn có thể thấy chúng tôi có 2 đối tượng không liên quan và Đính kèm giúp chúng tôi liên kết chúng với nhau. Ví dụ khác là sử dụng phương thức Đính kèm () để Đính kèm "Thực thể tách rời" trở lại ngữ cảnh (Có nhiều lý do khác nhau mà bạn có thể muốn một thực thể tách rời được đính kèm vào bối cảnh)
Morteza Manavi

1
Đúng, tôi đã nhận được. Tôi vừa xem một vid TechEd trên EF4 (của Julie Lerman), cho thấy một ví dụ. Bạn có thể có một thực thể mà bạn KHÔNG truy xuất từ ​​một truy vấn (nghĩa là nó bị sai lệch), nhưng bạn biết nó tồn tại, do đó bạn sử dụng Đính kèm để thực hiện CẬP NHẬT trên thực thể đó. Có ý nghĩa, mặc dù tôi vẫn chiến đấu để đưa ra một kịch bản mà bạn sẽ có một thực thể "bị ngắt kết nối". Cảm ơn bạn đã giúp đỡ.
RPM1984

1
Tuyệt quá. Tôi có thể yêu cầu bạn vui lòng chia sẻ liên kết của video cho các nhà phát triển đồng nghiệp khác có thể xảy ra để đọc bài đăng này không?
Morteza Manavi

3
Liên kết được chia sẻ ở trên bởi RPM1984 đã bị hỏng, hiện được chuyển hướng đến channel9.msdn.com/Events/TechEd/NorthAmerica/2010/DEV205 . Tận hưởng
Xếp chồng

31

Đây là một phản hồi muộn nhưng nó có thể giúp những người khác tìm thấy điều này.

Về cơ bản, một thực thể "bị ngắt kết nối" có thể xảy ra khi bạn thao tác với một thực thể nằm ngoài phạm vi "sử dụng".

Employee e = null;

using (var ctx = new MyModelContainer())
{
     e = ctx.Employees.SingleOrDefault(emp => emp .....);
}

using (var ctx2 = new MyModelContainer())
{
     e; // This entity instance is disconnected from ctx2
}

Nếu bạn nhập phạm vi "sử dụng" khác thì biến "e" sẽ bị ngắt kết nối vì nó thuộc phạm vi "sử dụng" trước đó và do phạm vi "sử dụng" trước đó bị hủy nên "e" bị ngắt kết nối.

Đó là cách tôi hiểu nó.


3
Ví dụ của Tchi là một ví dụ tuyệt vời và đơn giản - vâng, biến Nhân viên nên được khai báo bên ngoài. hãy thử e.Address.Street ngoài phạm vi và xem cửa sổ bật lên ngoại lệ tham chiếu null. Nếu bạn Đính kèm thì ứng dụng sẽ không phải quay lại DB cho Nhân viên trong phạm vi thứ hai.
Steve

9

Đây là một trích dẫn từ Khung thực thể lập trình: DbContext

Gọi Xóa trên một thực thể không được theo dõi bởi ngữ cảnh sẽ khiến cho một UnlimitedOperationException bị ném. Entity Framework đưa ra ngoại lệ này vì không rõ liệu thực thể bạn đang cố xóa là một thực thể hiện có cần được đánh dấu để xóa hoặc một thực thể mới cần bỏ qua. Vì lý do này, chúng tôi không thể sử dụng chỉ Xóa để đánh dấu một thực thể bị ngắt kết nối là Đã xóa; chúng ta cần phải đính kèm nó trước .

private static void TestDeleteDestination()
{
    Destination canyon;
    using (var context = new BreakAwayContext())
    {
        canyon = (from d in context.Destinations
        where d.Name == "Grand Canyon"
        select d).Single();
    }
    DeleteDestination(canyon);
}
private static void DeleteDestination(Destination destination)
{
    using (var context = new BreakAwayContext())
    {
        context.Destinations.Attach(destination);
        context.Destinations.Remove(destination);
        context.SaveChanges();
    }
}

Phương thức TestDeleteDestination mô phỏng ứng dụng khách đang tìm nạp Đích hiện có từ máy chủ và sau đó chuyển nó sang phương thức DeleteDestination trên máy chủ. Phương thức DeleteDestination sử dụng phương thức Đính kèm để cho bối cảnh biết rằng đó là Đích hiện có. Sau đó, phương thức Xóa được sử dụng để đăng ký Điểm đến hiện tại để xóa


-8

Điều gì về chỉ tham chiếu đến khóa chính thay vì đính kèm?

I E:

var existingPerson = ctx.Persons.SingleOrDefault(p => p.Name = "Joe Bloggs" };
var myAddress = ctx.Addresses.First(a => a.PersonID != existingPerson.PersonID);
existingPerson.AddressId = myAddress.Id // not -> existingPerson.Addresses.Attach(myAddress);
// OR:
myAddress.Person.Id = existingPerson.Id // not -> myAddress.PersonReference.Attach(existingPerson);
ctx.SaveChanges();
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.