7

Trong một mối quan hệ nhiều-nhiều một chiều giữa RegistrationItem, nơi một RegistrationISet<Item> ItemsPurchasedItem không có tài liệu tham khảo lại để đăng ký (nó không phải là một cách hữu ích để khám phá những đồ vật), khi Tôi nhìn vào SQL được tạo ra, tôi thấythành thạo NHibernate - không cần thiết cập nhật

INSERT INTO Registrations_Items (RegistrationId, ItemId) VALUES (@p0, @p1);@p0 = 1 [Type: Int32 (0)], @p1 = 1 [Type: Int32 (0)] 
UPDATE Items SET Price = @p0, Name = @p1, [...], ListIndex = @p5, EventId = @p6 WHERE ItemId = @p7 

Các tham số được truyền cho bản cập nhật là chính xác, nhưng không có gì về mục đã thay đổi, vì vậy không cần cập nhật.

Lập bản đồ bằng cách tự động ghi đè này tại chỗ cho Registration và không ghi đè cho Item. Lược đồ DB trông hoàn toàn chính xác. Tôi đã xóa tất cả các quy ước và thử nghiệm lại và hành vi vẫn tiếp tục tồn tại, vì vậy nó không phải là bất kỳ quy ước lập bản đồ nào của tôi đang thực hiện điều này.

mapping.HasManyToMany(e => e.ItemsPurchased).AsSet().Cascade.All().Not.Inverse();

Tại sao NHibernate làm UPDATE cuộc gọi này và những gì tôi có thể làm gì để ngăn chặn nó? Nó không thực sự làm tổn thương bất cứ điều gì nhưng nó cho thấy rằng tôi đã làm điều gì đó sai, vì vậy tôi muốn tìm ra những gì.

Edit: mỗi bình luận dưới đây, tôi đã tạo ra một thử nghiệm đơn vị mà tạo ra một Event (Item phải thuộc về một Event), thêm hai Items với nó, evicts là người đầu tiên từ phiên và xóa phiên, sau đó Gets là người đầu tiên trở lại bằng ID của nó.

tôi nhận thấy một cái gì đó kỳ lạ trong dòng sản phẩm Chọn danh sách bên (thứ 2 từ dưới)

INSERT INTO Events (blah blah blah...) 
select @@IDENTITY 
INSERT INTO Items (Price, Name, StartDate, EndDate, ExternalID, ListIndex, EventId) VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6);@p0 = 100.42 [Type: Decimal (0)], @p1 = 'Item 1' [Type: String (0)], @p2 = NULL [Type: DateTime (0)], @p3 = NULL [Type: DateTime (0)], @p4 = '123' [Type: String (0)], @p5 = 0 [Type: Int32 (0)], @p6 = 1 [Type: Int32 (0)] 
select @@IDENTITY 
SELECT blah blah blah FROM Events event0_ WHERE [email protected];@p0 = 1 [Type: Int32 (0)] 
SELECT itemsforsa0_.EventId as EventId1_, itemsforsa0_.ItemId as ItemId1_, itemsforsa0_.ListIndex as ListIndex1_, itemsforsa0_.ItemId as ItemId3_0_, itemsforsa0_.Price as Price3_0_, itemsforsa0_.Name as Name3_0_, itemsforsa0_.StartDate as StartDate3_0_, itemsforsa0_.EndDate as EndDate3_0_, itemsforsa0_.ExternalID as ExternalID3_0_, itemsforsa0_.ListIndex as ListIndex3_0_, itemsforsa0_.EventId as EventId3_0_ FROM Items itemsforsa0_ WHERE [email protected];@p0 = 1 [Type: Int32 (0)] 
UPDATE Items SET Price = @p0, Name = @p1, StartDate = @p2, EndDate = @p3, ExternalID = @p4, ListIndex = @p5, EventId = @p6 WHERE ItemId = @p7;@p0 = 100.42000 [Type: Decimal (0)], @p1 = 'Item 1' [Type: String (0)], @p2 = NULL [Type: DateTime (0)], @p3 = NULL [Type: DateTime (0)], @p4 = '123' [Type: String (0)], @p5 = 0 [Type: Int32 (0)], @p6 = 1 [Type: Int32 (0)], @p7 = 1 [Type: Int32 (0)] 

Bảng này được tạo ra một cách chính xác:

create table Items (
    ItemId INT IDENTITY NOT NULL, 
    Price NUMERIC(19,5) not null, 
    Name NVARCHAR(255) not null, 
    StartDate DATETIME null, 
    EndDate DATETIME null, 
    ExternalID NVARCHAR(255) not null, 
    ListIndex INT not null, 
    EventId INT not null, 
    primary key (ItemId) 
) 

Các DateTimes là cố tình nullable vì một mục có thể không cần để có ngày cụ thể (ví dụ về thứ gì đó sẽ là "đăng ký sớm chim").

+0

Để xác nhận vấn đề cập nhật ảo, 'Nhận' cùng một mục và kiểm tra xem bản cập nhật có được phát hành khi bật/cam kết hay không. Sau đó, bạn có thể loại trừ bất kỳ vấn đề xếp tầng nhiều đến nhiều. – dotjoe

+0

Điều này có liên quan đến việc có một trường thập phân không? Google sẽ đưa ra một số kết quả cho Bản cập nhật và số thập phân của Phantom, nhưng tôi chưa nhận được phần cuối của những gì họ nói. –

Trả lời

9

này được gọi là: Phantom cập nhật, nó thường liên quan với các bản đồ của các đối tượng của bạn

Đây là nguyên nhân chính:

Hãy tưởng tượng chúng ta có một đối tượng như thế này

public class Product 
{ 
    public Guid Id { get; set; } 
    public int ReorderLevel { get; set; } 
    public decimal UnitPrice { get; set; } 
} 

và bản đồ:

public class ProductMap : ClassMap<Product> 
{ 
    public ProductMap() 
    { 
     Not.LazyLoad(); 
     Id(x => x.Id).GeneratedBy.GuidComb(); 
     Map(x => x.ReorderLevel); 
     Map(x => x.UnitPrice).Not.Nullable(); 
    } 
} 

Lưu ý rằng ReorderLevel sẽ chấp nhận null

Nếu bạn lưu tổ chức này mà không chỉ định một ReorderLevel nó sẽ được lưu với một giá trị null, nhưng sau đó khi bạn tải nó về từ cơ sở dữ liệu, vì loại ReorderLevelint, một sẽ được thêm vào sẽ khiến thực thể bị đánh dấu là bẩn và do đó nó sẽ gây ra bản cập nhật

Các loại lỗi này rất khó phát hiện và theo dõi, tôi khuyên bạn nên sử dụng các loại Nullable<> khi bạn thực sự muốn trong cơ sở dữ liệu

Con đường tôi thường thực hiện điều này là tạo ra một quy ước đó sẽ tự động thiết lập Value Types tôi để null nếu chúng được khai báo với Nullable<>, nếu không trường sẽ được đánh dấu là NotNullable

Chỉ cần để bổ sung, đây là cách ước của tôi trông như:

mapper.BeforeMapProperty += (ins, memb, cust) => 
    { 
     var type = memb.LocalMember.GetPropertyOrFieldType(); 

     if (type.IsValueType) 
     { 
      if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) 
      { 
       cust.Column(x => { x.NotNullable(notnull: false); }); 
      } 
      else 
      { 
       cust.Column(x => { x.NotNullable(notnull: true); }); 
      } 
     } 
    } 
+0

Tôi đã có 'ColumnNullConvention 'để đảm bảo rằng bất kỳ trường nào được đánh dấu bằng' DataAnnotations.Required' được khai báo là không null. –

+0

Tôi đã chỉnh sửa câu hỏi ban đầu để hiển thị SQL cho một chu trình đơn giản-xóa-tuôn ra-nhận và bao gồm ánh xạ bảng. –

+1

Vấn đề là với trường Giá ', như bạn có thể thấy, nó được chèn vào giá trị 100.42 nhưng sau đó nó được cập nhật với 100.42000 Tôi nghĩ rằng đó là đánh dấu thực thể là bẩn.Bạn có logic tùy chỉnh để định dạng trường 'Giá' không ?, bạn cũng có thể cập nhật bài đăng với ánh xạ trường' Giá' và thực thể của nó không? – Jupaol

1

Như đã nói ở trên (bên dưới? ai biết. Hãy tìm nhận xét tôi còn lại trên câu trả lời khác), tôi nhận thấy rằng sự khác biệt giữa bài kiểm tra đơn vị CanGenerateDatabaseSchema và kiểm tra đơn vị CanGetItem là người đã cho tôi DECIMAL (6,2) và người kia đã cho tôi DECIMAL (19,0).

Tôi chọc xung quanh nhiều hơn và nhận ra rằng CanGenerateDatabaseSchema đang sử dụng cấu hình "thực" của tôi (từ dự án Web) và thử nghiệm khác đang sử dụng cấu hình "kiểm tra đơn vị" của tôi. Thử nghiệm đơn vị của tôi đã được chạy với Sql Server CE ... khi tôi thay đổi các bài kiểm tra đơn vị của mình để sử dụng cùng cấu hình với cơ sở dữ liệu thực của tôi (Sql Server 2005), đột nhiên bản cập nhật ảo biến mất.

Vì vậy, nếu có ai khác chạy vào các Bản cập nhật Phantom không mong muốn bằng số thập phân ... hãy kiểm tra xem bạn có đang sử dụng Sql Server CE không. Kể từ khi thử nghiệm thực sự là đi qua (bình luận nói rằng nó không chính xác, nó không thất bại, chỉ cần làm thêm công việc), tôi đoán tôi sẽ sống với nó, mặc dù tại sao Sql CE bỏ qua cấu hình của tôi là một câu hỏi hay, và lỗi NH hoặc FNH có thể xảy ra.