5

Xin lỗi vì tiêu đề mơ hồ, thật khó để diễn tả điều này trong một dòng duy nhất:Tại sao trở thành ràng buộc tham chiếu không phù hợp sau khi cập nhật khóa ngoại?

Tôi có 2 thực thể UserUserAddress, nơi Người dùng có 2 phím nước ngoài DefaultInvoiceAddressIdDefaultDeliveryAddressId và UserAddress có một chìa khóa nước ngoài UserId.

Đối tượng người dùng có thuộc tính điều hướng cho địa chỉ mặc định (DefaultInvoiceAddressDefaultDeliveryAddress) cũng như một cho tất cả địa chỉ của anh ấy: AllAddresses.

Việc ánh xạ, vv hoạt động, tạo và cập nhật người dùng và địa chỉ cũng hoạt động.

Điều gì không hoạt động mặc dù đang đặt Địa chỉ của người dùng hiện tại, ví dụ: DefaultInvoiceAddress. Theo thuật ngữ SQL, điều tôi muốn xảy ra là UPDATE USER SET DefaultInvoiceAddressId = 5 WHERE Id = 3.

Tôi đã thử này theo cách sau:

private void MarkAs(User user, UserAddress address, User.AddressType type) { 
     if (context.Entry(user).State == EntityState.Detached) 
      context.Users.Attach(user); 

     // guess I don't really need this: 
     if (context.Entry(address).State == EntityState.Detached) 
      context.UserAddresses.Attach(address); 

     if (type.HasFlag(User.AddressType.DefaultInvoice)) { 
      user.DefaultInvoiceAddressId = address.Id; 
      user.DefaultInvoiceAddress = null; 
      context.Entry(user).Property(u => u.DefaultInvoiceAddressId).IsModified = true; 
     } 

     if (type.HasFlag(User.AddressType.DefaultDelivery)) { 
      user.DefaultDeliveryAddressId = address.Id; 
      user.DefaultDeliveryAddress = null; 
      context.Entry(user).Property(u => u.DefaultDeliveryAddressId).IsModified = true; 
     } 
    } 

Phương pháp này được gọi là cả khi tạo UserAddresses mới cũng như khi cập nhật địa chỉ. Kịch bản tạo các công trình như mong đợi, tuy nhiên trong trường hợp cập nhật tôi nhận được lỗi sau:

The changes to the database were committed successfully, 
but an error occurred while updating the object context. 
The ObjectContext might be in an inconsistent state. 
Inner exception message: A referential integrity constraint violation occurred: 
The property values that define the referential constraints are not consistent between principal and dependent objects in the relationship. 

tôi gọi là phương pháp với một đối tượng tài khoản tôi retrive từ cơ sở dữ liệu và các DefaultDeliveryAddress nó chứa, mà tôi tải cùng với nó thông qua háo hức tải.

var user = mainDb.User.Get(UnitTestData.Users.Martin.Id, User.Include.DefaultAddresses); 
var existingAddress = user.DefaultDeliveryAddress; 
mainDb.User.Addresses.SetAs(user, existingAddress, User.AddressType.DefaultInvoice)) 
// the SetAs method verfies input parameters, calls MarkAs and then SaveChanges 

Tóm lại, Tôi chỉ muốn làm cho DefaultDeliveryAddress của người dùng cũng DefaultInvoiceAddress của mình, mà sẽ được dễ dàng thực hiện với các lệnh SQL Cập nhật ở trên, nhưng tôi đang thiếu một cái gì đó với mã EF của tôi. Tôi đã kiểm tra rằng:

  • Chỉ Id được thiết lập, tài sản chuyển hướng (DefaultInvoiceAddress) được tái thiết lập để null
  • UserAddress.UserId = User.Id (rõ ràng vì nó đã được giao cho người sử dụng)
  • Đối tượng sử dụng sẽ trở thành Modified (kiểm tra với debugger), kể từ khi một trong những thuộc tính của nó đã được đánh dấu là sửa đổi
  • tôi cũng đã cố gắng thanh toán bù trừ bất động sản chuyển hướng cả hai địa chỉ mặc định, nhưng điều đó không giúp được gì

Tôi nghi ngờ vấn đề này là do thực thể người dùng có 2 tham chiếu đến UserAddress và cả hai khóa ngoại được đặt để chỉ cùng một địa chỉ - làm cách nào để tôi có thể làm việc với EF?

Cập nhật:

Sau đây là các ánh xạ của thực thể tài:

// from UserMap.cs: 
... 
     Property(t => t.DefaultInvoiceAddressId).HasColumnName("DefaultInvoiceAddressId"); 
     Property(t => t.DefaultDeliveryAddressId).HasColumnName("DefaultDeliveryAddressId"); 

     // Relationships 
     HasOptional(t => t.DefaultInvoiceAddress) 
      .WithMany() 
      .HasForeignKey(t => t.DefaultInvoiceAddressId); 

     HasOptional(t => t.DefaultDeliveryAddress) 
      .WithMany() 
      .HasForeignKey(t => t.DefaultDeliveryAddressId); 

     HasMany(t => t.AllAddresses) 
      .WithRequired() 
      .HasForeignKey(t => t.UserId) 
      .WillCascadeOnDelete(); 

UserAddress không có tính chất định hướng lại cho người dùng; nó chỉ contanis HasMaxLength và HasColumnName cài đặt (tôi loại trừ chúng để giữ cho câu hỏi có phần dễ đọc).

Cập nhật 2

Dưới đây là các lệnh thực thi từ IntelliTrace:

The command text "update [TestSchema].[User] 
set [DefaultInvoiceAddressId] = @0 
where ([Id] = @1) 
" was executed on connection "Server=(localdb)\..." 

Hình phạt đối với tôi; dường như chỉ quản lý nhà nước EF bị nhầm lẫn bởi ánh xạ chính.

+0

Đăng định nghĩa và cấu hình Người dùng & Địa chỉ. Đồng thời đăng các đoạn mã SQL thực tế được tạo ra. –

+0

Hmm, lệnh SELECT đơn giản có thể nhận được từ một truy vấn nhưng đối với UPDATE/INSERT bạn sẽ cần đến studio SQL Mgmt hoặc một hồ sơ (profiler nhỏ có vẻ đáng xem). –

+0

Tôi tìm thấy một cách để có được các tuyên bố với Intellitrace, tôi đã cập nhật câu hỏi của tôi với lệnh. – enzi

Trả lời

7

Đã giải quyết được vấn đề: dường như nó tạo ra sự khác biệt khi đặt thuộc tính điều hướng thành rỗng, vì EF có thể hiểu rằng thay đổi/cập nhật dự kiến ​​(ít nhất đó là điều tôi nghi ngờ).

Phiên bản sau của phương pháp MarkAs hoạt động:

private void MarkAs(User user, UserAddress address, User.AddressType type) { 
     if (context.Entry(user).State == EntityState.Detached) { 
      // clear navigation properties before attaching the entity 
      user.DefaultInvoiceAddress = null; 
      user.DefaultDeliveryAddress = null; 
      context.Users.Attach(user); 
     } 
     // address doesn't have to be attached 

     if (type.HasFlag(User.AddressType.DefaultInvoice)) { 
      // previously I tried to clear the navigation property here 
      user.DefaultInvoiceAddressId = address.Id; 
      context.Entry(user).Property(u => u.DefaultInvoiceAddressId).IsModified = true; 
     } 

     if (type.HasFlag(User.AddressType.DefaultDelivery)) { 
      user.DefaultDeliveryAddressId = address.Id; 
      context.Entry(user).Property(u => u.DefaultDeliveryAddressId).IsModified = true; 
     } 
    } 

Tóm lại những phát hiện của tôi cho độc giả trong tương lai:

  • Nếu bạn có ý định để cập nhật các đối tượng thông qua thuộc tính chính nước ngoài, chuyển hướng rõ ràng tính chất. EF không cần chúng để tìm ra câu lệnh cập nhật.
  • Xóa các thuộc tính điều hướng trước bạn đính kèm thực thể vào ngữ cảnh, nếu không EF có thể hiểu đó là thay đổi (trong trường hợp của tôi khóa ngoại là không thể, nếu trường hợp đó không đủ thông minh để bỏ qua thay đổi thuộc tính điều hướng).

Tôi sẽ không chấp nhận câu trả lời của riêng tôi ngay lập tức để cung cấp cho người đọc (đủ điều kiện) khác một cơ hội để trả lời; nếu không có câu trả lời nào được đăng trong 2 ngày tới, tôi sẽ chấp nhận câu trả lời này.