9

との間の一方向の多対多の関係でRegistrationItem、aRegistrationには登録への参照があり、登録への参照はISet<Item> ItemsPurchasedありItemません(オブジェクトグラフを調べるのに便利な方法ではありません)。生成されているSQLを見ると、次のようになります。

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

更新に渡されたパラメーターは正しいですが、アイテムについては何も変更されていないため、更新は必要ありません。

マッピングは、このオーバーライドを使用して自動マッピングしRegistration、のオーバーライドはありませんItem。DBスキーマは完全に正しいように見えます。すべての規則を削除して再度テストしたところ、動作が持続したため、これを行っているのは私のマッピング規則ではありません。

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

NHibernateがこのUPDATE呼び出しを行うのはなぜですか?それを停止するにはどうすればよいですか?何も害はありませんが、何かおかしなことをしたことを示唆しているので、何を考えたいと思います。

編集: 以下のコメントごとに、EventItemに属している必要がありますEvent)を作成し、それに2Itemsを追加し、セッションから最初のイベントを削除してセッションをフラッシュし、IDで最初のテストを取得する単体テストを作成しました。

下のSELECT項目の行(下から2番目)に何か奇妙なことに気づきました

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 event0_.EventId=@p0;@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 itemsforsa0_.EventId=@p0;@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)]

テーブルは正しく作成されます。

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)
)

アイテムは日付固有である必要がない場合があるため、DateTimesは意図的にnull許容になります(「早期登録」などの例)。

4

2 に答える 2

10

これはファントム更新と呼ばれ、通常はオブジェクトのマッピングに関連しています

これが主な原因です。

このようなオブジェクトがあると想像してください

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

そして地図:

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();
   }
}

ReorderLevelはヌルを受け入れることに注意してください

を指定せずにこのエンティティをReorderLevel保存すると、null値とともに保存されますが、データベースからロードし直すと、ReorderLevelタイプがintであるため、0が追加され、エンティティがダーティとしてマークされるため、更新が発生します

この種のエラーは検出と追跡が困難Nullable<>です。データベースに null が本当に必要な場合は型を使用することをお勧めします

私が通常これを達成する方法は、で宣言されている場合にmyValue Typesを自動的に設定する規則を作成することです。そうでない場合、フィールドは次のようにマークされます。nullNullable<>NotNullable

補足するために、これは私の慣習がどのように見えるかです:

    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); });
            }
        }
    }
于 2012-05-30T20:43:11.633 に答える
1

CanGenerateDatabaseSchema上で述べたように (以下? 誰が知っているか。私が他の回答に残したコメントを探してください)、単体テストと単体テストの違いはCanGetItem、一方が私に与えているものDECIMAL (6,2)と、もう一方が私に与えているものであることに気付きましたDECIMAL (19,0)

CanGenerateDatabaseSchema私はもっ​​と突っ込んで、それが私の「実際の」構成(Webプロジェクトから)を使用していて、他のテストが私の「単体テスト」構成を使用していることに気付きました。私の単体テストは Sql Server CE に対して実行されていました...実際のデータベース (Sql Server 2005) と同じ構成を使用するように単体テストを変更すると、ファントム更新が突然なくなりました。

したがって、他の誰かが 10 進数で予期しない Phantom Updates に遭遇した場合は、Sql Server CE を使用しているかどうかを確認してください。テストは実際に合格しているので (失敗しているというコメントは間違っています。失敗しているのではなく、余分な作業を行っているだけです)、私はそれを受け入れると思いますが、Sql CE が私の構成を無視している理由は良い質問です。 NH または FNH のバグの可能性があります。

于 2012-05-31T15:00:51.033 に答える