2012-07-26 19 views
14

Tôi đang gặp rắc rối với việc phát hiện những thay đổi của một tài sản chuyển hướng:Entity Framework sẽ không phát hiện những thay đổi bất động sản chuyển hướng

mô hình thử nghiệm của tôi trông như thế này:

public class Person 
{ 
    public int Id { get; set; } 

    public string Name { get; set; } 

    public virtual Address Address { get; set; } 
} 

public class Address 
{ 
    public int Id { get; set; } 

    public string Name { get; set; } 
} 

Tôi đã tạo ra và lưu một đối tượng kiểu Person với cả thuộc tính Name và Address được gán. Vấn đề của tôi là nếu tôi lấy lại đối tượng Person từ cơ sở dữ liệu và tôi thay đổi thuộc tính Address (ví dụ thành Null) thì e.f. không phát hiện sự thay đổi! Mã của tôi là thế này:

using (var ctx = new EFContext()) 
{ 
    Person p = ctx.People.First(); 
    //p.Address IS NOT NULL! 
    p.Address = null; 
    var entry = ctx.Entry(p); 
} 

Tại sao entry.StateUnchanged?

Chỉnh sửa: Nếu tôi gọi SaveChanges, bản ghi được lưu chính xác (Địa chỉ trở thành null)!

Chỉnh sửa 2: Tôi đã tạo thuộc tính khóa ngoài như đề xuất và nếu tôi kiểm tra đối tượng Person trong studio trực quan, trạng thái được sửa đổi .. nếu tôi không dừng với trình gỡ rối kiểm tra giá trị của đối tượng trạng thái không thay đổi!

Chỉnh sửa 3: Đang tải đối tượng Person bằng ctx.People.Include (x => x.Address) .irst(); giải quyết vấn đề. Có cách nào để tránh gọi Bao gồm và tiếp tục sửa đổi thuộc tính Địa chỉ thay vì địa chỉ AddressId không?

+0

Điều gì xảy ra nếu bạn gọi ctx.DetectChanges()? – Maarten

+0

Không có gì. Kết quả là như nhau!! – Mones

Trả lời

24

Trước hết: Bạn PHẢI làm theo lời khuyên của @ billy để sử dụng Include. Nhận xét của bạn "p.Address IS NOT NULL!" chỉ đúng vì bạn đang xem p.Address trong trình gỡ lỗi và do đó kích hoạt tải chậm trong trình gỡ lỗi, do đó thay đổi đặt địa chỉ thành null được phát hiện. Trong chế độ phát hành hoặc khi bạn không kiểm tra các thuộc tính trong trình gỡ lỗi, mã của bạn sẽ không hoạt động và sẽ không có thay đổi nào được lưu.

Vì vậy, câu trả lời cho bạn Chỉnh sửa 3 là: Số

Thứ hai: var entry = ctx.Entry(p) chỉ trả thực thể khẳng định và bạn không thay đổi trạng thái thực thể mà thay vào đó một mối quan hệ nhà nước, hay chính xác hơn bạn đã xóa một mối quan hệ.Bạn không thể kiểm tra trạng thái mối quan hệ với DbContext API nhưng chỉ với ObjectContext API:

Person p = ctx.People.Include(x => x.Address).First(); 
p.Address = null; 
var objCtx = ((IObjectContextAdapter)ctx).ObjectContext; 
var objentr = objCtx.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted); 

objentr sẽ có một mục nhập kiểu RelationshipEntry bây giờ:

enter image description here

EF sẽ xem xét nhập mối quan hệ này cùng với các mục nhập trạng thái thực thể khi bạn gọi SaveChanges() và xóa mối quan hệ, tức là đặt cột khóa ngoài Address của Person trong cơ sở dữ liệu thành NULL.

Giới thiệu chỉnh sửa 2: Thay đổi thuộc tính khóa ngoài (là thuộc tính vô hướng trong mô hình của bạn) là thay đổi của thực thể, vì vậy trạng thái thực thể sẽ là Modified trong trường hợp này.

+0

Wow! cảm ơn câu trả lời thực sự tốt! –

+0

Câu trả lời hay, nhưng tôi có một nghi ngờ. Làm thế nào để tôi xác định rằng một mục cụ thể bị thay đổi này và không phải khác, bởi vì đây là một truy vấn đến bối cảnh cụ thể. –

+0

Làm thế nào để bạn làm điều này trong EF Core, vì không có 'ObjectContext'? – grokky

5

Bạn cần bao gồm điều hướng Địa chỉ. chống đỡ. trong truy vấn của bạn, nếu không EF sẽ không xem xét thay đổi nó khi bạn tiết kiệm:

using (var ctx = new EFContext()) 
{ 
    Person p = ctx.People.Include(x => x.Address).First(); 
    //p.Address IS NOT NULL! 
    p.Address = null; 
    var entry = ctx.Entry(p); 
} 

Bạn cũng có thể sử dụng các phím nước ngoài trong mô hình của bạn, mà tôi thích rất nhiều:

public class Person 
{ 
    public int Id { get; set; } 

    public string Name { get; set; } 

    public virtual Address Address { get; set; } 

    public int? AddressId {get; set;} 
} 

...

using (var ctx = new EFContext()) 
{ 
    Person p = ctx.People.First(); 
    p.AddressId = null; 
    var entry = ctx.Entry(p); 
} 
+0

Tôi đã thử nghiệm giải pháp với DetectChanges nhưng không có cách nào .. vẫn có trạng thái Không thay đổi! Nếu tôi sử dụng int? chìa khóa nước ngoài tôi đoán nó sẽ làm việc ngay vì tôi sẽ thay đổi giá trị của một tài sản "đơn giản" và không phải là tài sản chuyển hướng trực tiếp. Trong thực tế, nếu tôi thay đổi thuộc tính Name của đối tượng Person, mọi thứ đều OK! – Mones

2

Trong ứng dụng của tôi, trước khi tải lại được yêu cầu hoặc người dùng rời khỏi mục/xem, tôi thực hiện một số kiểm tra để đảm bảo không có thay đổi chưa được lưu.

Điều này về cơ bản đang chạy khỏi câu trả lời hiện được chấp nhận, nhưng tôi muốn cung cấp triển khai và chú ý rằng bạn phải gọi Context.ChangeTracker.DetectChanges() trước khi ObjectContext.ObjectStateManager có thể nhận các thay đổi mối quan hệ! Tôi đã dành khá nhiều thời gian để sửa lỗi này, ngớ ngẩn!

_EagleContext.ChangeTracker.DetectChanges(); 

var objectContext = ((IObjectContextAdapter)_EagleContext).ObjectContext; 
var changedEntities = objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified); 

if (_EagleContext.ChangeTracker.Entries().Any(e => e.State == EntityState.Modified) 
    || changedEntities.Count() != 0) 
{ 
    var dialogResult = MessageBox.Show("There are changes to save, are you sure you want to reload?", "Warning", MessageBoxButton.YesNo); 
    if (dialogResult == MessageBoxResult.No) 
    { 
     return; 
    } 
} 

// Continue with reloading... 
Các vấn đề liên quan